blob: a221e6b61462614b148087a36f25351d1427f828 [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.com7fce0de2012-11-29 14:31:50 +000032#define FORCE_RELEASE 1 // 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.com7fce0de2012-11-29 14:31:50 +0000846static bool isActiveOp(bool angleIsOp, int otherNonZero, ShapeOp op) {
caryclark@google.com31143cf2012-11-09 22:14:19 +0000847 return gOpLookup[op][otherNonZero][angleIsOp];
caryclark@google.com235f56a2012-09-14 14:19:30 +0000848}
849
caryclark@google.comf839c032012-10-26 21:03:50 +0000850// wrap path to keep track of whether the contour is initialized and non-empty
851class PathWrapper {
852public:
853 PathWrapper(SkPath& path)
854 : fPathPtr(&path)
855 {
856 init();
857 }
858
859 void close() {
860 if (!fHasMove) {
861 return;
862 }
863 bool callClose = isClosed();
864 lineTo();
865 if (fEmpty) {
866 return;
867 }
868 if (callClose) {
869 #if DEBUG_PATH_CONSTRUCTION
870 SkDebugf("path.close();\n");
871 #endif
872 fPathPtr->close();
873 }
874 init();
875 }
876
877 void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
878 lineTo();
879 moveTo();
880#if DEBUG_PATH_CONSTRUCTION
881 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
882 pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
883#endif
884 fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
885 fDefer[0] = fDefer[1] = pt3;
886 fEmpty = false;
887 }
888
889 void deferredLine(const SkPoint& pt) {
890 if (pt == fDefer[1]) {
891 return;
892 }
893 if (changedSlopes(pt)) {
894 lineTo();
895 fDefer[0] = fDefer[1];
896 }
897 fDefer[1] = pt;
898 }
899
900 void deferredMove(const SkPoint& pt) {
901 fMoved = true;
902 fHasMove = true;
903 fEmpty = true;
904 fDefer[0] = fDefer[1] = pt;
905 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000906
caryclark@google.comf839c032012-10-26 21:03:50 +0000907 void deferredMoveLine(const SkPoint& pt) {
908 if (!fHasMove) {
909 deferredMove(pt);
910 }
911 deferredLine(pt);
912 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000913
caryclark@google.comf839c032012-10-26 21:03:50 +0000914 bool hasMove() const {
915 return fHasMove;
916 }
917
918 void init() {
919 fEmpty = true;
920 fHasMove = false;
921 fMoved = false;
922 }
923
924 bool isClosed() const {
925 return !fEmpty && fFirstPt == fDefer[1];
926 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000927
caryclark@google.comf839c032012-10-26 21:03:50 +0000928 void lineTo() {
929 if (fDefer[0] == fDefer[1]) {
930 return;
931 }
932 moveTo();
933 fEmpty = false;
934#if DEBUG_PATH_CONSTRUCTION
935 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
936#endif
937 fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
938 fDefer[0] = fDefer[1];
939 }
940
941 const SkPath* nativePath() const {
942 return fPathPtr;
943 }
944
945 void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
946 lineTo();
947 moveTo();
948#if DEBUG_PATH_CONSTRUCTION
949 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
950 pt1.fX, pt1.fY, pt2.fX, pt2.fY);
951#endif
952 fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
953 fDefer[0] = fDefer[1] = pt2;
954 fEmpty = false;
955 }
956
957protected:
958 bool changedSlopes(const SkPoint& pt) const {
959 if (fDefer[0] == fDefer[1]) {
960 return false;
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000961 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000962 SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
963 SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
964 SkScalar lineDx = pt.fX - fDefer[1].fX;
965 SkScalar lineDy = pt.fY - fDefer[1].fY;
966 return deferDx * lineDy != deferDy * lineDx;
967 }
968
969 void moveTo() {
970 if (!fMoved) {
971 return;
972 }
973 fFirstPt = fDefer[0];
974#if DEBUG_PATH_CONSTRUCTION
975 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
976#endif
977 fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
978 fMoved = false;
979 }
980
981private:
982 SkPath* fPathPtr;
983 SkPoint fDefer[2];
984 SkPoint fFirstPt;
985 bool fEmpty;
986 bool fHasMove;
987 bool fMoved;
988};
989
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000990class Segment {
991public:
992 Segment() {
993#if DEBUG_DUMP
994 fID = ++gSegmentID;
995#endif
996 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +0000997
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000998 bool operator<(const Segment& rh) const {
999 return fBounds.fTop < rh.fBounds.fTop;
1000 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001001
caryclark@google.com9764cc62012-07-12 19:29:45 +00001002 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
1003 if (activeAngleInner(index, done, angles)) {
1004 return true;
1005 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001006 double referenceT = fTs[index].fT;
1007 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001008 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001009 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001010 return true;
1011 }
1012 }
1013 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001014 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001015 return true;
1016 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001017 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001018 return false;
1019 }
1020
caryclark@google.com9764cc62012-07-12 19:29:45 +00001021 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001022 Span* span = &fTs[index];
1023 Segment* other = span->fOther;
1024 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001025 return other->activeAngleInner(oIndex, done, angles);
1026 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001027
caryclark@google.com9764cc62012-07-12 19:29:45 +00001028 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001029 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001030 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001031 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +00001032 if (upSpan.fWindValue) {
1033 addAngle(angles, index, next);
caryclark@google.comf839c032012-10-26 21:03:50 +00001034 if (upSpan.fDone || upSpan.fUnsortableEnd) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001035 done++;
1036 } else if (upSpan.fWindSum != SK_MinS32) {
1037 return true;
1038 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00001039 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001040 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001041 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001042 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +00001043 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001044 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +00001045 if (downSpan.fWindValue) {
1046 addAngle(angles, index, prev);
1047 if (downSpan.fDone) {
1048 done++;
1049 } else if (downSpan.fWindSum != SK_MinS32) {
1050 return true;
1051 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00001052 }
1053 }
1054 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001055 }
1056
caryclark@google.comf839c032012-10-26 21:03:50 +00001057 void activeLeftTop(SkPoint& result) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001058 SkASSERT(!done());
1059 int count = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001060 result.fY = SK_ScalarMax;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001061 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00001062 bool lastUnsortable = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001063 for (int index = 0; index < count; ++index) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001064 const Span& span = fTs[index];
1065 if (span.fUnsortableStart | lastUnsortable) {
1066 goto next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001067 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001068 if (!span.fDone | !lastDone) {
1069 const SkPoint& xy = xyAtT(index);
1070 if (result.fY < xy.fY) {
1071 goto next;
1072 }
1073 if (result.fY == xy.fY && result.fX < xy.fX) {
1074 goto next;
1075 }
1076 result = xy;
1077 }
1078 next:
1079 lastDone = span.fDone;
1080 lastUnsortable = span.fUnsortableEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001081 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001082 SkASSERT(result.fY < SK_ScalarMax);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001083 }
1084
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001085 bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, ShapeOp op) {
1086 int sumMiWinding = updateWinding(endIndex, index);
1087 int sumSuWinding = updateOppWinding(endIndex, index);
1088 if (fOperand) {
1089 SkTSwap<int>(sumMiWinding, sumSuWinding);
1090 }
1091 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
1092 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, sumMiWinding, sumSuWinding,
1093 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
1094 }
1095
1096 bool activeOp(int xorMiMask, int xorSuMask,
1097 int index, int endIndex, ShapeOp op,
1098 int& sumMiWinding, int& sumSuWinding,
1099 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
1100 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
1101 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
1102 int mask, oppMask;
1103 if (operand()) {
1104 mask = xorSuMask;
1105 oppMask = xorMiMask;
1106 } else {
1107 mask = xorMiMask;
1108 oppMask = xorSuMask;
1109 }
1110 if ((sumWinding & mask) && (maxWinding & mask)) {
1111 return false;
1112 }
1113 int oppCoin = oppSign(index, endIndex) & oppMask;
1114 if (oppCoin) {
1115 return op == kIntersect_Op || op == kUnion_Op
1116 || (oppSumWinding & oppMask && oppMaxWinding & oppMask);
1117 }
1118 bool oppNonZero = oppMaxWinding & oppMask;
1119 return isActiveOp(operand(), oppNonZero, op);
1120 }
1121
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001122 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001123 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001124 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001125#if DEBUG_ANGLE
caryclark@google.com31143cf2012-11-09 22:14:19 +00001126 if (angles.count() > 1 && !fTs[start].fTiny) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001127 SkPoint angle0Pt, newPt;
1128 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
1129 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
1130 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
1131 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
1132 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
1133 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001134#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001135 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001136 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001137
caryclark@google.com2ddff932012-08-07 21:25:27 +00001138 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001139 double oEnd) {
1140 int tIndex = -1;
1141 int tCount = fTs.count();
1142 int oIndex = -1;
1143 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001144 do {
1145 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001146 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001147 int tIndexStart = tIndex;
1148 do {
1149 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001150 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001151 int oIndexStart = oIndex;
1152 double nextT;
1153 do {
1154 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001155 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001156 double oNextT;
1157 do {
1158 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001159 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001160 // at this point, spans before and after are at:
1161 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1162 // if tIndexStart == 0, no prior span
1163 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001164
caryclark@google.comcc905052012-07-25 20:59:42 +00001165 // advance the span with zero winding
1166 // if the following span exists (not past the end, non-zero winding)
1167 // connect the two edges
1168 if (!fTs[tIndexStart].fWindValue) {
1169 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1170 #if DEBUG_CONCIDENT
1171 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1172 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001173 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1174 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001175 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001176 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001177 }
1178 if (nextT < 1 && fTs[tIndex].fWindValue) {
1179 #if DEBUG_CONCIDENT
1180 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1181 __FUNCTION__, fID, other.fID, tIndex,
1182 fTs[tIndex].fT, xyAtT(tIndex).fX,
1183 xyAtT(tIndex).fY);
1184 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001185 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001186 }
1187 } else {
1188 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1189 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1190 #if DEBUG_CONCIDENT
1191 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1192 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001193 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1194 other.xyAtT(oIndexStart).fY);
1195 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001196 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001197 }
1198 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1199 #if DEBUG_CONCIDENT
1200 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1201 __FUNCTION__, fID, other.fID, oIndex,
1202 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1203 other.xyAtT(oIndex).fY);
1204 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1205 #endif
1206 }
1207 }
1208 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001209
caryclark@google.comcc905052012-07-25 20:59:42 +00001210 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1211 double oEnd) {
1212 // walk this to outsideTs[0]
1213 // walk other to outsideTs[1]
1214 // if either is > 0, add a pointer to the other, copying adjacent winding
1215 int tIndex = -1;
1216 int oIndex = -1;
1217 double tStart = outsideTs[0];
1218 double oStart = outsideTs[1];
1219 do {
1220 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001221 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001222 do {
1223 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001224 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001225 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001226 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001227 }
1228 tStart = fTs[tIndex].fT;
1229 oStart = other.fTs[oIndex].fT;
1230 do {
1231 double nextT;
1232 do {
1233 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001234 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001235 tStart = nextT;
1236 do {
1237 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001238 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001239 oStart = nextT;
1240 if (tStart == 1 && oStart == 1) {
1241 break;
1242 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001243 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001244 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001245 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001246
caryclark@google.com235f56a2012-09-14 14:19:30 +00001247 void addCubic(const SkPoint pts[4], bool operand) {
1248 init(pts, SkPath::kCubic_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001249 fBounds.setCubicBounds(pts);
1250 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001251
caryclark@google.comf839c032012-10-26 21:03:50 +00001252 /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001253 SkPoint edge[4];
caryclark@google.comf839c032012-10-26 21:03:50 +00001254 const SkPoint* ePtr;
1255 int lastT = fTs.count() - 1;
1256 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
1257 ePtr = fPts;
1258 } else {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001259 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.comf839c032012-10-26 21:03:50 +00001260 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1261 ePtr = edge;
1262 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001263 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001264 bool reverse = ePtr == fPts && start != 0;
1265 if (reverse) {
1266 path.deferredMoveLine(ePtr[fVerb]);
1267 switch (fVerb) {
1268 case SkPath::kLine_Verb:
1269 path.deferredLine(ePtr[0]);
1270 break;
1271 case SkPath::kQuad_Verb:
1272 path.quadTo(ePtr[1], ePtr[0]);
1273 break;
1274 case SkPath::kCubic_Verb:
1275 path.cubicTo(ePtr[2], ePtr[1], ePtr[0]);
1276 break;
1277 default:
1278 SkASSERT(0);
1279 }
1280 // return ePtr[0];
1281 } else {
1282 path.deferredMoveLine(ePtr[0]);
1283 switch (fVerb) {
1284 case SkPath::kLine_Verb:
1285 path.deferredLine(ePtr[1]);
1286 break;
1287 case SkPath::kQuad_Verb:
1288 path.quadTo(ePtr[1], ePtr[2]);
1289 break;
1290 case SkPath::kCubic_Verb:
1291 path.cubicTo(ePtr[1], ePtr[2], ePtr[3]);
1292 break;
1293 default:
1294 SkASSERT(0);
1295 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001296 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001297 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001298 // return ePtr[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001299 }
1300
caryclark@google.com235f56a2012-09-14 14:19:30 +00001301 void addLine(const SkPoint pts[2], bool operand) {
1302 init(pts, SkPath::kLine_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001303 fBounds.set(pts, 2);
1304 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001305
caryclark@google.comf839c032012-10-26 21:03:50 +00001306#if 0
1307 const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001308 const SkPoint& pt = xyAtT(tIndex);
1309 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001310 path.deferredMove(pt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001311 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001312 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001313 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001314#endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001315
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001316 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001317 void addOtherT(int index, double otherT, int otherIndex) {
1318 Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00001319 #if PIN_ADD_T
caryclark@google.com185c7c42012-10-19 18:26:24 +00001320 if (precisely_less_than_zero(otherT)) {
1321 otherT = 0;
1322 } else if (precisely_greater_than_one(otherT)) {
1323 otherT = 1;
1324 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001325 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001326 span.fOtherT = otherT;
1327 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001328 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001329
caryclark@google.com235f56a2012-09-14 14:19:30 +00001330 void addQuad(const SkPoint pts[3], bool operand) {
1331 init(pts, SkPath::kQuad_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001332 fBounds.setQuadBounds(pts);
1333 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001334
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001335 // Defer all coincident edge processing until
1336 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001337
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001338// no need to be tricky; insert in normal T order
1339// resolve overlapping ts when considering coincidence later
1340
1341 // add non-coincident intersection. Resulting edges are sorted in T.
1342 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001343 // FIXME: in the pathological case where there is a ton of intercepts,
1344 // binary search?
1345 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001346 size_t tCount = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001347 #if PIN_ADD_T
caryclark@google.comc899ad92012-08-23 15:24:42 +00001348 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001349 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001350 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001351 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001352 newT = 1;
1353 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001354 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001355 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001356 // OPTIMIZATION: if there are three or more identical Ts, then
1357 // the fourth and following could be further insertion-sorted so
1358 // that all the edges are clockwise or counterclockwise.
1359 // This could later limit segment tests to the two adjacent
1360 // neighbors, although it doesn't help with determining which
1361 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001362 if (newT < fTs[index].fT) {
1363 insertedAt = index;
1364 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001365 }
1366 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001367 Span* span;
1368 if (insertedAt >= 0) {
1369 span = fTs.insert(insertedAt);
1370 } else {
1371 insertedAt = tCount;
1372 span = fTs.append();
1373 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001374 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001375 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001376 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001377 span->fWindSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001378 span->fOppSum = SK_MinS32;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001379 span->fWindValue = 1;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001380 span->fOppValue = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00001381 span->fTiny = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001382 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001383 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001384 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001385 span->fUnsortableStart = false;
1386 span->fUnsortableEnd = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00001387 if (span - fTs.begin() > 0 && !span[-1].fDone
1388 && !precisely_negative(newT - span[-1].fT)
1389 // && approximately_negative(newT - span[-1].fT)
1390 && xyAtT(&span[-1]) == xyAtT(span)) {
1391 span[-1].fTiny = true;
1392 span[-1].fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001393 if (approximately_negative(newT - span[-1].fT)) {
1394 if (approximately_greater_than_one(newT)) {
1395 span[-1].fUnsortableStart = true;
1396 span[-2].fUnsortableEnd = true;
1397 }
1398 if (approximately_less_than_zero(span[-1].fT)) {
1399 span->fUnsortableStart = true;
1400 span[-1].fUnsortableEnd = true;
1401 }
1402 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001403 ++fDoneSpans;
1404 }
1405 if (fTs.end() - span > 1 && !span->fDone
1406 && !precisely_negative(span[1].fT - newT)
1407 // && approximately_negative(span[1].fT - newT)
1408 && xyAtT(&span[1]) == xyAtT(span)) {
1409 span->fTiny = true;
1410 span->fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001411 if (approximately_negative(span[1].fT - newT)) {
1412 if (approximately_greater_than_one(span[1].fT)) {
1413 span->fUnsortableStart = true;
1414 span[-1].fUnsortableEnd = true;
1415 }
1416 if (approximately_less_than_zero(newT)) {
1417 span[1].fUnsortableStart = true;
1418 span->fUnsortableEnd = true;
1419 }
1420 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001421 ++fDoneSpans;
1422 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001423 return insertedAt;
1424 }
1425
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001426 // set spans from start to end to decrement by one
1427 // note this walks other backwards
1428 // FIMXE: there's probably an edge case that can be constructed where
1429 // two span in one segment are separated by float epsilon on one span but
1430 // not the other, if one segment is very small. For this
1431 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001432 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001433 // sorted? It feels better in such a case to match the span's other span
1434 // pointer since both coincident segments must contain the same spans.
1435 void addTCancel(double startT, double endT, Segment& other,
1436 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001437 SkASSERT(!approximately_negative(endT - startT));
1438 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001439 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001440 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001441 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001442 ++index;
1443 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001444 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001445 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001446 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001447 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001448 Span* test = &fTs[index];
1449 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001450 SkTDArray<double> outsideTs;
1451 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001452 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001453 bool decrement = test->fWindValue && oTest->fWindValue && !binary;
caryclark@google.comcc905052012-07-25 20:59:42 +00001454 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001455 double testT = test->fT;
1456 double oTestT = oTest->fT;
1457 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001458 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001459 if (decrement) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001460 decrementSpan(span);
caryclark@google.com200c2112012-08-03 15:05:04 +00001461 } else if (track && span->fT < 1 && oTestT < 1) {
1462 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001463 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001464 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001465 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001466 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001467 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1468 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1469 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001470 while (approximately_negative(otherTMatchStart - oSpan->fT)
1471 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001472 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001473 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001474 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001475 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001476 other.decrementSpan(oSpan);
1477 } else if (track && oSpan->fT < 1 && testT < 1) {
1478 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001479 }
1480 if (!oIndex) {
1481 break;
1482 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001483 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001484 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001485 test = span;
1486 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001487 } while (!approximately_negative(endT - test->fT));
1488 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001489 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001490 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001491 double tStart = outsideTs[0];
1492 double oStart = outsideTs[1];
1493 addCancelOutsides(tStart, oStart, other, oEndT);
1494 int count = outsideTs.count();
1495 if (count > 2) {
1496 double tStart = outsideTs[count - 2];
1497 double oStart = outsideTs[count - 1];
1498 addCancelOutsides(tStart, oStart, other, oEndT);
1499 }
caryclark@google.com18063442012-07-25 12:05:18 +00001500 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001501 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001502 double tStart = oOutsideTs[0];
1503 double oStart = oOutsideTs[1];
1504 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001505 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001506 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00001507
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001508 int bumpCoincidentThis(const Span* oTest, const bool transfer, const bool decrementThis,
caryclark@google.com729e1c42012-11-21 21:36:34 +00001509 const bool thisXor, const int oXorMask, const bool opp, int index,
1510 SkTDArray<double>& outsideTs, SkTDArray<double>& xOutsideTs)
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001511 {
1512 Span* const test = &fTs[index];
1513 Span* end = test;
1514 const int startIndex = index;
1515 const double oStartT = oTest->fT;
1516 do {
1517 if (transfer) {
caryclark@google.com6ec15262012-11-16 20:16:50 +00001518 if (opp) {
1519 if (decrementThis) {
caryclark@google.com729e1c42012-11-21 21:36:34 +00001520 zeroSpan(end, oStartT);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001521 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001522 } else {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001523 end->fWindValue += oTest->fOppValue;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001524 end->fOppValue = (end->fOppValue + oTest->fWindValue) & oXorMask;
1525 if (thisXor) {
1526 SkASSERT(end->fWindValue);
1527 if (!(end->fWindValue & 1)) {
1528 zeroSpan(end, oStartT);
1529 TrackOutside(outsideTs, end->fT, oStartT);
1530 }
1531 }
caryclark@google.com6ec15262012-11-16 20:16:50 +00001532 }
1533 } else if (!decrementThis & !thisXor) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001534 #ifdef SK_DEBUG
1535 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
1536 #endif
1537 ++(end->fWindValue);
1538 } else if (decrementSpan(end)) {
1539 TrackOutside(outsideTs, end->fT, oStartT);
1540 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001541 } else if (oTest->fWindValue) {
1542 SkASSERT(decrementThis);
1543 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1544 TrackOutside(xOutsideTs, end->fT, oStartT);
1545 }
1546 }
1547 end = &fTs[++index];
1548 } while (approximately_negative(end->fT - test->fT));
1549 return index;
1550 }
1551
1552 // because of the order in which coincidences are resolved, this and other
1553 // may not have the same intermediate points. Compute the corresponding
1554 // intermediate T values (using this as the master, other as the follower)
1555 // and walk other conditionally -- hoping that it catches up in the end
1556 int bumpCoincidentOther(const Span* test, const bool transfer, const bool decrementThis,
caryclark@google.com729e1c42012-11-21 21:36:34 +00001557 const bool otherXor, const int xorMask, const bool opp, const double tRatio,
1558 const double oEndT, int& oIndex, SkTDArray<double>& oOutsideTs)
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001559 {
1560 Span* const oTest = &fTs[oIndex];
1561 Span* oEnd = oTest;
1562 const double startT = test->fT;
1563 const int oStartIndex = oIndex;
1564 const double oStartT = oTest->fT;
1565 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
1566 while (!approximately_negative(oEndT - oEnd->fT)
1567 && approximately_negative(oEnd->fT - otherTMatch)) {
1568 if (transfer) {
caryclark@google.com6ec15262012-11-16 20:16:50 +00001569 if (opp) {
1570 if (decrementThis) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001571 oEnd->fWindValue += test->fOppValue;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001572 oEnd->fOppValue = (oEnd->fOppValue + test->fWindValue) & xorMask;
1573 if (otherXor) {
1574 SkASSERT(oEnd->fWindValue);
1575 if (!(oEnd->fWindValue & 1)) {
1576 zeroSpan(oEnd, startT);
1577 TrackOutside(oOutsideTs, oEnd->fT, startT);
1578 }
1579 }
caryclark@google.com6ec15262012-11-16 20:16:50 +00001580 } else {
caryclark@google.com729e1c42012-11-21 21:36:34 +00001581 zeroSpan(oEnd, startT);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001582 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001583 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00001584 } else if (decrementThis & !otherXor) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001585 #ifdef SK_DEBUG
1586 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1587 #endif
1588 ++(oEnd->fWindValue);
1589 } else if (decrementSpan(oEnd)) {
1590 TrackOutside(oOutsideTs, oEnd->fT, startT);
1591 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001592 } else if (test->fWindValue) {
1593 SkASSERT(decrementThis);
1594 if (oStartIndex > 0 && fTs[oStartIndex - 1].fWindValue) {
1595 SkASSERT(0); // track for later?
1596 }
1597 }
1598 oEnd = &fTs[++oIndex];
1599 }
1600 return oIndex;
1601 }
1602
1603 // FIXME: need to test this case:
1604 // contourA has two segments that are coincident
1605 // contourB has two segments that are coincident in the same place
1606 // each ends up with +2/0 pairs for winding count
1607 // since logic below doesn't transfer count (only increments/decrements) can this be
1608 // resolved to +4/0 ?
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001609
1610 // set spans from start to end to increment the greater by one and decrement
1611 // the lesser
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001612 void addTCoincident(bool thisXor, bool otherXor, double startT, double endT,
caryclark@google.com235f56a2012-09-14 14:19:30 +00001613 Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001614 SkASSERT(!approximately_negative(endT - startT));
1615 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001616 bool opp = fOperand ^ other.fOperand;
1617 if (!opp) {
1618 otherXor = thisXor;
1619 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001620 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001621 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001622 ++index;
1623 }
1624 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001625 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001626 ++oIndex;
1627 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001628 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001629 Span* test = &fTs[index];
1630 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001631 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001632 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001633 SkTDArray<double> oOutsideTs;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001634 int xorMask = thisXor ? 1 : -1;
1635 int oXorMask = otherXor ? 1 : -1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001636 do {
caryclark@google.com6ec15262012-11-16 20:16:50 +00001637 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001638 bool decrementThis = test->fWindValue < oTest->fWindValue ||
1639 (test->fWindValue == oTest->fWindValue && thisXor);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001640 if (decrementThis) {
caryclark@google.com729e1c42012-11-21 21:36:34 +00001641 oIndex = other.bumpCoincidentOther(test, transfer, decrementThis, otherXor, xorMask,
1642 opp, tRatio, oEndT, oIndex, oOutsideTs);
1643 index = bumpCoincidentThis(oTest, transfer, decrementThis, thisXor, oXorMask, opp,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001644 index, outsideTs, xOutsideTs);
1645 } else {
caryclark@google.com729e1c42012-11-21 21:36:34 +00001646 index = bumpCoincidentThis(oTest, transfer, decrementThis, thisXor, oXorMask, opp,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001647 index, outsideTs, xOutsideTs);
caryclark@google.com729e1c42012-11-21 21:36:34 +00001648 oIndex = other.bumpCoincidentOther(test, transfer, decrementThis, otherXor, xorMask,
1649 opp, tRatio, oEndT, oIndex, oOutsideTs);
caryclark@google.com59823f72012-08-09 18:17:47 +00001650 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001651 test = &fTs[index];
1652 oTest = &other.fTs[oIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001653 } while (!approximately_negative(endT - test->fT));
1654 SkASSERT(approximately_negative(oTest->fT - oEndT));
1655 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001656 if (!done()) {
1657 if (outsideTs.count()) {
1658 addCoinOutsides(outsideTs, other, oEndT);
1659 }
1660 if (xOutsideTs.count()) {
1661 addCoinOutsides(xOutsideTs, other, oEndT);
1662 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001663 }
1664 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001665 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001666 }
1667 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001668
caryclark@google.comcc905052012-07-25 20:59:42 +00001669 // FIXME: this doesn't prevent the same span from being added twice
1670 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001671 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001672 int tCount = fTs.count();
1673 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1674 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001675 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001676 break;
1677 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001678 if (approximately_negative(span.fT - t) && span.fOther == &other
1679 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001680#if DEBUG_ADD_T_PAIR
1681 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1682 __FUNCTION__, fID, t, other.fID, otherT);
1683#endif
1684 return;
1685 }
1686 }
caryclark@google.com47580692012-07-23 12:14:49 +00001687#if DEBUG_ADD_T_PAIR
1688 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1689 __FUNCTION__, fID, t, other.fID, otherT);
1690#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001691 int insertedAt = addT(t, &other);
1692 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001693 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001694 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001695 matchWindingValue(insertedAt, t, borrowWind);
1696 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001697 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001698
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001699 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001700 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001701 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1702 addAngle(angles, end, start);
1703 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001704 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001705 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001706 int tIndex = nextExactSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001707 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001708 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001709 }
1710 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001711
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001712 const Bounds& bounds() const {
1713 return fBounds;
1714 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001715
caryclark@google.com31143cf2012-11-09 22:14:19 +00001716 void buildAngles(int index, SkTDArray<Angle>& angles, bool includeOpp) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001717 double referenceT = fTs[index].fT;
1718 int lesser = index;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001719 while (--lesser >= 0 && (includeOpp || fTs[lesser].fOther->fOperand == fOperand)
1720 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00001721 buildAnglesInner(lesser, angles);
1722 }
1723 do {
1724 buildAnglesInner(index, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001725 } while (++index < fTs.count() && (includeOpp || fTs[index].fOther->fOperand == fOperand)
1726 && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001727 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001728
1729 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1730 Span* span = &fTs[index];
1731 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001732 // if there is only one live crossing, and no coincidence, continue
1733 // in the same direction
1734 // if there is coincidence, the only choice may be to reverse direction
1735 // find edge on either side of intersection
1736 int oIndex = span->fOtherIndex;
1737 // if done == -1, prior span has already been processed
1738 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001739 int next = other->nextExactSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001740 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001741 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001742 next = other->nextExactSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001743 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001744 // add candidate into and away from junction
1745 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001746 }
1747
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001748 int computeSum(int startIndex, int endIndex, bool binary) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001749 SkTDArray<Angle> angles;
1750 addTwoAngles(startIndex, endIndex, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001751 buildAngles(endIndex, angles, false);
caryclark@google.comd1688742012-09-18 20:08:37 +00001752 // OPTIMIZATION: check all angles to see if any have computed wind sum
1753 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001754 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001755 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001756#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001757 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001758#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001759 if (!sortable) {
1760 return SK_MinS32;
1761 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001762 int angleCount = angles.count();
1763 const Angle* angle;
1764 const Segment* base;
1765 int winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001766 int oWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001767 int firstIndex = 0;
1768 do {
1769 angle = sorted[firstIndex];
1770 base = angle->segment();
1771 winding = base->windSum(angle);
1772 if (winding != SK_MinS32) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001773 oWinding = base->oppSum(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001774 break;
1775 }
1776 if (++firstIndex == angleCount) {
1777 return SK_MinS32;
1778 }
1779 } while (true);
1780 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001781 int spanWinding = base->spanSign(angle);
1782 bool inner = useInnerWinding(winding + spanWinding, winding);
1783 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001784 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001785 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001786 inner ? winding + spanWinding : winding);
1787 #endif
1788 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001789 winding += spanWinding;
1790 }
1791 #if DEBUG_SORT
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001792 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001793 #endif
1794 int nextIndex = firstIndex + 1;
1795 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001796 winding -= base->spanSign(angle);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001797 oWinding -= base->oppSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001798 do {
1799 if (nextIndex == angleCount) {
1800 nextIndex = 0;
1801 }
1802 angle = sorted[nextIndex];
1803 Segment* segment = angle->segment();
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001804 bool opp = base->fOperand ^ segment->fOperand;
1805 int maxWinding, oMaxWinding;
1806 int spanSign = segment->spanSign(angle);
1807 int oppoSign = segment->oppSign(angle);
1808 if (opp) {
1809 oMaxWinding = oWinding;
1810 oWinding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001811 maxWinding = winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001812 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001813 winding -= oppoSign;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001814 }
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001815 } else {
1816 maxWinding = winding;
1817 winding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001818 oMaxWinding = oWinding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001819 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001820 oWinding -= oppoSign;
1821 }
1822 }
1823 if (segment->windSum(angle) == SK_MinS32) {
1824 if (opp) {
1825 if (useInnerWinding(oMaxWinding, oWinding)) {
1826 oMaxWinding = oWinding;
1827 }
1828 if (oppoSign && useInnerWinding(maxWinding, winding)) {
1829 maxWinding = winding;
1830 }
1831 segment->markAndChaseWinding(angle, oMaxWinding, maxWinding);
1832 } else {
1833 if (useInnerWinding(maxWinding, winding)) {
1834 maxWinding = winding;
1835 }
1836 if (oppoSign && useInnerWinding(oMaxWinding, oWinding)) {
1837 oMaxWinding = oWinding;
1838 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001839 segment->markAndChaseWinding(angle, maxWinding, binary ? oMaxWinding : 0);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001840 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001841 }
1842 } while (++nextIndex != lastIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001843 int minIndex = SkMin32(startIndex, endIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001844 return windSum(minIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001845 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001846
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001847 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001848 int bestT = -1;
1849 SkScalar top = bounds().fTop;
1850 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001851 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001852 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001853 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001854 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001855 if (fTs[start].fWindValue == 0) {
1856 continue;
1857 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001858 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001859 double startT = fTs[start].fT;
1860 double endT = fTs[end].fT;
1861 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001862 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001863 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001864 // FIXME: always use original and limit results to T values within
1865 // start t and end t.
1866 // OPTIMIZE: use specialty function that intersects ray with curve,
1867 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001868 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1869 false, intersections);
1870 if (pts == 0) {
1871 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001872 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001873 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1874 // if the intersection is edge on, wait for another one
1875 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001876 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001877 for (int index = 0; index < pts; ++index) {
1878 SkPoint pt;
1879 double foundT = intersections.fT[0][index];
1880 double testT = startT + (endT - startT) * foundT;
1881 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1882 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001883 if (fVerb > SkPath::kLine_Verb
1884 && !approximately_less_than_zero(foundT)
1885 && !approximately_greater_than_one(foundT)) {
1886 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1887 if (approximately_zero(dx)) {
1888 continue;
1889 }
1890 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001891 bestY = pt.fY;
1892 bestT = foundT < 1 ? start : end;
1893 hitT = testT;
1894 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001895 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001896 } while (fTs[end].fT != 1);
1897 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001898 }
caryclark@google.com18063442012-07-25 12:05:18 +00001899
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001900 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1901 // if a segment is connected to this one, consider it crossing
1902 int tIndex;
1903 if (fPts[0].fX == basePt.fX) {
1904 tIndex = 0;
1905 do {
1906 const Span& sSpan = fTs[tIndex];
1907 const Segment* sOther = sSpan.fOther;
1908 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1909 continue;
1910 }
1911 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1912 : sOther->fBounds.fRight > basePt.fX) {
1913 return true;
1914 }
1915 } while (fTs[++tIndex].fT == 0);
1916 }
1917 if (fPts[fVerb].fX == basePt.fX) {
1918 tIndex = fTs.count() - 1;
1919 do {
1920 const Span& eSpan = fTs[tIndex];
1921 const Segment* eOther = eSpan.fOther;
1922 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1923 continue;
1924 }
1925 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1926 : eOther->fBounds.fRight > basePt.fX) {
1927 return true;
1928 }
1929 } while (fTs[--tIndex].fT == 1);
1930 }
1931 return false;
1932 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001933
caryclark@google.com18063442012-07-25 12:05:18 +00001934 bool decrementSpan(Span* span) {
1935 SkASSERT(span->fWindValue > 0);
1936 if (--(span->fWindValue) == 0) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001937 if (!span->fDone) {
1938 span->fDone = true;
1939 ++fDoneSpans;
1940 }
caryclark@google.com18063442012-07-25 12:05:18 +00001941 return true;
1942 }
1943 return false;
1944 }
1945
caryclark@google.com15fa1382012-05-07 20:49:36 +00001946 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001947 SkASSERT(fDoneSpans <= fTs.count());
1948 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001949 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001950
caryclark@google.comf839c032012-10-26 21:03:50 +00001951 bool done(int min) const {
1952 return fTs[min].fDone;
1953 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001954
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001955 bool done(const Angle* angle) const {
1956 return done(SkMin32(angle->start(), angle->end()));
caryclark@google.com47580692012-07-23 12:14:49 +00001957 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001958
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001959 /*
1960 The M and S variable name parts stand for the operators.
1961 Mi stands for Minuend (see wiki subtraction, analogous to difference)
1962 Su stands for Subtrahend
1963 The Opp variable name part designates that the value is for the Opposite operator.
1964 Opposite values result from combining coincident spans.
1965 */
1966
1967 Segment* findNextOp(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd,
1968 bool& unsortable, ShapeOp op, const int xorMiMask, const int xorSuMask) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001969 const int startIndex = nextStart;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001970 const int endIndex = nextEnd;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001971 SkASSERT(startIndex != endIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001972 const int count = fTs.count();
1973 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
1974 const int step = SkSign32(endIndex - startIndex);
1975 const int end = nextExactSpan(startIndex, step);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001976 SkASSERT(end >= 0);
1977 Span* endSpan = &fTs[end];
1978 Segment* other;
1979 if (isSimple(end)) {
1980 // mark the smaller of startIndex, endIndex done, and all adjacent
1981 // spans with the same T value (but not 'other' spans)
1982 #if DEBUG_WINDING
1983 SkDebugf("%s simple\n", __FUNCTION__);
1984 #endif
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001985 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001986 other = endSpan->fOther;
1987 nextStart = endSpan->fOtherIndex;
1988 double startT = other->fTs[nextStart].fT;
1989 nextEnd = nextStart;
1990 do {
1991 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001992 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00001993 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001994 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1995 return other;
1996 }
1997 // more than one viable candidate -- measure angles to find best
1998 SkTDArray<Angle> angles;
1999 SkASSERT(startIndex - endIndex != 0);
2000 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2001 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002002 buildAngles(end, angles, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002003 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002004 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002005 int angleCount = angles.count();
2006 int firstIndex = findStartingEdge(sorted, startIndex, end);
2007 SkASSERT(firstIndex >= 0);
2008 #if DEBUG_SORT
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002009 debugShowSort(__FUNCTION__, sorted, firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002010 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002011 if (!sortable) {
2012 unsortable = true;
2013 return NULL;
2014 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002015 SkASSERT(sorted[firstIndex]->segment() == this);
2016 #if DEBUG_WINDING
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002017 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
2018 sorted[firstIndex]->sign());
caryclark@google.com235f56a2012-09-14 14:19:30 +00002019 #endif
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002020 int sumMiWinding = updateWinding(endIndex, startIndex);
2021 int sumSuWinding = updateOppWinding(endIndex, startIndex);
2022 if (operand()) {
2023 SkTSwap<int>(sumMiWinding, sumSuWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002024 }
2025 int nextIndex = firstIndex + 1;
2026 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2027 const Angle* foundAngle = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002028 bool foundDone = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002029 // iterate through the angle, and compute everyone's winding
caryclark@google.com235f56a2012-09-14 14:19:30 +00002030 Segment* nextSegment;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002031 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002032 SkASSERT(nextIndex != firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002033 if (nextIndex == angleCount) {
2034 nextIndex = 0;
2035 }
2036 const Angle* nextAngle = sorted[nextIndex];
2037 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002038 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
2039 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
2040 nextAngle->end(), op, sumMiWinding, sumSuWinding,
2041 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
2042 if (activeAngle && (!foundAngle || foundDone)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002043 foundAngle = nextAngle;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002044 foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002045 }
2046 if (nextSegment->done()) {
2047 continue;
2048 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002049 if (nextSegment->windSum(nextAngle) != SK_MinS32) {
2050 continue;
2051 }
2052 Span* last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding,
2053 oppSumWinding, activeAngle, nextAngle);
2054 if (last) {
2055 *chase.append() = last;
2056#if DEBUG_WINDING
2057 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2058 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2059#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002060 }
2061 } while (++nextIndex != lastIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002062 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com235f56a2012-09-14 14:19:30 +00002063 if (!foundAngle) {
2064 return NULL;
2065 }
2066 nextStart = foundAngle->start();
2067 nextEnd = foundAngle->end();
2068 nextSegment = foundAngle->segment();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002069
caryclark@google.com235f56a2012-09-14 14:19:30 +00002070 #if DEBUG_WINDING
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002071 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2072 __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002073 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002074 return nextSegment;
2075 }
caryclark@google.com47580692012-07-23 12:14:49 +00002076
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002077 // so the span needs to contain the pairing info found here
2078 // this should include the winding computed for the edge, and
2079 // what edge it connects to, and whether it is discarded
2080 // (maybe discarded == abs(winding) > 1) ?
2081 // only need derivatives for duration of sorting, add a new struct
2082 // for pairings, remove extra spans that have zero length and
2083 // reference an unused other
2084 // for coincident, the last span on the other may be marked done
2085 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002086
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002087 // if loop is exhausted, contour may be closed.
2088 // FIXME: pass in close point so we can check for closure
2089
2090 // given a segment, and a sense of where 'inside' is, return the next
2091 // segment. If this segment has an intersection, or ends in multiple
2092 // segments, find the mate that continues the outside.
2093 // note that if there are multiples, but no coincidence, we can limit
2094 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00002095
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002096 // mark found segments as done
2097
caryclark@google.com15fa1382012-05-07 20:49:36 +00002098 // start is the index of the beginning T of this edge
2099 // it is guaranteed to have an end which describes a non-zero length (?)
2100 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00002101 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002102 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
2103 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002104 const int startIndex = nextStart;
2105 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002106 int outerWinding = winding;
2107 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00002108 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002109 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
2110 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00002111 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002112 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002113 outerWinding = innerWinding;
2114 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002115 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002116 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002117 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2118 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002119 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002120 int end = nextExactSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002121 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002122 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002123 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002124 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002125 // mark the smaller of startIndex, endIndex done, and all adjacent
2126 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002127 #if DEBUG_WINDING
2128 SkDebugf("%s simple\n", __FUNCTION__);
2129 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002130 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002131 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002132 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00002133 double startT = other->fTs[nextStart].fT;
2134 nextEnd = nextStart;
2135 do {
2136 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002137 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002138 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00002139 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00002140 return other;
2141 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002142 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00002143 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002144 SkASSERT(startIndex - endIndex != 0);
2145 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002146 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002147 buildAngles(end, angles, false);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002148 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002149 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002150 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002151 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002152 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002153 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002154 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002155 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002156 if (!sortable) {
2157 unsortable = true;
2158 return NULL;
2159 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002160 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002161 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002162 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00002163 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00002164 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002165 int nextIndex = firstIndex + 1;
2166 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2167 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00002168 // FIXME: found done logic probably fails if there are more than 4
2169 // sorted angles. It should bias towards the first and last undone
2170 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002171 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00002172 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002173 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002174 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00002175 bool altFlipped = false;
2176 bool foundFlipped = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002177 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002178 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00002179 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002180 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002181 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002182 nextIndex = 0;
2183 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002184 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00002185 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00002186 if (sumWinding) {
2187 lastNonZeroSum = sumWinding;
2188 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002189 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002190 bool nextDone = nextSegment->done(nextAngle);
2191 bool nextTiny = nextSegment->tiny(nextAngle);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002192 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002193 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002194 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002195 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002196 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2197 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002198 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002199 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002200 if (!active) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002201 // FIXME: shouldn't this call mark and chase done ?
caryclark@google.com59823f72012-08-09 18:17:47 +00002202 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002203 // FIXME: shouldn't this call mark and chase winding ?
caryclark@google.com47580692012-07-23 12:14:49 +00002204 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002205 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002206 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002207 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002208 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002209 return NULL;
2210 }
caryclark@google.com47580692012-07-23 12:14:49 +00002211 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002212 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002213 foundDone = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002214 foundFlipped = altFlipped;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002215 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002216 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002217 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002218
caryclark@google.com24bec792012-08-20 12:43:57 +00002219 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002220 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002221 if (foundAngle && foundDone2) {
2222 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002223 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002224 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002225 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002226 foundDone2 = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002227 foundFlipped = altFlipped;
2228 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002229 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002230 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002231 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002232 }
2233 // if the winding is non-zero, nextAngle does not connect to
2234 // current chain. If we haven't done so already, mark the angle
2235 // as done, record the winding value, and mark connected unambiguous
2236 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002237 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002238 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002239 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002240 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002241 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002242 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002243 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002244 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002245 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002246 }
2247 if (last) {
2248 *chase.append() = last;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002249 #if DEBUG_WINDING
2250 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2251 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2252 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002253 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002254 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002255 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002256 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002257 if (!foundAngle) {
2258 return NULL;
2259 }
2260 nextStart = foundAngle->start();
2261 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002262 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002263 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002264 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2265 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002266 if (winding) {
2267 #if DEBUG_WINDING
2268 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2269 if (foundSum == SK_MinS32) {
2270 SkDebugf("?");
2271 } else {
2272 SkDebugf("%d", foundSum);
2273 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002274 SkDebugf("\n");
2275 #endif
2276 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002277 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002278 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002279 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002280 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002281 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002282 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002283
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002284 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002285 const int startIndex = nextStart;
2286 const int endIndex = nextEnd;
2287 SkASSERT(startIndex != endIndex);
2288 int count = fTs.count();
2289 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2290 : startIndex > 0);
2291 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002292 int end = nextExactSpan(startIndex, step);
caryclark@google.com24bec792012-08-20 12:43:57 +00002293 SkASSERT(end >= 0);
2294 Span* endSpan = &fTs[end];
2295 Segment* other;
2296 markDone(SkMin32(startIndex, endIndex), 1);
2297 if (isSimple(end)) {
2298 #if DEBUG_WINDING
2299 SkDebugf("%s simple\n", __FUNCTION__);
2300 #endif
2301 other = endSpan->fOther;
2302 nextStart = endSpan->fOtherIndex;
2303 double startT = other->fTs[nextStart].fT;
2304 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002305 if ((approximately_less_than_zero(startT) && step < 0)
2306 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002307 step = -step;
2308 SkDEBUGCODE(firstLoop = false;)
2309 }
2310 do {
2311 nextEnd = nextStart;
2312 do {
2313 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002314 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002315 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002316 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2317 break;
2318 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002319 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002320 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002321 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002322 SkDEBUGCODE(firstLoop = false;)
2323 step = -step;
2324 } while (true);
2325 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2326 return other;
2327 }
2328 SkTDArray<Angle> angles;
2329 SkASSERT(startIndex - endIndex != 0);
2330 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2331 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002332 buildAngles(end, angles, false);
caryclark@google.com24bec792012-08-20 12:43:57 +00002333 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002334 bool sortable = SortAngles(angles, sorted);
caryclark@google.com24bec792012-08-20 12:43:57 +00002335 int angleCount = angles.count();
2336 int firstIndex = findStartingEdge(sorted, startIndex, end);
2337 SkASSERT(firstIndex >= 0);
2338 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002339 debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002340 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002341 if (!sortable) {
2342 unsortable = true;
2343 return NULL;
2344 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002345 SkASSERT(sorted[firstIndex]->segment() == this);
2346 int nextIndex = firstIndex + 1;
2347 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2348 const Angle* nextAngle;
2349 Segment* nextSegment;
2350 do {
2351 if (nextIndex == angleCount) {
2352 nextIndex = 0;
2353 }
2354 nextAngle = sorted[nextIndex];
2355 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002356 if (!nextSegment->done(nextAngle)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002357 break;
2358 }
2359 if (++nextIndex == lastIndex) {
2360 return NULL;
2361 }
2362 } while (true);
2363 nextStart = nextAngle->start();
2364 nextEnd = nextAngle->end();
2365 return nextSegment;
2366 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002367
2368 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2369 int angleCount = sorted.count();
2370 int firstIndex = -1;
2371 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2372 const Angle* angle = sorted[angleIndex];
2373 if (angle->segment() == this && angle->start() == end &&
2374 angle->end() == start) {
2375 firstIndex = angleIndex;
2376 break;
2377 }
2378 }
2379 return firstIndex;
2380 }
2381
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002382 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002383 void findTooCloseToCall(bool thisXor, bool otherXor) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002384 int count = fTs.count();
2385 if (count < 3) { // require t=0, x, 1 at minimum
2386 return;
2387 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002388 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002389 int moCount;
2390 Span* match;
2391 Segment* mOther;
2392 do {
2393 match = &fTs[matchIndex];
2394 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002395 // FIXME: allow quads, cubics to be near coincident?
2396 if (mOther->fVerb == SkPath::kLine_Verb) {
2397 moCount = mOther->fTs.count();
2398 if (moCount >= 3) {
2399 break;
2400 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002401 }
2402 if (++matchIndex >= count) {
2403 return;
2404 }
2405 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002406 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002407 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002408 // look for a pair of nearby T values that map to the same (x,y) value
2409 // if found, see if the pair of other segments share a common point. If
2410 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002411 for (int index = matchIndex + 1; index < count; ++index) {
2412 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002413 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002414 continue;
2415 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002416 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002417 if (tOther->fVerb != SkPath::kLine_Verb) {
2418 continue; // FIXME: allow quads, cubics to be near coincident?
2419 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002420 int toCount = tOther->fTs.count();
2421 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002422 continue;
2423 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002424 const SkPoint* testPt = &xyAtT(test);
2425 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002426 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002427 moCount = toCount;
2428 match = test;
2429 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002430 matchPt = testPt;
2431 continue;
2432 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002433 int moStart = -1;
2434 int moEnd = -1;
2435 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002436 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002437 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002438 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002439 continue;
2440 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002441 if (moSpan.fOther == this) {
2442 if (moSpan.fOtherT == match->fT) {
2443 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002444 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002445 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002446 continue;
2447 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002448 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002449 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2450 moStart = -1;
2451 break;
2452 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002453 SkASSERT(moEnd == -1);
2454 moEnd = moIndex;
2455 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002456 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002457 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002458 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002459 continue;
2460 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002461 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002462 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002463 continue;
2464 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002465 int toStart = -1;
2466 int toEnd = -1;
2467 double toStartT, toEndT;
2468 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2469 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002470 if (toSpan.fDone) {
2471 continue;
2472 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002473 if (toSpan.fOther == this) {
2474 if (toSpan.fOtherT == test->fT) {
2475 toStart = toIndex;
2476 toStartT = toSpan.fT;
2477 }
2478 continue;
2479 }
2480 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002481 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2482 moStart = -1;
2483 break;
2484 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002485 SkASSERT(toEnd == -1);
2486 toEnd = toIndex;
2487 toEndT = toSpan.fT;
2488 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002489 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002490 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2491 if (toStart <= 0 || toEnd <= 0) {
2492 continue;
2493 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002494 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002495 continue;
2496 }
2497 // test to see if the segment between there and here is linear
2498 if (!mOther->isLinear(moStart, moEnd)
2499 || !tOther->isLinear(toStart, toEnd)) {
2500 continue;
2501 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002502 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002503 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002504 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002505 } else {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002506 // FIXME: this is bogus for multiple ops
2507 // the xorMask needs to be accumulated from the union of the two
2508 // edges -- which means that the segment must have its own copy of the mask
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002509 mOther->addTCoincident(thisXor, otherXor,
2510 moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002511 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002512 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002513 }
2514
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002515 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002516 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002517 // a) mark spans with either end unsortable as done, or
2518 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2519 // when encountering an unsortable span
2520
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002521 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2522 // and use more concise logic like the old edge walker code?
2523 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002524 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002525 // iterate through T intersections and return topmost
2526 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002527 SkASSERT(!done());
2528 int firstT;
2529 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002530 SkPoint topPt;
2531 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002532 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002533 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002534 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002535 bool lastUnsortable = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002536 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002537 const Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00002538 if (span.fUnsortableStart | lastUnsortable) {
2539 goto next;
2540 }
2541 if (!span.fDone | !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002542 const SkPoint& intercept = xyAtT(&span);
2543 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2544 && topPt.fX > intercept.fX)) {
2545 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002546 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002547 } else if (topPt == intercept) {
2548 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002549 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002550 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002551 next:
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002552 lastDone = span.fDone;
caryclark@google.comf839c032012-10-26 21:03:50 +00002553 lastUnsortable = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002554 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002555 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002556 int step = 1;
2557 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002558 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002559 step = -1;
2560 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002561 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002562 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002563 // if the topmost T is not on end, or is three-way or more, find left
2564 // look for left-ness from tLeft to firstT (matching y of other)
2565 SkTDArray<Angle> angles;
2566 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002567 addTwoAngles(end, firstT, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002568 buildAngles(firstT, angles, true);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002569 SkTDArray<Angle*> sorted;
caryclark@google.comf839c032012-10-26 21:03:50 +00002570 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002571 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002572 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002573 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00002574 if (!sortable) {
2575 return NULL;
2576 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002577 // skip edges that have already been processed
2578 firstT = -1;
2579 Segment* leftSegment;
2580 do {
2581 const Angle* angle = sorted[++firstT];
caryclark@google.comf839c032012-10-26 21:03:50 +00002582 SkASSERT(!angle->unsortable());
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002583 leftSegment = angle->segment();
2584 tIndex = angle->end();
2585 endIndex = angle->start();
2586 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002587 return leftSegment;
2588 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002589
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002590 // FIXME: not crazy about this
2591 // when the intersections are performed, the other index is into an
2592 // incomplete array. as the array grows, the indices become incorrect
2593 // while the following fixes the indices up again, it isn't smart about
2594 // skipping segments whose indices are already correct
2595 // assuming we leave the code that wrote the index in the first place
2596 void fixOtherTIndex() {
2597 int iCount = fTs.count();
2598 for (int i = 0; i < iCount; ++i) {
2599 Span& iSpan = fTs[i];
2600 double oT = iSpan.fOtherT;
2601 Segment* other = iSpan.fOther;
2602 int oCount = other->fTs.count();
2603 for (int o = 0; o < oCount; ++o) {
2604 Span& oSpan = other->fTs[o];
2605 if (oT == oSpan.fT && this == oSpan.fOther) {
2606 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002607 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002608 }
2609 }
2610 }
2611 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002612
caryclark@google.com495f8e42012-05-31 13:13:11 +00002613 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002614 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002615 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002616 SkASSERT(end >= 0);
2617 if (multipleSpans(end)) {
2618 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002619 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002620 const Span& endSpan = fTs[end];
2621 Segment* other = endSpan.fOther;
2622 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002623 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002624 Span* last = other->innerChaseDone(index, step, winding);
2625 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002626 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002627 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002628
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002629 Span* innerChaseDoneBinary(int index, int step, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002630 int end = nextExactSpan(index, step);
2631 SkASSERT(end >= 0);
2632 if (multipleSpans(end)) {
2633 return &fTs[end];
2634 }
2635 const Span& endSpan = fTs[end];
2636 Segment* other = endSpan.fOther;
2637 index = endSpan.fOtherIndex;
2638 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002639 Span* last = other->innerChaseDoneBinary(index, step, winding, oppWinding);
2640 other->markDoneBinary(SkMin32(index, otherEnd), winding, oppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002641 return last;
2642 }
2643
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002644 Span* innerChaseDoneBinary(int index, int step) {
2645 int end = nextExactSpan(index, step);
2646 SkASSERT(end >= 0);
2647 if (multipleSpans(end)) {
2648 return &fTs[end];
2649 }
2650 const Span& endSpan = fTs[end];
2651 Segment* other = endSpan.fOther;
2652 index = endSpan.fOtherIndex;
2653 int otherEnd = other->nextExactSpan(index, step);
2654 Span* last = other->innerChaseDoneBinary(index, step);
2655 other->markDoneBinary(SkMin32(index, otherEnd));
2656 return last;
2657 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002658
caryclark@google.com59823f72012-08-09 18:17:47 +00002659 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002660 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002661 SkASSERT(end >= 0);
2662 if (multipleSpans(end)) {
2663 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002664 }
2665 const Span& endSpan = fTs[end];
2666 Segment* other = endSpan.fOther;
2667 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002668 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002669 int min = SkMin32(index, otherEnd);
2670 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002671 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002672 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002673 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002674 Span* last = other->innerChaseWinding(index, step, winding);
2675 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002676 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002677 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002678
caryclark@google.com31143cf2012-11-09 22:14:19 +00002679 Span* innerChaseWinding(int index, int step, int winding, int oppWinding) {
2680 int end = nextExactSpan(index, step);
2681 SkASSERT(end >= 0);
2682 if (multipleSpans(end)) {
2683 return &fTs[end];
2684 }
2685 const Span& endSpan = fTs[end];
2686 Segment* other = endSpan.fOther;
2687 index = endSpan.fOtherIndex;
2688 int otherEnd = other->nextExactSpan(index, step);
2689 int min = SkMin32(index, otherEnd);
2690 if (other->fTs[min].fWindSum != SK_MinS32) {
2691 SkASSERT(other->fTs[min].fWindSum == winding);
2692 return NULL;
2693 }
2694 Span* last = other->innerChaseWinding(index, step, winding, oppWinding);
2695 other->markWinding(min, winding, oppWinding);
2696 return last;
2697 }
2698
caryclark@google.com235f56a2012-09-14 14:19:30 +00002699 void init(const SkPoint pts[], SkPath::Verb verb, bool operand) {
2700 fDoneSpans = 0;
2701 fOperand = operand;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002702 fPts = pts;
2703 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002704 }
2705
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002706 void initWinding(int start, int end, int winding, int oppWinding) {
2707 int local = spanSign(start, end);
2708 if (local * winding >= 0) {
2709 winding += local;
2710 }
2711 local = oppSign(start, end);
2712 if (local * oppWinding >= 0) {
2713 oppWinding += local;
2714 }
2715 markAndChaseWinding(start, end, winding, oppWinding);
2716 }
2717
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002718 bool intersected() const {
2719 return fTs.count() > 0;
2720 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002721
2722 bool isConnected(int startIndex, int endIndex) const {
2723 return fTs[startIndex].fWindSum != SK_MinS32
2724 || fTs[endIndex].fWindSum != SK_MinS32;
2725 }
2726
caryclark@google.com235f56a2012-09-14 14:19:30 +00002727 bool isHorizontal() const {
2728 return fBounds.fTop == fBounds.fBottom;
2729 }
2730
caryclark@google.com15fa1382012-05-07 20:49:36 +00002731 bool isLinear(int start, int end) const {
2732 if (fVerb == SkPath::kLine_Verb) {
2733 return true;
2734 }
2735 if (fVerb == SkPath::kQuad_Verb) {
2736 SkPoint qPart[3];
2737 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2738 return QuadIsLinear(qPart);
2739 } else {
2740 SkASSERT(fVerb == SkPath::kCubic_Verb);
2741 SkPoint cPart[4];
2742 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2743 return CubicIsLinear(cPart);
2744 }
2745 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002746
2747 // OPTIMIZE: successive calls could start were the last leaves off
2748 // or calls could specialize to walk forwards or backwards
2749 bool isMissing(double startT) const {
2750 size_t tCount = fTs.count();
2751 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002752 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002753 return false;
2754 }
2755 }
2756 return true;
2757 }
2758
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002759 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002760 int count = fTs.count();
2761 if (count == 2) {
2762 return true;
2763 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002764 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002765 if (approximately_less_than_zero(t)) {
2766 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002767 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002768 if (approximately_greater_than_one(t)) {
2769 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002770 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002771 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002772 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002773
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002774 bool isVertical() const {
2775 return fBounds.fLeft == fBounds.fRight;
2776 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002777
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002778 SkScalar leftMost(int start, int end) const {
2779 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2780 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002781
caryclark@google.com495f8e42012-05-31 13:13:11 +00002782 // this span is excluded by the winding rule -- chase the ends
2783 // as long as they are unambiguous to mark connections as done
2784 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002785 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002786 int index = angle->start();
2787 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002788 return markAndChaseDone(index, endIndex, winding);
2789 }
2790
caryclark@google.com31143cf2012-11-09 22:14:19 +00002791 Span* markAndChaseDone(int index, int endIndex, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002792 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002793 Span* last = innerChaseDone(index, step, winding);
2794 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002795 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002796 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002797
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002798 Span* markAndChaseDoneBinary(const Angle* angle, int winding, int oppWinding) {
2799 int index = angle->start();
2800 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002801 int step = SkSign32(endIndex - index);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002802 Span* last = innerChaseDoneBinary(index, step, winding, oppWinding);
2803 markDoneBinary(SkMin32(index, endIndex), winding, oppWinding);
2804 return last;
2805 }
2806
2807 Span* markAndChaseDoneBinary(int index, int endIndex) {
2808 int step = SkSign32(endIndex - index);
2809 Span* last = innerChaseDoneBinary(index, step);
2810 markDoneBinary(SkMin32(index, endIndex));
caryclark@google.com31143cf2012-11-09 22:14:19 +00002811 return last;
2812 }
2813
caryclark@google.com59823f72012-08-09 18:17:47 +00002814 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002815 int index = angle->start();
2816 int endIndex = angle->end();
2817 int min = SkMin32(index, endIndex);
2818 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002819 Span* last = innerChaseWinding(index, step, winding);
2820 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002821 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002822 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002823
2824 Span* markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002825 int min = SkMin32(index, endIndex);
2826 int step = SkSign32(endIndex - index);
2827 Span* last = innerChaseWinding(index, step, winding, oppWinding);
2828 markWinding(min, winding, oppWinding);
2829 return last;
2830 }
2831
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002832 Span* markAndChaseWinding(const Angle* angle, int winding, int oppWinding) {
2833 int start = angle->start();
2834 int end = angle->end();
2835 return markAndChaseWinding(start, end, winding, oppWinding);
2836 }
2837
2838 Span* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
2839 bool activeAngle, const Angle* angle) {
2840 SkASSERT(angle->segment() == this);
2841 if (useInnerWinding(maxWinding, sumWinding)) {
2842 maxWinding = sumWinding;
2843 }
2844 if (oppMaxWinding != oppSumWinding && useInnerWinding(oppMaxWinding, oppSumWinding)) {
2845 oppMaxWinding = oppSumWinding;
2846 }
2847 Span* last;
2848 if (activeAngle) {
2849 last = markAndChaseWinding(angle, maxWinding, oppMaxWinding);
2850 } else {
2851 last = markAndChaseDoneBinary(angle, maxWinding, oppMaxWinding);
2852 }
2853 return last;
2854 }
2855
caryclark@google.com495f8e42012-05-31 13:13:11 +00002856 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002857 // This may be called when the segment is already marked done. While this
2858 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002859 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002860 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002861 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002862 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002863 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002864 double referenceT = fTs[index].fT;
2865 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002866 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2867 markOneDone(__FUNCTION__, lesser, winding);
2868 }
2869 do {
2870 markOneDone(__FUNCTION__, index, winding);
2871 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00002872 }
2873
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002874 void markDoneBinary(int index, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002875 // SkASSERT(!done());
2876 SkASSERT(winding);
2877 double referenceT = fTs[index].fT;
2878 int lesser = index;
2879 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002880 markOneDoneBinary(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002881 }
2882 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002883 markOneDoneBinary(__FUNCTION__, index, winding, oppWinding);
2884 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2885 }
2886
2887 void markDoneBinary(int index) {
2888 double referenceT = fTs[index].fT;
2889 int lesser = index;
2890 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2891 markOneDoneBinary(__FUNCTION__, lesser);
2892 }
2893 do {
2894 markOneDoneBinary(__FUNCTION__, index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002895 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002896 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002897
caryclark@google.com24bec792012-08-20 12:43:57 +00002898 void markOneDone(const char* funName, int tIndex, int winding) {
2899 Span* span = markOneWinding(funName, tIndex, winding);
2900 if (!span) {
2901 return;
2902 }
2903 span->fDone = true;
2904 fDoneSpans++;
2905 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002906
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002907 void markOneDoneBinary(const char* funName, int tIndex) {
2908 Span* span = verifyOneWinding(funName, tIndex);
2909 if (!span) {
2910 return;
2911 }
2912 span->fDone = true;
2913 fDoneSpans++;
2914 }
2915
2916 void markOneDoneBinary(const char* funName, int tIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002917 Span* span = markOneWinding(funName, tIndex, winding, oppWinding);
2918 if (!span) {
2919 return;
2920 }
2921 span->fDone = true;
2922 fDoneSpans++;
2923 }
2924
caryclark@google.com24bec792012-08-20 12:43:57 +00002925 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2926 Span& span = fTs[tIndex];
2927 if (span.fDone) {
2928 return NULL;
2929 }
2930 #if DEBUG_MARK_DONE
2931 debugShowNewWinding(funName, span, winding);
2932 #endif
2933 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002934 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002935 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002936 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002937 span.fWindSum = winding;
2938 return &span;
2939 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002940
caryclark@google.com31143cf2012-11-09 22:14:19 +00002941 Span* markOneWinding(const char* funName, int tIndex, int winding, int oppWinding) {
2942 Span& span = fTs[tIndex];
2943 if (span.fDone) {
2944 return NULL;
2945 }
2946 #if DEBUG_MARK_DONE
2947 debugShowNewWinding(funName, span, winding, oppWinding);
2948 #endif
2949 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
2950 #ifdef SK_DEBUG
2951 SkASSERT(abs(winding) <= gDebugMaxWindSum);
2952 #endif
2953 span.fWindSum = winding;
2954 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
2955 #ifdef SK_DEBUG
2956 SkASSERT(abs(oppWinding) <= gDebugMaxWindSum);
2957 #endif
2958 span.fOppSum = oppWinding;
2959 return &span;
2960 }
2961
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002962 Span* verifyOneWinding(const char* funName, int tIndex) {
2963 Span& span = fTs[tIndex];
2964 if (span.fDone) {
2965 return NULL;
2966 }
2967 #if DEBUG_MARK_DONE
2968 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
2969 #endif
2970 SkASSERT(span.fWindSum != SK_MinS32);
2971 SkASSERT(span.fOppSum != SK_MinS32);
2972 return &span;
2973 }
2974
caryclark@google.comf839c032012-10-26 21:03:50 +00002975 // note that just because a span has one end that is unsortable, that's
2976 // not enough to mark it done. The other end may be sortable, allowing the
2977 // span to be added.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002978 void markUnsortable(int start, int end) {
2979 Span* span = &fTs[start];
2980 if (start < end) {
2981 span->fUnsortableStart = true;
2982 } else {
2983 --span;
2984 span->fUnsortableEnd = true;
2985 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002986 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002987 return;
2988 }
2989 span->fDone = true;
2990 fDoneSpans++;
2991 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002992
caryclark@google.com59823f72012-08-09 18:17:47 +00002993 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002994 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002995 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002996 double referenceT = fTs[index].fT;
2997 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002998 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2999 markOneWinding(__FUNCTION__, lesser, winding);
3000 }
3001 do {
3002 markOneWinding(__FUNCTION__, index, winding);
3003 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00003004 }
3005
3006 void markWinding(int index, int winding, int oppWinding) {
3007 // SkASSERT(!done());
3008 SkASSERT(winding);
3009 double referenceT = fTs[index].fT;
3010 int lesser = index;
3011 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3012 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003013 }
3014 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003015 markOneWinding(__FUNCTION__, index, winding, oppWinding);
3016 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003017 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003018
caryclark@google.com2ddff932012-08-07 21:25:27 +00003019 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003020 int nextDoorWind = SK_MaxS32;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003021 int nextOppWind = SK_MaxS32;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003022 if (tIndex > 0) {
3023 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003024 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003025 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003026 nextOppWind = below.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003027 }
3028 }
3029 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
3030 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003031 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003032 nextDoorWind = above.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003033 nextOppWind = above.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003034 }
3035 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003036 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
3037 const Span& below = fTs[tIndex - 1];
3038 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003039 nextOppWind = below.fOppValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003040 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003041 if (nextDoorWind != SK_MaxS32) {
3042 Span& newSpan = fTs[tIndex];
3043 newSpan.fWindValue = nextDoorWind;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003044 newSpan.fOppValue = nextOppWind;
caryclark@google.comf839c032012-10-26 21:03:50 +00003045 if (!nextDoorWind && !newSpan.fDone) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003046 newSpan.fDone = true;
3047 ++fDoneSpans;
3048 }
3049 }
3050 }
3051
caryclark@google.com9764cc62012-07-12 19:29:45 +00003052 // return span if when chasing, two or more radiating spans are not done
3053 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
3054 // candidate and the remaining spans have windValue == 0 (canceled by
3055 // coincidence). The coincident edges could either be removed altogether,
3056 // or this code could be more complicated in detecting this case. Worth it?
3057 bool multipleSpans(int end) const {
3058 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003059 }
3060
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003061 // This has callers for two different situations: one establishes the end
3062 // of the current span, and one establishes the beginning of the next span
3063 // (thus the name). When this is looking for the end of the current span,
3064 // coincidence is found when the beginning Ts contain -step and the end
3065 // contains step. When it is looking for the beginning of the next, the
3066 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003067 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003068 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003069 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00003070 int count = fTs.count();
3071 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00003072 while (step > 0 ? ++to < count : --to >= 0) {
3073 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003074 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003075 continue;
3076 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003077 return to;
3078 }
3079 return -1;
3080 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00003081
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003082 // FIXME
3083 // this returns at any difference in T, vs. a preset minimum. It may be
3084 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003085 // OPTIMIZATION splitting this into separate loops for up/down steps
3086 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003087 int nextExactSpan(int from, int step) const {
3088 const Span& fromSpan = fTs[from];
3089 int count = fTs.count();
3090 int to = from;
3091 while (step > 0 ? ++to < count : --to >= 0) {
3092 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00003093 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003094 continue;
3095 }
3096 return to;
3097 }
3098 return -1;
3099 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003100
caryclark@google.com235f56a2012-09-14 14:19:30 +00003101 bool operand() const {
3102 return fOperand;
3103 }
3104
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003105 int oppSign(const Angle* angle) const {
3106 SkASSERT(angle->segment() == this);
3107 return oppSign(angle->start(), angle->end());
3108 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003109
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003110 int oppSign(int startIndex, int endIndex) const {
3111 int result = startIndex < endIndex ? -fTs[startIndex].fOppValue
3112 : fTs[endIndex].fOppValue;
3113#if DEBUG_WIND_BUMP
3114 SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
3115#endif
3116 return result;
3117 }
3118
caryclark@google.com31143cf2012-11-09 22:14:19 +00003119 int oppSum(int tIndex) const {
3120 return fTs[tIndex].fOppSum;
3121 }
3122
3123 int oppSum(const Angle* angle) const {
3124 int lesser = SkMin32(angle->start(), angle->end());
3125 return fTs[lesser].fOppSum;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003126 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003127
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003128 int oppValue(int tIndex) const {
3129 return fTs[tIndex].fOppValue;
3130 }
3131
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003132 const SkPoint* pts() const {
3133 return fPts;
3134 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003135
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003136 void reset() {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003137 init(NULL, (SkPath::Verb) -1, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003138 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
3139 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003140 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00003141
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003142 void setUpWindings(int index, int endIndex, int& sumMiWinding, int& sumSuWinding,
3143 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
3144 int deltaSum = spanSign(index, endIndex);
3145 int oppDeltaSum = oppSign(index, endIndex);
3146 if (operand()) {
3147 maxWinding = sumSuWinding;
3148 sumWinding = sumSuWinding -= deltaSum;
3149 oppMaxWinding = sumMiWinding;
3150 oppSumWinding = sumMiWinding -= oppDeltaSum;
3151 } else {
3152 maxWinding = sumMiWinding;
3153 sumWinding = sumMiWinding -= deltaSum;
3154 oppMaxWinding = sumSuWinding;
3155 oppSumWinding = sumSuWinding -= oppDeltaSum;
3156 }
3157 }
3158
caryclark@google.comf839c032012-10-26 21:03:50 +00003159 // This marks all spans unsortable so that this info is available for early
3160 // exclusion in find top and others. This could be optimized to only mark
3161 // adjacent spans that unsortable. However, this makes it difficult to later
3162 // determine starting points for edge detection in find top and the like.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003163 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003164 bool sortable = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003165 int angleCount = angles.count();
3166 int angleIndex;
3167 angleList.setReserve(angleCount);
3168 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003169 Angle& angle = angles[angleIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00003170 *angleList.append() = &angle;
3171 sortable &= !angle.unsortable();
3172 }
3173 if (sortable) {
3174 QSort<Angle>(angleList.begin(), angleList.end() - 1);
3175 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3176 if (angles[angleIndex].unsortable()) {
3177 sortable = false;
3178 break;
3179 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003180 }
3181 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003182 if (!sortable) {
3183 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3184 Angle& angle = angles[angleIndex];
3185 angle.segment()->markUnsortable(angle.start(), angle.end());
3186 }
3187 }
3188 return sortable;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003189 }
3190
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003191 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003192 const Span& span(int tIndex) const {
3193 return fTs[tIndex];
3194 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003195
caryclark@google.com235f56a2012-09-14 14:19:30 +00003196 int spanSign(const Angle* angle) const {
3197 SkASSERT(angle->segment() == this);
3198 return spanSign(angle->start(), angle->end());
3199 }
3200
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003201 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003202 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue
3203 : fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003204#if DEBUG_WIND_BUMP
3205 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
3206#endif
3207 return result;
3208 }
3209
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003210 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003211 double t(int tIndex) const {
3212 return fTs[tIndex].fT;
3213 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003214
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003215 bool tiny(const Angle* angle) const {
3216 int start = angle->start();
3217 int end = angle->end();
caryclark@google.comf839c032012-10-26 21:03:50 +00003218 const Span& mSpan = fTs[SkMin32(start, end)];
3219 return mSpan.fTiny;
3220 }
3221
caryclark@google.com18063442012-07-25 12:05:18 +00003222 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
3223 double start) {
3224 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003225 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00003226 *outsideTs.append() = end;
3227 *outsideTs.append() = start;
3228 }
3229 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003230
caryclark@google.com24bec792012-08-20 12:43:57 +00003231 void undoneSpan(int& start, int& end) {
3232 size_t tCount = fTs.count();
3233 size_t index;
3234 for (index = 0; index < tCount; ++index) {
3235 if (!fTs[index].fDone) {
3236 break;
3237 }
3238 }
3239 SkASSERT(index < tCount - 1);
3240 start = index;
3241 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003242 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00003243 SkASSERT(index < tCount);
3244 SkASSERT(index < tCount);
3245 end = index;
3246 }
caryclark@google.com18063442012-07-25 12:05:18 +00003247
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003248 bool unsortable(int index) const {
3249 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
3250 }
3251
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003252 void updatePts(const SkPoint pts[]) {
3253 fPts = pts;
3254 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003255
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003256 int updateOppWinding(int index, int endIndex) const {
3257 int lesser = SkMin32(index, endIndex);
3258 int oppWinding = oppSum(lesser);
3259 int oppSpanWinding = oppSign(index, endIndex);
3260 if (oppSpanWinding && useInnerWinding(oppWinding - oppSpanWinding, oppWinding)) {
3261 oppWinding -= oppSpanWinding;
3262 }
3263 return oppWinding;
3264 }
3265
3266 int updateOppWinding(const Angle* angle) const {
3267 int startIndex = angle->start();
3268 int endIndex = angle->end();
3269 return updateOppWinding(endIndex, startIndex);
3270 }
3271
3272 int updateOppWindingReverse(const Angle* angle) const {
3273 int startIndex = angle->start();
3274 int endIndex = angle->end();
3275 return updateOppWinding(startIndex, endIndex);
3276 }
3277
3278 int updateWinding(int index, int endIndex) const {
3279 int lesser = SkMin32(index, endIndex);
3280 int winding = windSum(lesser);
3281 int spanWinding = spanSign(index, endIndex);
3282 if (useInnerWinding(winding - spanWinding, winding)) {
3283 winding -= spanWinding;
3284 }
3285 return winding;
3286 }
3287
3288 int updateWinding(const Angle* angle) const {
3289 int startIndex = angle->start();
3290 int endIndex = angle->end();
3291 return updateWinding(endIndex, startIndex);
3292 }
3293
3294 int updateWindingReverse(const Angle* angle) const {
3295 int startIndex = angle->start();
3296 int endIndex = angle->end();
3297 return updateWinding(startIndex, endIndex);
3298 }
3299
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003300 SkPath::Verb verb() const {
3301 return fVerb;
3302 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003303
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003304 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003305 return fTs[tIndex].fWindSum;
3306 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003307
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003308 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003309 int start = angle->start();
3310 int end = angle->end();
3311 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003312 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003313 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003314
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003315 int windValue(int tIndex) const {
3316 return fTs[tIndex].fWindValue;
3317 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003318
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003319 int windValue(const Angle* angle) const {
3320 int start = angle->start();
3321 int end = angle->end();
3322 int index = SkMin32(start, end);
3323 return windValue(index);
3324 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003325
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003326 SkScalar xAtT(const Span* span) const {
3327 return xyAtT(span).fX;
3328 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003329
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003330 const SkPoint& xyAtT(int index) const {
3331 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003332 }
3333
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003334 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003335 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003336 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003337 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003338 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003339 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003340 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003341 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003342 }
3343 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00003344 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003345 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003346
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003347 SkScalar yAtT(int index) const {
3348 return yAtT(&fTs[index]);
3349 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003350
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003351 SkScalar yAtT(const Span* span) const {
3352 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003353 }
3354
caryclark@google.com729e1c42012-11-21 21:36:34 +00003355 void zeroSpan(Span* span, double otherT) {
caryclark@google.com6ec15262012-11-16 20:16:50 +00003356 SkASSERT(span->fWindValue > 0);
3357 span->fWindValue = 0;
3358 if (!span->fDone) {
3359 span->fDone = true;
3360 ++fDoneSpans;
3361 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00003362 int oppValue = span->fOppValue;
3363 if (!oppValue) {
3364 return;
3365 }
3366 span->fOppValue = 0;
3367 Segment* other = span->fOther;
3368 Span& oSpan = other->fTs[span->fOtherIndex];
3369 SkASSERT(0);
caryclark@google.com6ec15262012-11-16 20:16:50 +00003370 }
3371
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003372#if DEBUG_DUMP
3373 void dump() const {
3374 const char className[] = "Segment";
3375 const int tab = 4;
3376 for (int i = 0; i < fTs.count(); ++i) {
3377 SkPoint out;
3378 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
3379 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003380 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003381 tab + sizeof(className), className, fID,
3382 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003383 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003384 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003385 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003386 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00003387 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003388 }
3389#endif
3390
caryclark@google.com47580692012-07-23 12:14:49 +00003391#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00003392 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003393 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00003394 for (int i = 0; i < fTs.count(); ++i) {
3395 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3396 return;
3397 }
3398 }
3399 SkASSERT(0);
3400 }
3401#endif
3402
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003403#if DEBUG_DUMP
3404 int debugID() const {
3405 return fID;
3406 }
3407#endif
3408
caryclark@google.com24bec792012-08-20 12:43:57 +00003409#if DEBUG_WINDING
3410 void debugShowSums() const {
3411 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
3412 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
3413 for (int i = 0; i < fTs.count(); ++i) {
3414 const Span& span = fTs[i];
3415 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
3416 if (span.fWindSum == SK_MinS32) {
3417 SkDebugf("?");
3418 } else {
3419 SkDebugf("%d", span.fWindSum);
3420 }
3421 SkDebugf("]");
3422 }
3423 SkDebugf("\n");
3424 }
3425#endif
3426
caryclark@google.comcc905052012-07-25 20:59:42 +00003427#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003428 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00003429 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00003430 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00003431 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00003432 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
3433 }
3434 SkDebugf("\n");
3435 }
3436#endif
3437
caryclark@google.com027de222012-07-12 12:52:50 +00003438#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003439 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00003440 if (done()) {
3441 return;
3442 }
3443 for (int i = 0; i < fTs.count(); ++i) {
3444 if (fTs[i].fDone) {
3445 continue;
3446 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003447 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00003448 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3449 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3450 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3451 }
3452 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00003453 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00003454 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00003455 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003456 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3457 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3458 if (fTs[i].fWindSum == SK_MinS32) {
3459 SkDebugf("?");
3460 } else {
3461 SkDebugf("%d", fTs[i].fWindSum);
3462 }
3463 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00003464 }
3465 }
skia.committer@gmail.comb0a327e2012-11-21 02:02:25 +00003466
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003467 // This isn't useful yet -- but leaving it in for now in case i think of something
3468 // to use it for
3469 void validateActiveSpans() const {
3470 if (done()) {
3471 return;
3472 }
3473 int tCount = fTs.count();
3474 for (int index = 0; index < tCount; ++index) {
3475 if (fTs[index].fDone) {
3476 continue;
3477 }
3478 // count number of connections which are not done
3479 int first = index;
3480 double baseT = fTs[index].fT;
3481 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
3482 --first;
3483 }
3484 int last = index;
3485 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
3486 ++last;
3487 }
3488 int connections = 0;
3489 connections += first > 0 && !fTs[first - 1].fDone;
3490 for (int test = first; test <= last; ++test) {
3491 connections += !fTs[test].fDone;
3492 const Segment* other = fTs[test].fOther;
3493 int oIndex = fTs[test].fOtherIndex;
3494 connections += !other->fTs[oIndex].fDone;
3495 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3496 }
3497 // SkASSERT(!(connections & 1));
3498 }
3499 }
caryclark@google.com027de222012-07-12 12:52:50 +00003500#endif
3501
caryclark@google.com0c803d02012-08-06 11:15:47 +00003502#if DEBUG_MARK_DONE
3503 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
3504 const SkPoint& pt = xyAtT(&span);
3505 SkDebugf("%s id=%d", fun, fID);
3506 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3507 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3508 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3509 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003510 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3511 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3512 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
3513 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00003514 if (span.fWindSum == SK_MinS32) {
3515 SkDebugf("?");
3516 } else {
3517 SkDebugf("%d", span.fWindSum);
3518 }
3519 SkDebugf(" windValue=%d\n", span.fWindValue);
3520 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003521
3522 void debugShowNewWinding(const char* fun, const Span& span, int winding, int oppWinding) {
3523 const SkPoint& pt = xyAtT(&span);
3524 SkDebugf("%s id=%d", fun, fID);
3525 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3526 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3527 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3528 }
3529 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3530 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3531 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d newOppSum=%d oppSum=",
3532 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
3533 winding, oppWinding);
3534 if (span.fOppSum == SK_MinS32) {
3535 SkDebugf("?");
3536 } else {
3537 SkDebugf("%d", span.fOppSum);
3538 }
3539 SkDebugf(" windSum=");
3540 if (span.fWindSum == SK_MinS32) {
3541 SkDebugf("?");
3542 } else {
3543 SkDebugf("%d", span.fWindSum);
3544 }
3545 SkDebugf(" windValue=%d\n", span.fWindValue);
3546 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003547#endif
3548
caryclark@google.com47580692012-07-23 12:14:49 +00003549#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003550 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com31143cf2012-11-09 22:14:19 +00003551 const int contourWinding, const int oppContourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003552 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003553 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003554 int lastSum = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003555 int oppLastSum = oppContourWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003556 const Angle* firstAngle = angles[first];
3557 int windSum = lastSum - spanSign(firstAngle);
3558 int oppoSign = oppSign(firstAngle);
3559 int oppWindSum = oppLastSum - oppoSign;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003560 SkDebugf("%s %s contourWinding=%d oppContourWinding=%d sign=%d\n", fun, __FUNCTION__,
3561 contourWinding, oppContourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003562 int index = first;
3563 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003564 do {
3565 const Angle& angle = *angles[index];
3566 const Segment& segment = *angle.segment();
3567 int start = angle.start();
3568 int end = angle.end();
3569 const Span& sSpan = segment.fTs[start];
3570 const Span& eSpan = segment.fTs[end];
3571 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com31143cf2012-11-09 22:14:19 +00003572 bool opp = segment.fOperand ^ fOperand;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003573 if (!firstTime) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003574 oppoSign = segment.oppSign(&angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003575 if (opp) {
3576 oppLastSum = oppWindSum;
3577 oppWindSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003578 if (oppoSign) {
3579 lastSum = windSum;
3580 windSum -= oppoSign;
3581 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003582 } else {
3583 lastSum = windSum;
3584 windSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003585 if (oppoSign) {
3586 oppLastSum = oppWindSum;
3587 oppWindSum -= oppoSign;
3588 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003589 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00003590 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003591 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 +00003592 " sign=%d windValue=%d windSum=",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003593 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003594 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003595 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
3596 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
3597 mSpan.fWindValue);
3598 if (mSpan.fWindSum == SK_MinS32) {
3599 SkDebugf("?");
3600 } else {
3601 SkDebugf("%d", mSpan.fWindSum);
3602 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003603 int last, wind;
3604 if (opp) {
3605 last = oppLastSum;
3606 wind = oppWindSum;
3607 } else {
3608 last = lastSum;
3609 wind = windSum;
3610 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003611 if (!oppoSign) {
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003612 SkDebugf(" %d->%d (max=%d)", last, wind,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003613 useInnerWinding(last, wind) ? wind : last);
3614 } else {
3615 SkDebugf(" %d->%d (%d->%d)", last, wind, opp ? lastSum : oppLastSum,
3616 opp ? windSum : oppWindSum);
3617 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003618 SkDebugf(" done=%d tiny=%d opp=%d\n", mSpan.fDone, mSpan.fTiny, opp);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003619#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00003620 angle.debugShow(segment.xyAtT(&sSpan));
3621#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003622 ++index;
3623 if (index == angles.count()) {
3624 index = 0;
3625 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003626 if (firstTime) {
3627 firstTime = false;
3628 }
caryclark@google.com47580692012-07-23 12:14:49 +00003629 } while (index != first);
3630 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003631
3632 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first) {
3633 const Angle* firstAngle = angles[first];
3634 const Segment* segment = firstAngle->segment();
3635 int winding = segment->updateWinding(firstAngle);
3636 int oppWinding = segment->updateOppWinding(firstAngle);
3637 debugShowSort(fun, angles, first, winding, oppWinding);
3638 }
3639
caryclark@google.com47580692012-07-23 12:14:49 +00003640#endif
3641
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003642#if DEBUG_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003643 static char as_digit(int value) {
3644 return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
3645 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00003646#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003647
caryclark@google.com729e1c42012-11-21 21:36:34 +00003648#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003649 int debugShowWindingValues(int slotCount, int ofInterest) const {
3650 if (!(1 << fID & ofInterest)) {
3651 return 0;
3652 }
3653 int sum = 0;
3654 SkTDArray<char> slots;
3655 slots.setCount(slotCount * 2);
3656 memset(slots.begin(), ' ', slotCount * 2);
3657 for (int i = 0; i < fTs.count(); ++i) {
3658 // if (!(1 << fTs[i].fOther->fID & ofInterest)) {
3659 // continue;
3660 // }
3661 sum += fTs[i].fWindValue;
3662 slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
3663 sum += fTs[i].fOppValue;
3664 slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
3665 }
3666 SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
3667 slots.begin() + slotCount);
3668 return sum;
3669 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00003670#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003671
caryclark@google.com729e1c42012-11-21 21:36:34 +00003672#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003673 bool debugVerifyWinding(int start, int end, int winding) const {
3674 const Span& span = fTs[SkMin32(start, end)];
3675 int spanWinding = span.fWindSum;
3676 if (spanWinding == SK_MinS32) {
3677 return true;
3678 }
3679 int spanSign = SkSign32(start - end);
3680 int signedVal = spanSign * span.fWindValue;
3681 if (signedVal < 0) {
3682 spanWinding -= signedVal;
3683 }
3684 return span.fWindSum == winding;
3685 }
3686#endif
3687
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003688private:
3689 const SkPoint* fPts;
3690 SkPath::Verb fVerb;
3691 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003692 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00003693 int fDoneSpans; // quick check that segment is finished
caryclark@google.com235f56a2012-09-14 14:19:30 +00003694 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003695#if DEBUG_DUMP
3696 int fID;
3697#endif
3698};
3699
caryclark@google.comb9738012012-07-03 19:53:30 +00003700class Contour;
3701
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003702struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00003703 Contour* fContours[2];
3704 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003705 double fTs[2][2];
3706};
3707
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003708class Contour {
3709public:
3710 Contour() {
3711 reset();
3712#if DEBUG_DUMP
3713 fID = ++gContourID;
3714#endif
3715 }
3716
3717 bool operator<(const Contour& rh) const {
3718 return fBounds.fTop == rh.fBounds.fTop
3719 ? fBounds.fLeft < rh.fBounds.fLeft
3720 : fBounds.fTop < rh.fBounds.fTop;
3721 }
3722
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003723 void addCoincident(int index, Contour* other, int otherIndex,
3724 const Intersections& ts, bool swap) {
3725 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003726 coincidence.fContours[0] = this; // FIXME: no need to store
caryclark@google.comb9738012012-07-03 19:53:30 +00003727 coincidence.fContours[1] = other;
3728 coincidence.fSegments[0] = index;
3729 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00003730 if (fSegments[index].verb() == SkPath::kLine_Verb &&
3731 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
3732 // FIXME: coincident lines use legacy Ts instead of coincident Ts
3733 coincidence.fTs[swap][0] = ts.fT[0][0];
3734 coincidence.fTs[swap][1] = ts.fT[0][1];
3735 coincidence.fTs[!swap][0] = ts.fT[1][0];
3736 coincidence.fTs[!swap][1] = ts.fT[1][1];
3737 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
3738 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
3739 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
3740 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
3741 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
3742 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
3743 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003744 }
3745
3746 void addCross(const Contour* crosser) {
3747#ifdef DEBUG_CROSS
3748 for (int index = 0; index < fCrosses.count(); ++index) {
3749 SkASSERT(fCrosses[index] != crosser);
3750 }
3751#endif
3752 *fCrosses.append() = crosser;
3753 }
3754
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003755 void addCubic(const SkPoint pts[4]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003756 fSegments.push_back().addCubic(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003757 fContainsCurves = true;
3758 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003759
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003760 int addLine(const SkPoint pts[2]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003761 fSegments.push_back().addLine(pts, fOperand);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003762 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003763 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003764
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003765 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3766 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3767 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003768
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003769 int addQuad(const SkPoint pts[3]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003770 fSegments.push_back().addQuad(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003771 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003772 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003773 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003774
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003775 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3776 containsIntercepts();
3777 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3778 }
3779
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003780 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003781 return fBounds;
3782 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003783
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003784 void complete() {
3785 setBounds();
3786 fContainsIntercepts = false;
3787 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003788
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003789 void containsIntercepts() {
3790 fContainsIntercepts = true;
3791 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003792
rmistry@google.comd6176b02012-08-23 18:14:13 +00003793 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003794 int &tIndex, double& hitT) {
3795 int segmentCount = fSegments.count();
3796 const Segment* bestSegment = NULL;
3797 for (int test = 0; test < segmentCount; ++test) {
3798 Segment* testSegment = &fSegments[test];
3799 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003800 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003801 continue;
3802 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003803 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003804 continue;
3805 }
3806 if (bounds.fLeft > basePt.fX) {
3807 continue;
3808 }
3809 if (bounds.fRight < basePt.fX) {
3810 continue;
3811 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003812 if (bounds.fLeft == bounds.fRight) {
3813 continue;
3814 }
3815 #if 0
3816 bool leftHalf = bounds.fLeft == basePt.fX;
3817 bool rightHalf = bounds.fRight == basePt.fX;
3818 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3819 basePt, leftHalf, rightHalf)) {
3820 continue;
3821 }
3822 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003823 double testHitT;
3824 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3825 if (testT >= 0) {
3826 bestSegment = testSegment;
3827 tIndex = testT;
3828 hitT = testHitT;
3829 }
3830 }
3831 return bestSegment;
3832 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003833
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003834 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003835 for (int index = 0; index < fCrosses.count(); ++index) {
3836 if (fCrosses[index] == crosser) {
3837 return true;
3838 }
3839 }
3840 return false;
3841 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003842
caryclark@google.comf839c032012-10-26 21:03:50 +00003843 const SkPoint& end() const {
3844 const Segment& segment = fSegments.back();
3845 return segment.pts()[segment.verb()];
3846 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003847
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003848 void findTooCloseToCall(bool otherXor) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003849 int segmentCount = fSegments.count();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003850 otherXor ^= fXor;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003851 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003852 fSegments[sIndex].findTooCloseToCall(fXor, otherXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003853 }
3854 }
3855
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003856 void fixOtherTIndex() {
3857 int segmentCount = fSegments.count();
3858 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3859 fSegments[sIndex].fixOtherTIndex();
3860 }
3861 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003862
caryclark@google.com31143cf2012-11-09 22:14:19 +00003863 bool operand() const {
3864 return fOperand;
3865 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003866
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003867 void reset() {
3868 fSegments.reset();
3869 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003870 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003871 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003872
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003873 void resolveCoincidence(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003874 int count = fCoincidences.count();
3875 for (int index = 0; index < count; ++index) {
3876 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00003877 Contour* thisContour = coincidence.fContours[0];
caryclark@google.com729e1c42012-11-21 21:36:34 +00003878 SkASSERT(thisContour == this);
caryclark@google.comb9738012012-07-03 19:53:30 +00003879 Contour* otherContour = coincidence.fContours[1];
3880 int thisIndex = coincidence.fSegments[0];
3881 int otherIndex = coincidence.fSegments[1];
3882 Segment& thisOne = thisContour->fSegments[thisIndex];
3883 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00003884 #if DEBUG_CONCIDENT
3885 thisOne.debugShowTs();
3886 other.debugShowTs();
3887 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003888 double startT = coincidence.fTs[0][0];
3889 double endT = coincidence.fTs[0][1];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003890 bool cancelers = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003891 if (startT > endT) {
3892 SkTSwap<double>(startT, endT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003893 cancelers ^= true; // FIXME: just assign true
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003894 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003895 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003896 double oStartT = coincidence.fTs[1][0];
3897 double oEndT = coincidence.fTs[1][1];
3898 if (oStartT > oEndT) {
3899 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003900 cancelers ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003901 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003902 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003903 bool opp = thisContour->fOperand ^ otherContour->fOperand;
3904 if (cancelers && !opp) {
3905 // make sure startT and endT have t entries
caryclark@google.com2ddff932012-08-07 21:25:27 +00003906 if (startT > 0 || oEndT < 1
3907 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3908 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003909 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003910 if (oStartT > 0 || endT < 1
3911 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3912 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003913 }
3914 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003915 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003916 if (startT > 0 || oStartT > 0
3917 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003918 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003919 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003920 if (endT < 1 || oEndT < 1
3921 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003922 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003923 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003924 thisOne.addTCoincident(thisContour->fXor, otherContour->fXor,
3925 startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003926 }
caryclark@google.com47580692012-07-23 12:14:49 +00003927 #if DEBUG_CONCIDENT
3928 thisOne.debugShowTs();
3929 other.debugShowTs();
3930 #endif
caryclark@google.com729e1c42012-11-21 21:36:34 +00003931 #if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003932 debugShowWindingValues(contourList);
3933 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003934 }
3935 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003936
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003937 const SkTArray<Segment>& segments() {
3938 return fSegments;
3939 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003940
caryclark@google.com235f56a2012-09-14 14:19:30 +00003941 void setOperand(bool isOp) {
3942 fOperand = isOp;
3943 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003944
caryclark@google.com235f56a2012-09-14 14:19:30 +00003945 void setXor(bool isXor) {
3946 fXor = isXor;
3947 }
3948
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003949 void sortSegments() {
3950 int segmentCount = fSegments.count();
3951 fSortedSegments.setReserve(segmentCount);
3952 for (int test = 0; test < segmentCount; ++test) {
3953 *fSortedSegments.append() = &fSegments[test];
3954 }
3955 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
3956 fFirstSorted = 0;
3957 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003958
caryclark@google.comf839c032012-10-26 21:03:50 +00003959 const SkPoint& start() const {
3960 return fSegments.front().pts()[0];
3961 }
3962
3963 void toPath(PathWrapper& path) const {
3964 int segmentCount = fSegments.count();
3965 const SkPoint& pt = fSegments.front().pts()[0];
3966 path.deferredMove(pt);
3967 for (int test = 0; test < segmentCount; ++test) {
3968 fSegments[test].addCurveTo(0, 1, path, true);
3969 }
3970 path.close();
3971 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003972
caryclark@google.comf839c032012-10-26 21:03:50 +00003973 void toPartialBackward(PathWrapper& path) const {
3974 int segmentCount = fSegments.count();
3975 for (int test = segmentCount - 1; test >= 0; --test) {
3976 fSegments[test].addCurveTo(1, 0, path, true);
3977 }
3978 }
3979
3980 void toPartialForward(PathWrapper& path) const {
3981 int segmentCount = fSegments.count();
3982 for (int test = 0; test < segmentCount; ++test) {
3983 fSegments[test].addCurveTo(0, 1, path, true);
3984 }
3985 }
3986
3987#if 0 // FIXME: obsolete, remove
caryclark@google.com15fa1382012-05-07 20:49:36 +00003988 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3989 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003990 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003991 // segments' top, and not a true sort, so it could be ameniable to regular
3992 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003993 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003994 int segmentCount = fSegments.count();
3995 SkASSERT(segmentCount > 0);
3996 int best = -1;
3997 Segment* bestSegment = NULL;
3998 while (++best < segmentCount) {
3999 Segment* testSegment = &fSegments[best];
4000 if (testSegment->done()) {
4001 continue;
4002 }
4003 bestSegment = testSegment;
4004 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004005 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004006 if (!bestSegment) {
4007 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004008 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004009 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00004010 for (int test = best + 1; test < segmentCount; ++test) {
4011 Segment* testSegment = &fSegments[test];
4012 if (testSegment->done()) {
4013 continue;
4014 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004015 if (testSegment->bounds().fTop > bestTop) {
4016 continue;
4017 }
4018 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00004019 if (bestTop > testTop) {
4020 bestTop = testTop;
4021 bestSegment = testSegment;
4022 }
4023 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004024 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004025 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004026 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004027#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004028
caryclark@google.comf839c032012-10-26 21:03:50 +00004029 Segment* topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004030 int segmentCount = fSortedSegments.count();
4031 SkASSERT(segmentCount > 0);
4032 Segment* bestSegment = NULL;
caryclark@google.comf839c032012-10-26 21:03:50 +00004033 int sortedIndex = fFirstSorted;
4034 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
4035 Segment* testSegment = fSortedSegments[sortedIndex];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004036 if (testSegment->done()) {
caryclark@google.comf839c032012-10-26 21:03:50 +00004037 if (sortedIndex == fFirstSorted) {
4038 ++fFirstSorted;
4039 }
4040 continue;
4041 }
4042 SkPoint testXY;
4043 testSegment->activeLeftTop(testXY);
4044 if (testXY.fY < topLeft.fY) {
4045 continue;
4046 }
4047 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
4048 continue;
4049 }
4050 if (bestXY.fY < testXY.fY) {
4051 continue;
4052 }
4053 if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004054 continue;
4055 }
4056 bestSegment = testSegment;
caryclark@google.comf839c032012-10-26 21:03:50 +00004057 bestXY = testXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004058 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004059 return bestSegment;
4060 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004061
caryclark@google.com24bec792012-08-20 12:43:57 +00004062 Segment* undoneSegment(int& start, int& end) {
4063 int segmentCount = fSegments.count();
4064 for (int test = 0; test < segmentCount; ++test) {
4065 Segment* testSegment = &fSegments[test];
4066 if (testSegment->done()) {
4067 continue;
4068 }
4069 testSegment->undoneSpan(start, end);
4070 return testSegment;
4071 }
4072 return NULL;
4073 }
4074
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004075 int updateSegment(int index, const SkPoint* pts) {
4076 Segment& segment = fSegments[index];
4077 segment.updatePts(pts);
4078 return segment.verb() + 1;
4079 }
4080
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004081#if DEBUG_TEST
4082 SkTArray<Segment>& debugSegments() {
4083 return fSegments;
4084 }
4085#endif
4086
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004087#if DEBUG_DUMP
4088 void dump() {
4089 int i;
4090 const char className[] = "Contour";
4091 const int tab = 4;
4092 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
4093 for (i = 0; i < fSegments.count(); ++i) {
4094 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
4095 className, i);
4096 fSegments[i].dump();
4097 }
4098 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
4099 tab + sizeof(className), className,
4100 fBounds.fLeft, fBounds.fTop,
4101 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004102 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
4103 className, fContainsIntercepts);
4104 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
4105 className, fContainsCurves);
4106 }
4107#endif
4108
caryclark@google.com027de222012-07-12 12:52:50 +00004109#if DEBUG_ACTIVE_SPANS
4110 void debugShowActiveSpans() {
4111 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004112 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00004113 }
4114 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004115
4116 void validateActiveSpans() {
4117 for (int index = 0; index < fSegments.count(); ++index) {
4118 fSegments[index].validateActiveSpans();
4119 }
4120 }
caryclark@google.com027de222012-07-12 12:52:50 +00004121#endif
4122
caryclark@google.com729e1c42012-11-21 21:36:34 +00004123#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004124 int debugShowWindingValues(int totalSegments, int ofInterest) {
4125 int count = fSegments.count();
4126 int sum = 0;
4127 for (int index = 0; index < count; ++index) {
4128 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
4129 }
4130 // SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
4131 return sum;
4132 }
4133
4134 static void debugShowWindingValues(SkTDArray<Contour*>& contourList) {
4135 // int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
4136 // int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
4137 int ofInterest = 1 << 5 | 1 << 8;
4138 int total = 0;
4139 int index;
4140 for (index = 0; index < contourList.count(); ++index) {
4141 total += contourList[index]->segments().count();
4142 }
4143 int sum = 0;
4144 for (index = 0; index < contourList.count(); ++index) {
4145 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
4146 }
4147 // SkDebugf("%s total=%d\n", __FUNCTION__, sum);
4148 }
4149#endif
4150
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004151protected:
4152 void setBounds() {
4153 int count = fSegments.count();
4154 if (count == 0) {
4155 SkDebugf("%s empty contour\n", __FUNCTION__);
4156 SkASSERT(0);
4157 // FIXME: delete empty contour?
4158 return;
4159 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004160 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004161 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004162 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004163 }
4164 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004165
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004166private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004167 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004168 SkTDArray<Segment*> fSortedSegments;
4169 int fFirstSorted;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004170 SkTDArray<Coincidence> fCoincidences;
4171 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004172 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004173 bool fContainsIntercepts;
4174 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004175 bool fOperand; // true for the second argument to a binary operator
4176 bool fXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004177#if DEBUG_DUMP
4178 int fID;
4179#endif
4180};
4181
4182class EdgeBuilder {
4183public:
4184
caryclark@google.comf839c032012-10-26 21:03:50 +00004185EdgeBuilder(const PathWrapper& path, SkTArray<Contour>& contours)
4186 : fPath(path.nativePath())
4187 , fContours(contours)
4188{
4189 init();
4190}
4191
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004192EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00004193 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004194 , fContours(contours)
4195{
caryclark@google.comf839c032012-10-26 21:03:50 +00004196 init();
4197}
4198
4199void init() {
4200 fCurrentContour = NULL;
4201 fOperand = false;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004202 fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004203#if DEBUG_DUMP
4204 gContourID = 0;
4205 gSegmentID = 0;
4206#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00004207 fSecondHalf = preFetch();
4208}
4209
4210void addOperand(const SkPath& path) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004211 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
4212 fPathVerbs.pop();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004213 fPath = &path;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004214 fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004215 preFetch();
4216}
4217
4218void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004219 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004220 complete();
4221 if (fCurrentContour && !fCurrentContour->segments().count()) {
4222 fContours.pop_back();
4223 }
4224 // correct pointers in contours since fReducePts may have moved as it grew
4225 int cIndex = 0;
4226 int extraCount = fExtra.count();
4227 SkASSERT(extraCount == 0 || fExtra[0] == -1);
4228 int eIndex = 0;
4229 int rIndex = 0;
4230 while (++eIndex < extraCount) {
4231 int offset = fExtra[eIndex];
4232 if (offset < 0) {
4233 ++cIndex;
4234 continue;
4235 }
4236 fCurrentContour = &fContours[cIndex];
4237 rIndex += fCurrentContour->updateSegment(offset - 1,
4238 &fReducePts[rIndex]);
4239 }
4240 fExtra.reset(); // we're done with this
4241}
4242
4243ShapeOpMask xorMask() const {
caryclark@google.com729e1c42012-11-21 21:36:34 +00004244 return fXorMask[fOperand];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004245}
4246
4247protected:
4248
4249void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004250 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004251 fCurrentContour->complete();
4252 fCurrentContour = NULL;
4253 }
4254}
4255
caryclark@google.com235f56a2012-09-14 14:19:30 +00004256// FIXME:remove once we can access path pts directly
4257int preFetch() {
4258 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004259 SkPoint pts[4];
4260 SkPath::Verb verb;
4261 do {
4262 verb = iter.next(pts);
4263 *fPathVerbs.append() = verb;
4264 if (verb == SkPath::kMove_Verb) {
4265 *fPathPts.append() = pts[0];
4266 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
4267 fPathPts.append(verb, &pts[1]);
4268 }
4269 } while (verb != SkPath::kDone_Verb);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004270 return fPathVerbs.count() - 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004271}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004272
caryclark@google.com235f56a2012-09-14 14:19:30 +00004273void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004274 SkPath::Verb reducedVerb;
4275 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004276 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004277 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004278 const SkPoint* finalCurveStart = NULL;
4279 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004280 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004281 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
4282 switch (verb) {
4283 case SkPath::kMove_Verb:
4284 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004285 if (!fCurrentContour) {
4286 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004287 fCurrentContour->setOperand(fOperand);
caryclark@google.com729e1c42012-11-21 21:36:34 +00004288 fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004289 *fExtra.append() = -1; // start new contour
4290 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004291 finalCurveEnd = pointsPtr++;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004292 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004293 case SkPath::kLine_Verb:
4294 // skip degenerate points
4295 if (pointsPtr[-1].fX != pointsPtr[0].fX
4296 || pointsPtr[-1].fY != pointsPtr[0].fY) {
4297 fCurrentContour->addLine(&pointsPtr[-1]);
4298 }
4299 break;
4300 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00004301
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004302 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
4303 if (reducedVerb == 0) {
4304 break; // skip degenerate points
4305 }
4306 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00004307 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004308 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004309 break;
4310 }
4311 fCurrentContour->addQuad(&pointsPtr[-1]);
4312 break;
4313 case SkPath::kCubic_Verb:
4314 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
4315 if (reducedVerb == 0) {
4316 break; // skip degenerate points
4317 }
4318 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004319 *fExtra.append() =
4320 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004321 break;
4322 }
4323 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004324 *fExtra.append() =
4325 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004326 break;
4327 }
4328 fCurrentContour->addCubic(&pointsPtr[-1]);
4329 break;
4330 case SkPath::kClose_Verb:
4331 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004332 if (finalCurveStart && finalCurveEnd
4333 && *finalCurveStart != *finalCurveEnd) {
4334 *fReducePts.append() = *finalCurveStart;
4335 *fReducePts.append() = *finalCurveEnd;
4336 *fExtra.append() =
4337 fCurrentContour->addLine(fReducePts.end() - 2);
4338 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004339 complete();
caryclark@google.com31143cf2012-11-09 22:14:19 +00004340 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004341 default:
4342 SkDEBUGFAIL("bad verb");
4343 return;
4344 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004345 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004346 pointsPtr += verb;
4347 SkASSERT(fCurrentContour);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004348 nextVerb:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004349 if (verbPtr == endOfFirstHalf) {
4350 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004351 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004352 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004353}
4354
4355private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004356 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004357 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004358 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004359 Contour* fCurrentContour;
4360 SkTArray<Contour>& fContours;
4361 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004362 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com729e1c42012-11-21 21:36:34 +00004363 ShapeOpMask fXorMask[2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004364 int fSecondHalf;
4365 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004366};
4367
4368class Work {
4369public:
4370 enum SegmentType {
4371 kHorizontalLine_Segment = -1,
4372 kVerticalLine_Segment = 0,
4373 kLine_Segment = SkPath::kLine_Verb,
4374 kQuad_Segment = SkPath::kQuad_Verb,
4375 kCubic_Segment = SkPath::kCubic_Verb,
4376 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00004377
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004378 void addCoincident(Work& other, const Intersections& ts, bool swap) {
4379 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
4380 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004381
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004382 // FIXME: does it make sense to write otherIndex now if we're going to
4383 // fix it up later?
4384 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004385 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004386 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004387
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004388 // Avoid collapsing t values that are close to the same since
4389 // we walk ts to describe consecutive intersections. Since a pair of ts can
4390 // be nearly equal, any problems caused by this should be taken care
4391 // of later.
4392 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004393 int addT(double newT, const Work& other) {
4394 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004395 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004396
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004397 bool advance() {
4398 return ++fIndex < fLast;
4399 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004400
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004401 SkScalar bottom() const {
4402 return bounds().fBottom;
4403 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004404
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004405 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004406 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004407 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004408
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004409 const SkPoint* cubic() const {
4410 return fCubic;
4411 }
4412
4413 void init(Contour* contour) {
4414 fContour = contour;
4415 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004416 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004417 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004418
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004419 bool isAdjacent(const Work& next) {
4420 return fContour == next.fContour && fIndex + 1 == next.fIndex;
4421 }
4422
4423 bool isFirstLast(const Work& next) {
4424 return fContour == next.fContour && fIndex == 0
4425 && next.fIndex == fLast - 1;
4426 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004427
4428 SkScalar left() const {
4429 return bounds().fLeft;
4430 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004431
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004432 void promoteToCubic() {
4433 fCubic[0] = pts()[0];
4434 fCubic[2] = pts()[1];
4435 fCubic[3] = pts()[2];
4436 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
4437 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
4438 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
4439 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
4440 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004441
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004442 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004443 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004444 }
4445
4446 SkScalar right() const {
4447 return bounds().fRight;
4448 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004449
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004450 ptrdiff_t segmentIndex() const {
4451 return fIndex;
4452 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004453
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004454 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004455 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004456 SegmentType type = (SegmentType) segment.verb();
4457 if (type != kLine_Segment) {
4458 return type;
4459 }
4460 if (segment.isHorizontal()) {
4461 return kHorizontalLine_Segment;
4462 }
4463 if (segment.isVertical()) {
4464 return kVerticalLine_Segment;
4465 }
4466 return kLine_Segment;
4467 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004468
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004469 bool startAfter(const Work& after) {
4470 fIndex = after.fIndex;
4471 return advance();
4472 }
4473
4474 SkScalar top() const {
4475 return bounds().fTop;
4476 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004477
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004478 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004479 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004480 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004481
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004482 SkScalar x() const {
4483 return bounds().fLeft;
4484 }
4485
4486 bool xFlipped() const {
4487 return x() != pts()[0].fX;
4488 }
4489
4490 SkScalar y() const {
4491 return bounds().fTop;
4492 }
4493
4494 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004495 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004496 }
4497
4498protected:
4499 Contour* fContour;
4500 SkPoint fCubic[4];
4501 int fIndex;
4502 int fLast;
4503};
4504
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004505#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004506static void debugShowLineIntersection(int pts, const Work& wt,
4507 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004508 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004509 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004510 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
4511 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4512 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
4513 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004514 return;
4515 }
4516 SkPoint wtOutPt, wnOutPt;
4517 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4518 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004519 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 +00004520 __FUNCTION__,
4521 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4522 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
4523 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004524 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004525 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00004526 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004527 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4528 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4529 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004530 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
4531 }
4532 SkDebugf("\n");
4533}
4534
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004535static void debugShowQuadLineIntersection(int pts, const Work& wt,
4536 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4537 if (!pts) {
4538 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
caryclark@google.com0b7da432012-10-31 19:00:20 +00004539 " (%1.9g,%1.9g %1.9g,%1.9g)\n",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004540 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4541 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004542 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004543 return;
4544 }
4545 SkPoint wtOutPt, wnOutPt;
4546 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4547 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4548 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4549 __FUNCTION__,
4550 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4551 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4552 wtOutPt.fX, wtOutPt.fY);
4553 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004554 QuadXYAtT(wt.pts(), wtTs[1], &wtOutPt);
4555 SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004556 }
4557 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4558 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4559 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4560 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004561 LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
4562 SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004563 }
4564 SkDebugf("\n");
4565}
4566
caryclark@google.coma461ff02012-10-11 12:54:23 +00004567static void debugShowQuadIntersection(int pts, const Work& wt,
4568 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4569 if (!pts) {
4570 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
4571 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
4572 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00004573 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00004574 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004575 wn.pts()[2].fX, wn.pts()[2].fY );
caryclark@google.coma461ff02012-10-11 12:54:23 +00004576 return;
4577 }
4578 SkPoint wtOutPt, wnOutPt;
4579 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4580 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4581 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4582 __FUNCTION__,
4583 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4584 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4585 wtOutPt.fX, wtOutPt.fY);
4586 if (pts == 2) {
4587 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
4588 }
4589 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4590 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4591 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
4592 wnOutPt.fX, wnOutPt.fY);
4593 if (pts == 2) {
4594 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004595 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004596 SkDebugf("\n");
4597}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004598#else
4599static void debugShowLineIntersection(int , const Work& ,
4600 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004601}
caryclark@google.coma461ff02012-10-11 12:54:23 +00004602
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004603static void debugShowQuadLineIntersection(int , const Work& ,
4604 const Work& , const double [2], const double [2]) {
4605}
4606
caryclark@google.coma461ff02012-10-11 12:54:23 +00004607static void debugShowQuadIntersection(int , const Work& ,
4608 const Work& , const double [2], const double [2]) {
4609}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004610#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004611
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004612static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004613
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004614 if (test != next) {
4615 if (test->bounds().fBottom < next->bounds().fTop) {
4616 return false;
4617 }
4618 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
4619 return true;
4620 }
4621 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004622 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004623 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004624 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004625 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004626 Work wn;
4627 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004628 if (test == next && !wn.startAfter(wt)) {
4629 continue;
4630 }
4631 do {
4632 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
4633 continue;
4634 }
4635 int pts;
4636 Intersections ts;
4637 bool swap = false;
4638 switch (wt.segmentType()) {
4639 case Work::kHorizontalLine_Segment:
4640 swap = true;
4641 switch (wn.segmentType()) {
4642 case Work::kHorizontalLine_Segment:
4643 case Work::kVerticalLine_Segment:
4644 case Work::kLine_Segment: {
4645 pts = HLineIntersect(wn.pts(), wt.left(),
4646 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004647 debugShowLineIntersection(pts, wt, wn,
4648 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004649 break;
4650 }
4651 case Work::kQuad_Segment: {
4652 pts = HQuadIntersect(wn.pts(), wt.left(),
4653 wt.right(), wt.y(), wt.xFlipped(), ts);
4654 break;
4655 }
4656 case Work::kCubic_Segment: {
4657 pts = HCubicIntersect(wn.pts(), wt.left(),
4658 wt.right(), wt.y(), wt.xFlipped(), ts);
4659 break;
4660 }
4661 default:
4662 SkASSERT(0);
4663 }
4664 break;
4665 case Work::kVerticalLine_Segment:
4666 swap = true;
4667 switch (wn.segmentType()) {
4668 case Work::kHorizontalLine_Segment:
4669 case Work::kVerticalLine_Segment:
4670 case Work::kLine_Segment: {
4671 pts = VLineIntersect(wn.pts(), wt.top(),
4672 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004673 debugShowLineIntersection(pts, wt, wn,
4674 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004675 break;
4676 }
4677 case Work::kQuad_Segment: {
4678 pts = VQuadIntersect(wn.pts(), wt.top(),
4679 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4680 break;
4681 }
4682 case Work::kCubic_Segment: {
4683 pts = VCubicIntersect(wn.pts(), wt.top(),
4684 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4685 break;
4686 }
4687 default:
4688 SkASSERT(0);
4689 }
4690 break;
4691 case Work::kLine_Segment:
4692 switch (wn.segmentType()) {
4693 case Work::kHorizontalLine_Segment:
4694 pts = HLineIntersect(wt.pts(), wn.left(),
4695 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004696 debugShowLineIntersection(pts, wt, wn,
4697 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004698 break;
4699 case Work::kVerticalLine_Segment:
4700 pts = VLineIntersect(wt.pts(), wn.top(),
4701 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004702 debugShowLineIntersection(pts, wt, wn,
4703 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004704 break;
4705 case Work::kLine_Segment: {
4706 pts = LineIntersect(wt.pts(), wn.pts(), ts);
4707 debugShowLineIntersection(pts, wt, wn,
4708 ts.fT[1], ts.fT[0]);
4709 break;
4710 }
4711 case Work::kQuad_Segment: {
4712 swap = true;
4713 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.com0b7da432012-10-31 19:00:20 +00004714 debugShowQuadLineIntersection(pts, wn, wt,
4715 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004716 break;
4717 }
4718 case Work::kCubic_Segment: {
4719 swap = true;
4720 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
4721 break;
4722 }
4723 default:
4724 SkASSERT(0);
4725 }
4726 break;
4727 case Work::kQuad_Segment:
4728 switch (wn.segmentType()) {
4729 case Work::kHorizontalLine_Segment:
4730 pts = HQuadIntersect(wt.pts(), wn.left(),
4731 wn.right(), wn.y(), wn.xFlipped(), ts);
4732 break;
4733 case Work::kVerticalLine_Segment:
4734 pts = VQuadIntersect(wt.pts(), wn.top(),
4735 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4736 break;
4737 case Work::kLine_Segment: {
4738 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004739 debugShowQuadLineIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004740 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004741 break;
4742 }
4743 case Work::kQuad_Segment: {
4744 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004745 debugShowQuadIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004746 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004747 break;
4748 }
4749 case Work::kCubic_Segment: {
4750 wt.promoteToCubic();
4751 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
4752 break;
4753 }
4754 default:
4755 SkASSERT(0);
4756 }
4757 break;
4758 case Work::kCubic_Segment:
4759 switch (wn.segmentType()) {
4760 case Work::kHorizontalLine_Segment:
4761 pts = HCubicIntersect(wt.pts(), wn.left(),
4762 wn.right(), wn.y(), wn.xFlipped(), ts);
4763 break;
4764 case Work::kVerticalLine_Segment:
4765 pts = VCubicIntersect(wt.pts(), wn.top(),
4766 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4767 break;
4768 case Work::kLine_Segment: {
4769 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
4770 break;
4771 }
4772 case Work::kQuad_Segment: {
4773 wn.promoteToCubic();
4774 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
4775 break;
4776 }
4777 case Work::kCubic_Segment: {
4778 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
4779 break;
4780 }
4781 default:
4782 SkASSERT(0);
4783 }
4784 break;
4785 default:
4786 SkASSERT(0);
4787 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004788 if (!foundCommonContour && pts > 0) {
4789 test->addCross(next);
4790 next->addCross(test);
4791 foundCommonContour = true;
4792 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004793 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00004794 if (pts == 2) {
4795 if (wn.segmentType() <= Work::kLine_Segment
4796 && wt.segmentType() <= Work::kLine_Segment) {
4797 wt.addCoincident(wn, ts, swap);
4798 continue;
4799 }
4800 if (wn.segmentType() == Work::kQuad_Segment
4801 && wt.segmentType() == Work::kQuad_Segment
4802 && ts.coincidentUsed() == 2) {
4803 wt.addCoincident(wn, ts, swap);
4804 continue;
4805 }
4806
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00004807 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004808 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004809 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
4810 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004811 int testTAt = wt.addT(ts.fT[swap][pt], wn);
4812 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004813 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
4814 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004815 }
4816 } while (wn.advance());
4817 } while (wt.advance());
4818 return true;
4819}
4820
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004821// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004822// see if coincidence is formed by clipping non-concident segments
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004823static void coincidenceCheck(SkTDArray<Contour*>& contourList, bool otherXor, int total) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004824 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00004825 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004826 Contour* contour = contourList[cIndex];
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004827 contour->resolveCoincidence(contourList);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004828 }
4829 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4830 Contour* contour = contourList[cIndex];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004831 contour->findTooCloseToCall(otherXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004832 }
4833}
4834
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004835// project a ray from the top of the contour up and see if it hits anything
4836// note: when we compute line intersections, we keep track of whether
4837// two contours touch, so we need only look at contours not touching this one.
4838// OPTIMIZATION: sort contourList vertically to avoid linear walk
4839static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com31143cf2012-11-09 22:14:19 +00004840 const Segment* current, int index, int endIndex, bool opp) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004841 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004842 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004843 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00004844 const Segment* test = NULL;
4845 int tIndex;
4846 double tHit;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004847 for (int cTest = 0; cTest < contourCount; ++cTest) {
4848 Contour* contour = contourList[cTest];
caryclark@google.com7fce0de2012-11-29 14:31:50 +00004849 if ((contour->operand() ^ current->operand()) != opp) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004850 continue;
4851 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004852 if (basePt.fY < contour->bounds().fTop) {
4853 continue;
4854 }
4855 if (bestY > contour->bounds().fBottom) {
4856 continue;
4857 }
caryclark@google.com47580692012-07-23 12:14:49 +00004858 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
4859 if (next) {
4860 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004861 }
caryclark@google.com47580692012-07-23 12:14:49 +00004862 }
4863 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00004864 return 0;
4865 }
4866 int winding, windValue;
4867 // If the ray hit the end of a span, we need to construct the wheel of
4868 // angles to find the span closest to the ray -- even if there are just
4869 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004870 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004871 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00004872 SkTDArray<Angle> angles;
4873 int end = test->nextSpan(tIndex, 1);
4874 if (end < 0) {
4875 end = test->nextSpan(tIndex, -1);
4876 }
4877 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00004878 SkASSERT(angles.count() > 0);
4879 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
4880#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00004881 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00004882#endif
4883 return 0;
4884 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004885 test->buildAngles(tIndex, angles, false);
caryclark@google.com47580692012-07-23 12:14:49 +00004886 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00004887 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00004888 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004889 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00004890 // hour after 6 o'clock
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004891 (void) Segment::SortAngles(angles, sorted);
caryclark@google.com47580692012-07-23 12:14:49 +00004892#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00004893 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00004894#endif
4895 // walk the sorted angle fan to find the lowest angle
4896 // above the base point. Currently, the first angle in the sorted array
4897 // is 12 noon or an earlier hour (the next counterclockwise)
4898 int count = sorted.count();
4899 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004900 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00004901 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004902 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00004903 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00004904 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004905 if (angle->unsortable()) {
4906 continue;
4907 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004908 if (baseMatches && angle->isHorizontal()) {
4909 continue;
4910 }
4911 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00004912 test = angle->segment();
4913 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
4914 const SkPoint* pts = test->pts();
4915 indexDx = pts[2].fX - pts[1].fX - indexDx;
4916 }
caryclark@google.com47580692012-07-23 12:14:49 +00004917 if (indexDx < 0) {
4918 left = index;
4919 } else if (indexDx > 0) {
4920 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00004921 int previous = index - 1;
4922 if (previous < 0) {
4923 previous = count - 1;
4924 }
4925 const Angle* prev = sorted[previous];
4926 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
4927#if DEBUG_SORT
4928 SkDebugf("%s use prev\n", __FUNCTION__);
4929#endif
4930 right = previous;
4931 }
caryclark@google.com47580692012-07-23 12:14:49 +00004932 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004933 } else {
4934 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004935 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004936 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004937 if (left < 0 && right < 0) {
4938 left = mid;
4939 }
caryclark@google.com47580692012-07-23 12:14:49 +00004940 SkASSERT(left >= 0 || right >= 0);
4941 if (left < 0) {
4942 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004943 } else if (left >= 0 && mid >= 0 && right >= 0
4944 && sorted[mid]->sign() == sorted[right]->sign()) {
4945 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004946 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004947 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00004948 test = angle->segment();
4949 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00004950 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004951 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00004952#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004953 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
4954 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00004955#endif
4956 } else {
4957 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00004958 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004959 windValue = test->windValue(tIndex);
4960#if DEBUG_WINDING
4961 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4962 windValue);
4963#endif
4964 }
4965 // see if a + change in T results in a +/- change in X (compute x'(T))
4966 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00004967 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
4968 const SkPoint* pts = test->pts();
4969 dx = pts[2].fX - pts[1].fX - dx;
4970 }
caryclark@google.com47580692012-07-23 12:14:49 +00004971#if DEBUG_WINDING
4972 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
4973#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00004974 SkASSERT(dx != 0);
4975 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00004976 winding += dx > 0 ? -windValue : windValue;
4977#if DEBUG_WINDING
4978 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
4979#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004980 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004981 return winding;
4982}
rmistry@google.comd6176b02012-08-23 18:14:13 +00004983
caryclark@google.com24bec792012-08-20 12:43:57 +00004984static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4985 int contourCount = contourList.count();
4986 Segment* result;
4987 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4988 Contour* contour = contourList[cIndex];
4989 result = contour->undoneSegment(start, end);
4990 if (result) {
4991 return result;
4992 }
4993 }
4994 return NULL;
4995}
4996
4997
4998
caryclark@google.com31143cf2012-11-09 22:14:19 +00004999static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005000 while (chase.count()) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005001 Span* span;
5002 chase.pop(&span);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005003 const Span& backPtr = span->fOther->span(span->fOtherIndex);
5004 Segment* segment = backPtr.fOther;
5005 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005006 SkTDArray<Angle> angles;
5007 int done = 0;
5008 if (segment->activeAngle(tIndex, done, angles)) {
5009 Angle* last = angles.end() - 1;
5010 tIndex = last->start();
5011 endIndex = last->end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005012 #if TRY_ROTATE
5013 *chase.insert(0) = span;
5014 #else
5015 *chase.append() = span;
5016 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005017 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005018 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00005019 if (done == angles.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00005020 continue;
5021 }
5022 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005023 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00005024#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005025 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00005026#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005027 if (!sortable) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005028 continue;
5029 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00005030 // find first angle, initialize winding to computed fWindSum
5031 int firstIndex = -1;
5032 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005033 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005034 do {
5035 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005036 segment = angle->segment();
5037 winding = segment->windSum(angle);
5038 } while (winding == SK_MinS32);
5039 int spanWinding = segment->spanSign(angle->start(), angle->end());
5040 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00005041 SkDebugf("%s winding=%d spanWinding=%d\n",
5042 __FUNCTION__, winding, spanWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00005043 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005044 // turn span winding into contour winding
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005045 if (spanWinding * winding < 0) {
5046 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005047 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005048 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005049 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005050 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005051 // we care about first sign and whether wind sum indicates this
5052 // edge is inside or outside. Maybe need to pass span winding
5053 // or first winding or something into this function?
5054 // advance to first undone angle, then return it and winding
5055 // (to set whether edges are active or not)
5056 int nextIndex = firstIndex + 1;
5057 int angleCount = sorted.count();
5058 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005059 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00005060 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00005061 do {
5062 SkASSERT(nextIndex != firstIndex);
5063 if (nextIndex == angleCount) {
5064 nextIndex = 0;
5065 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005066 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00005067 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005068 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00005069 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005070 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00005071 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
5072 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005073 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005074 tIndex = angle->start();
5075 endIndex = angle->end();
5076 int lesser = SkMin32(tIndex, endIndex);
5077 const Span& nextSpan = segment->span(lesser);
5078 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005079#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00005080 // FIXME: this be wrong. assign startWinding if edge is in
5081 // same direction. If the direction is opposite, winding to
5082 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00005083 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005084 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005085 }
caryclark@google.com59823f72012-08-09 18:17:47 +00005086 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005087#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005088 break;
5089 }
5090 } while (++nextIndex != lastIndex);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005091 #if TRY_ROTATE
5092 *chase.insert(0) = span;
5093 #else
5094 *chase.append() = span;
5095 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005096 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005097 }
5098 return NULL;
5099}
5100
caryclark@google.com027de222012-07-12 12:52:50 +00005101#if DEBUG_ACTIVE_SPANS
5102static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005103 int index;
5104 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00005105 contourList[index]->debugShowActiveSpans();
5106 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005107 for (index = 0; index < contourList.count(); ++ index) {
5108 contourList[index]->validateActiveSpans();
5109 }
caryclark@google.com027de222012-07-12 12:52:50 +00005110}
5111#endif
5112
caryclark@google.com27c449a2012-07-27 18:26:38 +00005113static bool windingIsActive(int winding, int spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005114 // FIXME: !spanWinding test must be superflorous, true?
caryclark@google.com27c449a2012-07-27 18:26:38 +00005115 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
5116 && (!winding || !spanWinding || winding == -spanWinding);
5117}
5118
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005119static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.comf839c032012-10-26 21:03:50 +00005120 int& endIndex, SkPoint& topLeft) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005121 Segment* result;
5122 do {
caryclark@google.comf839c032012-10-26 21:03:50 +00005123 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005124 int contourCount = contourList.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00005125 Segment* topStart = NULL;
5126 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5127 Contour* contour = contourList[cIndex];
5128 const Bounds& bounds = contour->bounds();
5129 if (bounds.fBottom < topLeft.fY) {
5130 continue;
5131 }
5132 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
5133 continue;
5134 }
5135 Segment* test = contour->topSortableSegment(topLeft, bestXY);
5136 if (test) {
5137 topStart = test;
5138 }
5139 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005140 if (!topStart) {
5141 return NULL;
5142 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005143 topLeft = bestXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005144 result = topStart->findTop(index, endIndex);
5145 } while (!result);
5146 return result;
5147}
caryclark@google.com31143cf2012-11-09 22:14:19 +00005148
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005149static int updateWindings(const Segment* current, int index, int endIndex, int& spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005150 int lesser = SkMin32(index, endIndex);
5151 spanWinding = current->spanSign(index, endIndex);
5152 int winding = current->windSum(lesser);
5153 bool inner = useInnerWinding(winding - spanWinding, winding);
5154#if DEBUG_WINDING
5155 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
5156 " inner=%d result=%d\n",
5157 __FUNCTION__, current->debugID(), current->t(lesser),
5158 spanWinding, winding, SkSign32(index - endIndex),
5159 useInnerWinding(winding - spanWinding, winding),
5160 inner ? winding - spanWinding : winding);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005161#endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005162 if (inner) {
5163 winding -= spanWinding;
5164 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005165 return winding;
5166}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005167
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005168// Each segment may have an inside or an outside. Segments contained within
5169// winding may have insides on either side, and form a contour that should be
5170// ignored. Segments that are coincident with opposing direction segments may
5171// have outsides on either side, and should also disappear.
5172// 'Normal' segments will have one inside and one outside. Subsequent connections
5173// when winding should follow the intersection direction. If more than one edge
5174// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005175 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00005176 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005177// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005178static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005179 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005180 bool unsortable = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005181 bool closable = true;
5182 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com15fa1382012-05-07 20:49:36 +00005183 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005184 int index, endIndex;
caryclark@google.com31143cf2012-11-09 22:14:19 +00005185 // iterates while top is unsortable
caryclark@google.comf839c032012-10-26 21:03:50 +00005186 Segment* current = findSortableTop(contourList, index, endIndex, topLeft);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005187 if (!current) {
5188 break;
5189 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005190 int contourWinding;
5191 if (firstContour) {
5192 contourWinding = 0;
5193 firstContour = false;
5194 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00005195 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005196 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00005197 if (sumWinding == SK_MinS32) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00005198 sumWinding = current->computeSum(index, endIndex, false);
caryclark@google.com200c2112012-08-03 15:05:04 +00005199 }
5200 if (sumWinding == SK_MinS32) {
5201 contourWinding = innerContourCheck(contourList, current,
caryclark@google.com31143cf2012-11-09 22:14:19 +00005202 index, endIndex, false);
caryclark@google.com200c2112012-08-03 15:05:04 +00005203 } else {
5204 contourWinding = sumWinding;
5205 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00005206 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
5207 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00005208 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005209 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00005210#if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00005211 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n",
5212 __FUNCTION__, sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00005213 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00005214#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005215 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005216#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005217 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005218 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
5219#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005220 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005221 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005222 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005223 // FIXME: needs work. While it works in limited situations, it does
5224 // not always compute winding correctly. Active should be removed and instead
5225 // the initial winding should be correctly passed in so that if the
5226 // inner contour is wound the same way, it never finds an accumulated
5227 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00005228 // other than zero when resolving sorted angles.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005229 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005230 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005231 bool active = windingIsActive(winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005232 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00005233 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
5234 __FUNCTION__, active ? "true" : "false",
5235 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005236 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005237 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005238 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005239 int nextStart = index;
5240 int nextEnd = endIndex;
5241 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005242 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005243 if (!next) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005244 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00005245 && current->verb() != SkPath::kLine_Verb
5246 && !simple.isClosed()) {
5247 current->addCurveTo(index, endIndex, simple, true);
5248 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005249 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005250 break;
5251 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005252 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005253 current = next;
5254 index = nextStart;
5255 endIndex = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005256 } while (!simple.isClosed()
5257 && ((active && !unsortable) || !current->done()));
5258 if (active) {
5259 if (!simple.isClosed()) {
5260 SkASSERT(unsortable);
5261 int min = SkMin32(index, endIndex);
5262 if (!current->done(min)) {
5263 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.com31143cf2012-11-09 22:14:19 +00005264 current->markDone(SkMin32(index, endIndex),
5265 winding ? winding : spanWinding);
caryclark@google.comf839c032012-10-26 21:03:50 +00005266 }
5267 closable = false;
5268 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005269 simple.close();
5270 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005271 current = findChase(chaseArray, index, endIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005272 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00005273 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005274 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005275 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005276 break;
5277 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005278 winding = updateWindings(current, index, endIndex, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005279 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005280 } while (true);
caryclark@google.comf839c032012-10-26 21:03:50 +00005281 return closable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005282}
5283
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005284// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005285static bool bridgeXor(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005286 Segment* current;
5287 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005288 bool unsortable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00005289 while ((current = findUndone(contourList, start, end))) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005290 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005291 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005292 int nextStart = start;
5293 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005294 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00005295 if (!next) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005296 if (simple.hasMove()
5297 && current->verb() != SkPath::kLine_Verb
5298 && !simple.isClosed()) {
5299 current->addCurveTo(start, end, simple, true);
5300 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005301 }
caryclark@google.com24bec792012-08-20 12:43:57 +00005302 break;
5303 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005304 current->addCurveTo(start, end, simple, true);
caryclark@google.com24bec792012-08-20 12:43:57 +00005305 current = next;
5306 start = nextStart;
5307 end = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005308 } while (!simple.isClosed());
5309 // FIXME: add unsortable test
5310 if (simple.hasMove()) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005311 simple.close();
5312 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005313 #if DEBUG_ACTIVE_SPANS
5314 debugShowActiveSpans(contourList);
5315 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00005316 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005317 return !unsortable;
caryclark@google.com24bec792012-08-20 12:43:57 +00005318}
5319
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005320static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
5321 int contourCount = contourList.count();
5322 for (int cTest = 0; cTest < contourCount; ++cTest) {
5323 Contour* contour = contourList[cTest];
5324 contour->fixOtherTIndex();
5325 }
5326}
5327
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005328static void sortSegments(SkTDArray<Contour*>& contourList) {
5329 int contourCount = contourList.count();
5330 for (int cTest = 0; cTest < contourCount; ++cTest) {
5331 Contour* contour = contourList[cTest];
5332 contour->sortSegments();
5333 }
5334}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005335
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005336static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005337 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005338 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005339 if (count == 0) {
5340 return;
5341 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005342 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005343 *list.append() = &contours[index];
5344 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005345 QSort<Contour>(list.begin(), list.end() - 1);
5346}
5347
caryclark@google.comf839c032012-10-26 21:03:50 +00005348static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005349 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005350}
5351
caryclark@google.comf839c032012-10-26 21:03:50 +00005352 /*
5353 check start and end of each contour
5354 if not the same, record them
5355 match them up
5356 connect closest
5357 reassemble contour pieces into new path
5358 */
5359static void assemble(const PathWrapper& path, PathWrapper& simple) {
5360#if DEBUG_PATH_CONSTRUCTION
5361 SkDebugf("%s\n", __FUNCTION__);
5362#endif
5363 SkTArray<Contour> contours;
5364 EdgeBuilder builder(path, contours);
5365 builder.finish();
5366 int count = contours.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005367 int outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005368 SkTDArray<int> runs; // indices of partial contours
caryclark@google.com0b7da432012-10-31 19:00:20 +00005369 for (outer = 0; outer < count; ++outer) {
5370 const Contour& eContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005371 const SkPoint& eStart = eContour.start();
5372 const SkPoint& eEnd = eContour.end();
5373 if (approximatelyEqual(eStart, eEnd)) {
5374 eContour.toPath(simple);
5375 continue;
5376 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005377 *runs.append() = outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005378 }
5379 count = runs.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005380 if (count == 0) {
5381 return;
5382 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005383 SkTDArray<int> sLink, eLink;
5384 sLink.setCount(count);
5385 eLink.setCount(count);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005386 SkTDArray<double> sBest, eBest;
5387 sBest.setCount(count);
5388 eBest.setCount(count);
caryclark@google.comf839c032012-10-26 21:03:50 +00005389 int rIndex;
5390 for (rIndex = 0; rIndex < count; ++rIndex) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005391 outer = runs[rIndex];
5392 const Contour& oContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005393 const SkPoint& oStart = oContour.start();
5394 const SkPoint& oEnd = oContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005395 double dx = oEnd.fX - oStart.fX;
5396 double dy = oEnd.fY - oStart.fY;
5397 double dist = dx * dx + dy * dy;
5398 sBest[rIndex] = eBest[rIndex] = dist;
5399 sLink[rIndex] = eLink[rIndex] = rIndex;
5400 }
5401 for (rIndex = 0; rIndex < count - 1; ++rIndex) {
5402 outer = runs[rIndex];
5403 const Contour& oContour = contours[outer];
5404 const SkPoint& oStart = oContour.start();
5405 const SkPoint& oEnd = oContour.end();
5406 double bestStartDist = sBest[rIndex];
5407 double bestEndDist = eBest[rIndex];
5408 for (int iIndex = rIndex + 1; iIndex < count; ++iIndex) {
5409 int inner = runs[iIndex];
5410 const Contour& iContour = contours[inner];
caryclark@google.comf839c032012-10-26 21:03:50 +00005411 const SkPoint& iStart = iContour.start();
5412 const SkPoint& iEnd = iContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005413 double dx = iStart.fX - oStart.fX;
5414 double dy = iStart.fY - oStart.fY;
5415 double dist = dx * dx + dy * dy;
5416 if (bestStartDist > dist) {
5417 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005418 sLink[rIndex] = ~iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005419 sLink[iIndex] = ~rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005420 }
5421 dx = iEnd.fX - oStart.fX;
5422 dy = iEnd.fY - oStart.fY;
5423 dist = dx * dx + dy * dy;
5424 if (bestStartDist > dist) {
5425 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005426 sLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005427 eLink[iIndex] = rIndex;
5428 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005429 dx = iStart.fX - oEnd.fX;
5430 dy = iStart.fY - oEnd.fY;
5431 dist = dx * dx + dy * dy;
5432 if (bestEndDist > dist) {
5433 bestEndDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005434 sLink[iIndex] = rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005435 eLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005436 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005437 dx = iEnd.fX - oEnd.fX;
5438 dy = iEnd.fY - oEnd.fY;
5439 dist = dx * dx + dy * dy;
5440 if (bestEndDist > dist) {
5441 bestEndDist = dist;
5442 eLink[iIndex] = ~rIndex;
5443 eLink[rIndex] = ~iIndex;
5444 }
5445 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005446 }
5447 rIndex = 0;
5448 bool forward = true;
5449 bool first = true;
5450 const SkPoint* startPtr;
5451 int sIndex = sLink[rIndex];
caryclark@google.com0b7da432012-10-31 19:00:20 +00005452 SkASSERT(sIndex != INT_MAX);
5453 sLink[rIndex] = INT_MAX;
5454 int eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005455 if (sIndex < 0) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005456 eIndex = sLink[~sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005457 sLink[~sIndex] = INT_MAX;
5458 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005459 eIndex = eLink[sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005460 eLink[sIndex] = INT_MAX;
5461 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005462 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005463 do {
5464 do {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005465 outer = runs[rIndex];
5466 const Contour& contour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005467 if (first) {
5468 startPtr = &contour.start();
5469 first = false;
5470 simple.deferredMove(startPtr[0]);
5471 }
5472 const SkPoint* endPtr;
5473 if (forward) {
5474 contour.toPartialForward(simple);
5475 endPtr = &contour.end();
5476 } else {
5477 contour.toPartialBackward(simple);
5478 endPtr = &contour.start();
5479 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005480 if (sIndex == eIndex) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005481 simple.close();
5482 first = forward = true;
5483 break;
5484 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005485 if (forward) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005486 eIndex = eLink[rIndex];
5487 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005488 eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005489 if (eIndex >= 0) {
5490 SkASSERT(sLink[eIndex] == rIndex);
5491 sLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005492 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005493 SkASSERT(eLink[~eIndex] == ~rIndex);
5494 eLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005495 }
5496 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005497 eIndex = sLink[rIndex];
5498 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005499 sLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005500 if (eIndex >= 0) {
5501 SkASSERT(eLink[eIndex] == rIndex);
5502 eLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005503 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005504 SkASSERT(sLink[~eIndex] == ~rIndex);
5505 sLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005506 }
5507 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005508 rIndex = eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005509 if (rIndex < 0) {
5510 forward ^= 1;
5511 rIndex = ~rIndex;
5512 }
5513 } while (true);
5514 for (rIndex = 0; rIndex < count; ++rIndex) {
5515 if (sLink[rIndex] != INT_MAX) {
5516 break;
5517 }
5518 }
5519 } while (rIndex < count);
5520 SkASSERT(first);
5521}
5522
5523void simplifyx(const SkPath& path, SkPath& result) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005524 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comf839c032012-10-26 21:03:50 +00005525 result.reset();
5526 result.setFillType(SkPath::kEvenOdd_FillType);
5527 PathWrapper simple(result);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005528
5529 // turn path into list of segments
5530 SkTArray<Contour> contours;
5531 // FIXME: add self-intersecting cubics' T values to segment
5532 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00005533 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005534 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005535 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005536 Contour** currentPtr = contourList.begin();
5537 if (!currentPtr) {
5538 return;
5539 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005540 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005541 // find all intersections between segments
5542 do {
5543 Contour** nextPtr = currentPtr;
5544 Contour* current = *currentPtr++;
5545 Contour* next;
5546 do {
5547 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005548 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005549 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005550 // eat through coincident edges
caryclark@google.com7ba591e2012-11-20 14:21:54 +00005551 coincidenceCheck(contourList, false, 0);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00005552 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005553 sortSegments(contourList);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005554#if DEBUG_ACTIVE_SPANS
5555 debugShowActiveSpans(contourList);
5556#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005557 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005558 if (builder.xorMask() == kWinding_Mask
5559 ? !bridgeWinding(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00005560 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005561 { // if some edges could not be resolved, assemble remaining fragments
caryclark@google.comf839c032012-10-26 21:03:50 +00005562 SkPath temp;
5563 temp.setFillType(SkPath::kEvenOdd_FillType);
5564 PathWrapper assembled(temp);
5565 assemble(simple, assembled);
5566 result = *assembled.nativePath();
caryclark@google.com24bec792012-08-20 12:43:57 +00005567 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005568}
5569