blob: 09ce42060e10ab812878e4ba3886e6ee61d72d58 [file] [log] [blame]
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.comb45a1b42012-05-18 20:50:33 +00007#include "Simplify.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00008
9#undef SkASSERT
10#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
11
caryclark@google.com15fa1382012-05-07 20:49:36 +000012// Terminology:
13// A Path contains one of more Contours
14// A Contour is made up of Segment array
caryclark@google.comb45a1b42012-05-18 20:50:33 +000015// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
16// A Verb is one of Line, Quad(ratic), or Cubic
caryclark@google.com15fa1382012-05-07 20:49:36 +000017// A Segment contains a Span array
18// A Span is describes a portion of a Segment using starting and ending T
19// T values range from 0 to 1, where 0 is the first Point in the Segment
caryclark@google.com47580692012-07-23 12:14:49 +000020// An Edge is a Segment generated from a Span
caryclark@google.com15fa1382012-05-07 20:49:36 +000021
caryclark@google.comfa0588f2012-04-26 21:01:06 +000022// FIXME: remove once debugging is complete
caryclark@google.com47580692012-07-23 12:14:49 +000023#ifdef SK_DEBUG
24int gDebugMaxWindSum = SK_MaxS32;
25int gDebugMaxWindValue = SK_MaxS32;
26#endif
caryclark@google.comd0deb4f2012-12-17 13:58:08 +000027bool gUseOldBridgeWinding = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000028
caryclark@google.comf839c032012-10-26 21:03:50 +000029#define PIN_ADD_T 0
caryclark@google.com0b7da432012-10-31 19:00:20 +000030#define TRY_ROTATE 1
caryclark@google.coma461ff02012-10-11 12:54:23 +000031
caryclark@google.com47580692012-07-23 12:14:49 +000032#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.come7bd5f42012-12-13 19:47:53 +000033#define FORCE_RELEASE 0 // set force release to 1 for multiple thread -- no debugging
caryclark@google.comfa0588f2012-04-26 21:01:06 +000034
caryclark@google.com31143cf2012-11-09 22:14:19 +000035#if FORCE_RELEASE || defined SK_RELEASE
caryclark@google.com47580692012-07-23 12:14:49 +000036
37const bool gRunTestsInOneThread = false;
38
39#define DEBUG_ACTIVE_SPANS 0
caryclark@google.com4eeda372012-12-06 21:47:48 +000040#define DEBUG_ACTIVE_SPANS_SHORT_FORM 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000041#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000042#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000043#define DEBUG_ANGLE 0
caryclark@google.come7bd5f42012-12-13 19:47:53 +000044#define DEBUG_ASSEMBLE 0
caryclark@google.com47580692012-07-23 12:14:49 +000045#define DEBUG_CONCIDENT 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000046#define DEBUG_CROSS 0
caryclark@google.come7bd5f42012-12-13 19:47:53 +000047#define DEBUG_FLOW 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000048#define DEBUG_MARK_DONE 0
caryclark@google.comf839c032012-10-26 21:03:50 +000049#define DEBUG_PATH_CONSTRUCTION 0
caryclark@google.com729e1c42012-11-21 21:36:34 +000050#define DEBUG_SHOW_WINDING 0
caryclark@google.com47580692012-07-23 12:14:49 +000051#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000052#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000053#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000054
55#else
56
caryclark@google.com47580692012-07-23 12:14:49 +000057const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000058
caryclark@google.comc91dfe42012-10-16 12:06:27 +000059#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com4eeda372012-12-06 21:47:48 +000060#define DEBUG_ACTIVE_SPANS_SHORT_FORM 1
caryclark@google.com6aea33f2012-10-09 14:11:58 +000061#define DEBUG_ADD_INTERSECTING_TS 1
62#define DEBUG_ADD_T_PAIR 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000063#define DEBUG_ANGLE 1
caryclark@google.come7bd5f42012-12-13 19:47:53 +000064#define DEBUG_ASSEMBLE 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000065#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000066#define DEBUG_CROSS 0
caryclark@google.come7bd5f42012-12-13 19:47:53 +000067#define DEBUG_FLOW 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000068#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000069#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com729e1c42012-11-21 21:36:34 +000070#define DEBUG_SHOW_WINDING 0
caryclark@google.com47580692012-07-23 12:14:49 +000071#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000072#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000073#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000074
75#endif
76
caryclark@google.com6aea33f2012-10-09 14:11:58 +000077#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
caryclark@google.com027de222012-07-12 12:52:50 +000078
caryclark@google.comfa0588f2012-04-26 21:01:06 +000079#if DEBUG_DUMP
80static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000081// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000082static int gContourID;
83static int gSegmentID;
84#endif
85
caryclark@google.com8dcf1142012-07-02 20:27:02 +000086#ifndef DEBUG_TEST
87#define DEBUG_TEST 0
88#endif
89
caryclark@google.com32546db2012-08-31 20:55:07 +000090#define MAKE_CONST_LINE(line, pts) \
91 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
92#define MAKE_CONST_QUAD(quad, pts) \
93 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
94 {pts[2].fX, pts[2].fY}}
95#define MAKE_CONST_CUBIC(cubic, pts) \
96 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
97 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
98
caryclark@google.comfa0588f2012-04-26 21:01:06 +000099static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
100 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000101 MAKE_CONST_LINE(aLine, a);
102 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000103 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
104}
105
106static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
107 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000108 MAKE_CONST_QUAD(aQuad, a);
109 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000110 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000111}
112
caryclark@google.com32546db2012-08-31 20:55:07 +0000113static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000114 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000115 MAKE_CONST_CUBIC(aCubic, a);
116 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000117 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
118}
119
120static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
121 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000122 MAKE_CONST_QUAD(aQuad, a);
123 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000124#define TRY_QUARTIC_SOLUTION 1
125#if TRY_QUARTIC_SOLUTION
126 intersect2(aQuad, bQuad, intersections);
127#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000128 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000129#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000130 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000131}
132
133static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
134 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000135 MAKE_CONST_CUBIC(aCubic, a);
136 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000137 intersect(aCubic, bCubic, intersections);
138 return intersections.fUsed;
139}
140
141static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
142 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000143 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000144 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
145}
146
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000147static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
148 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000149 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000150 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
151}
152
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000153static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
154 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000155 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000156 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
157}
158
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000159static int (* const HSegmentIntersect[])(const SkPoint [], SkScalar ,
160 SkScalar , SkScalar , bool , Intersections& ) = {
161 NULL,
162 HLineIntersect,
163 HQuadIntersect,
164 HCubicIntersect
165};
166
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000167static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
168 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000169 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000170 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
171}
172
173static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
174 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000175 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000176 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
177}
178
179static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
180 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000181 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000182 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000183}
184
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000185static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
186 SkScalar , SkScalar , bool , Intersections& ) = {
187 NULL,
188 VLineIntersect,
189 VQuadIntersect,
190 VCubicIntersect
191};
192
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000193static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000194 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000195 double x, y;
196 xy_at_t(line, t, x, y);
197 out->fX = SkDoubleToScalar(x);
198 out->fY = SkDoubleToScalar(y);
199}
200
201static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000202 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000203 double x, y;
204 xy_at_t(quad, t, x, y);
205 out->fX = SkDoubleToScalar(x);
206 out->fY = SkDoubleToScalar(y);
207}
208
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000209static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
210 MAKE_CONST_QUAD(quad, a);
211 xy_at_t(quad, t, out->x, out->y);
212}
213
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000214static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000215 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000216 double x, y;
217 xy_at_t(cubic, t, x, y);
218 out->fX = SkDoubleToScalar(x);
219 out->fY = SkDoubleToScalar(y);
220}
221
222static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
223 NULL,
224 LineXYAtT,
225 QuadXYAtT,
226 CubicXYAtT
227};
228
229static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000230 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000231 double x;
232 xy_at_t(aLine, t, x, *(double*) 0);
233 return SkDoubleToScalar(x);
234}
235
236static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000237 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000238 double x;
239 xy_at_t(quad, t, x, *(double*) 0);
240 return SkDoubleToScalar(x);
241}
242
243static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000244 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000245 double x;
246 xy_at_t(cubic, t, x, *(double*) 0);
247 return SkDoubleToScalar(x);
248}
249
250static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
251 NULL,
252 LineXAtT,
253 QuadXAtT,
254 CubicXAtT
255};
256
257static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000258 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000259 double y;
260 xy_at_t(aLine, t, *(double*) 0, y);
261 return SkDoubleToScalar(y);
262}
263
264static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000265 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000266 double y;
267 xy_at_t(quad, t, *(double*) 0, y);
268 return SkDoubleToScalar(y);
269}
270
271static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000272 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000273 double y;
274 xy_at_t(cubic, t, *(double*) 0, y);
275 return SkDoubleToScalar(y);
276}
277
278static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
279 NULL,
280 LineYAtT,
281 QuadYAtT,
282 CubicYAtT
283};
284
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000285static SkScalar LineDXAtT(const SkPoint a[2], double ) {
286 return a[1].fX - a[0].fX;
287}
288
289static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000290 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000291 double x;
292 dxdy_at_t(quad, t, x, *(double*) 0);
293 return SkDoubleToScalar(x);
294}
295
296static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000297 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000298 double x;
299 dxdy_at_t(cubic, t, x, *(double*) 0);
300 return SkDoubleToScalar(x);
301}
302
303static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
304 NULL,
305 LineDXAtT,
306 QuadDXAtT,
307 CubicDXAtT
308};
309
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000310static SkScalar LineDYAtT(const SkPoint a[2], double ) {
311 return a[1].fY - a[0].fY;
312}
313
314static SkScalar QuadDYAtT(const SkPoint a[3], double t) {
315 MAKE_CONST_QUAD(quad, a);
316 double y;
317 dxdy_at_t(quad, t, *(double*) 0, y);
318 return SkDoubleToScalar(y);
319}
320
321static SkScalar CubicDYAtT(const SkPoint a[4], double t) {
322 MAKE_CONST_CUBIC(cubic, a);
323 double y;
324 dxdy_at_t(cubic, t, *(double*) 0, y);
325 return SkDoubleToScalar(y);
326}
327
328static SkScalar (* const SegmentDYAtT[])(const SkPoint [], double ) = {
329 NULL,
330 LineDYAtT,
331 QuadDYAtT,
332 CubicDYAtT
333};
334
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000335static void LineSubDivide(const SkPoint a[2], double startT, double endT,
336 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000337 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000338 _Line dst;
339 sub_divide(aLine, startT, endT, dst);
340 sub[0].fX = SkDoubleToScalar(dst[0].x);
341 sub[0].fY = SkDoubleToScalar(dst[0].y);
342 sub[1].fX = SkDoubleToScalar(dst[1].x);
343 sub[1].fY = SkDoubleToScalar(dst[1].y);
344}
345
346static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
347 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000348 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000349 Quadratic dst;
350 sub_divide(aQuad, startT, endT, dst);
351 sub[0].fX = SkDoubleToScalar(dst[0].x);
352 sub[0].fY = SkDoubleToScalar(dst[0].y);
353 sub[1].fX = SkDoubleToScalar(dst[1].x);
354 sub[1].fY = SkDoubleToScalar(dst[1].y);
355 sub[2].fX = SkDoubleToScalar(dst[2].x);
356 sub[2].fY = SkDoubleToScalar(dst[2].y);
357}
358
359static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
360 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000361 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000362 Cubic dst;
363 sub_divide(aCubic, startT, endT, dst);
364 sub[0].fX = SkDoubleToScalar(dst[0].x);
365 sub[0].fY = SkDoubleToScalar(dst[0].y);
366 sub[1].fX = SkDoubleToScalar(dst[1].x);
367 sub[1].fY = SkDoubleToScalar(dst[1].y);
368 sub[2].fX = SkDoubleToScalar(dst[2].x);
369 sub[2].fY = SkDoubleToScalar(dst[2].y);
370 sub[3].fX = SkDoubleToScalar(dst[3].x);
371 sub[3].fY = SkDoubleToScalar(dst[3].y);
372}
373
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000374static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
375 SkPoint []) = {
376 NULL,
377 LineSubDivide,
378 QuadSubDivide,
379 CubicSubDivide
380};
381
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000382static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000383 _Line sub) {
384 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000385 _Line dst;
386 sub_divide(aLine, startT, endT, dst);
387 sub[0] = dst[0];
388 sub[1] = dst[1];
389}
390
391static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000392 Quadratic sub) {
393 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000394 Quadratic dst;
395 sub_divide(aQuad, startT, endT, dst);
396 sub[0] = dst[0];
397 sub[1] = dst[1];
398 sub[2] = dst[2];
399}
400
401static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000402 Cubic sub) {
403 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000404 Cubic dst;
405 sub_divide(aCubic, startT, endT, dst);
406 sub[0] = dst[0];
407 sub[1] = dst[1];
408 sub[2] = dst[2];
409 sub[3] = dst[3];
410}
411
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000412#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000413static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
414 SkRect& bounds) {
415 SkPoint dst[3];
416 QuadSubDivide(a, startT, endT, dst);
417 bounds.fLeft = bounds.fRight = dst[0].fX;
418 bounds.fTop = bounds.fBottom = dst[0].fY;
419 for (int index = 1; index < 3; ++index) {
420 bounds.growToInclude(dst[index].fX, dst[index].fY);
421 }
422}
423
424static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
425 SkRect& bounds) {
426 SkPoint dst[4];
427 CubicSubDivide(a, startT, endT, dst);
428 bounds.fLeft = bounds.fRight = dst[0].fX;
429 bounds.fTop = bounds.fBottom = dst[0].fY;
430 for (int index = 1; index < 4; ++index) {
431 bounds.growToInclude(dst[index].fX, dst[index].fY);
432 }
433}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000434#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000435
caryclark@google.com15fa1382012-05-07 20:49:36 +0000436static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000437 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000438 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000439 Quadratic dst;
440 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000441 if (order == 2) { // quad became line
442 for (int index = 0; index < order; ++index) {
443 SkPoint* pt = reducePts.append();
444 pt->fX = SkDoubleToScalar(dst[index].x);
445 pt->fY = SkDoubleToScalar(dst[index].y);
446 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000447 }
448 return (SkPath::Verb) (order - 1);
449}
450
451static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
452 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000453 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000454 Cubic dst;
455 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000456 if (order == 2 || order == 3) { // cubic became line or quad
457 for (int index = 0; index < order; ++index) {
458 SkPoint* pt = reducePts.append();
459 pt->fX = SkDoubleToScalar(dst[index].x);
460 pt->fY = SkDoubleToScalar(dst[index].y);
461 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000462 }
463 return (SkPath::Verb) (order - 1);
464}
465
caryclark@google.com15fa1382012-05-07 20:49:36 +0000466static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000467 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000468 return isLinear(aQuad, 0, 2);
469}
470
471static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000472 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000473 return isLinear(aCubic, 0, 3);
474}
475
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000476static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000477 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000478 double x[2];
479 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000480 xy_at_t(aLine, endT, x[1], *(double*) 0);
481 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000482}
483
484static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000485 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000486 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000487}
488
489static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000490 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000491 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000492}
493
494static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
495 NULL,
496 LineLeftMost,
497 QuadLeftMost,
498 CubicLeftMost
499};
500
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000501#if 0 // currently unused
caryclark@google.com235f56a2012-09-14 14:19:30 +0000502static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
503 Intersections& intersections) {
504 MAKE_CONST_QUAD(aQuad, a);
505 MAKE_CONST_LINE(bLine, b);
506 return intersectRay(aQuad, bLine, intersections);
507}
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000508#endif
509
510static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
511 Intersections& intersections) {
512 MAKE_CONST_QUAD(aQuad, a);
513 return intersectRay(aQuad, bLine, intersections);
514}
caryclark@google.com235f56a2012-09-14 14:19:30 +0000515
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000516class Segment;
517
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000518struct Span {
519 Segment* fOther;
520 mutable SkPoint fPt; // lazily computed as needed
521 double fT;
522 double fOtherT; // value at fOther[fOtherIndex].fT
523 int fOtherIndex; // can't be used during intersection
caryclark@google.com31143cf2012-11-09 22:14:19 +0000524 int fWindSum; // accumulated from contours surrounding this one.
525 int fOppSum; // for binary operators: the opposite winding sum
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000526 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000527 int fOppValue; // normally 0 -- when binary coincident edges combine, opp value goes here
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000528 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000529 bool fUnsortableStart; // set when start is part of an unsortable pair
530 bool fUnsortableEnd; // set when end is part of an unsortable pair
caryclark@google.comf839c032012-10-26 21:03:50 +0000531 bool fTiny; // if set, span may still be considered once for edge following
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000532};
533
caryclark@google.com15fa1382012-05-07 20:49:36 +0000534// sorting angles
535// given angles of {dx dy ddx ddy dddx dddy} sort them
536class Angle {
537public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000538 // FIXME: this is bogus for quads and cubics
539 // if the quads and cubics' line from end pt to ctrl pt are coincident,
540 // there's no obvious way to determine the curve ordering from the
541 // derivatives alone. In particular, if one quadratic's coincident tangent
542 // is longer than the other curve, the final control point can place the
543 // longer curve on either side of the shorter one.
544 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
545 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000546
caryclark@google.com32546db2012-08-31 20:55:07 +0000547 /*(
548 for quads and cubics, set up a parameterized line (e.g. LineParameters )
549 for points [0] to [1]. See if point [2] is on that line, or on one side
550 or the other. If it both quads' end points are on the same side, choose
551 the shorter tangent. If the tangents are equal, choose the better second
552 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000553
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000554 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000555 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000556 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000557 double y = dy();
558 double ry = rh.dy();
559 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
560 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000561 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000562 double x = dx();
563 double rx = rh.dx();
564 if (y == 0 && ry == 0 && x * rx < 0) {
565 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000566 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000567 double x_ry = x * ry;
568 double rx_y = rx * y;
569 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000570 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000571 return cmp < 0;
572 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000573 if (approximately_zero(x_ry) && approximately_zero(rx_y)
574 && !approximately_zero_squared(cmp)) {
575 return cmp < 0;
576 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000577 // at this point, the initial tangent line is coincident
caryclark@google.com31143cf2012-11-09 22:14:19 +0000578 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide)
579 || !approximately_zero(rh.fSide))) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000580 // FIXME: running demo will trigger this assertion
581 // (don't know if commenting out will trigger further assertion or not)
582 // commenting it out allows demo to run in release, though
583 // SkASSERT(fSide != rh.fSide);
584 return fSide < rh.fSide;
585 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000586 // see if either curve can be lengthened and try the tangent compare again
587 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
588 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
589 Angle longer = *this;
590 Angle rhLonger = rh;
591 if (longer.lengthen() | rhLonger.lengthen()) {
592 return longer < rhLonger;
593 }
caryclark@google.coma461ff02012-10-11 12:54:23 +0000594 // what if we extend in the other direction?
595 longer = *this;
596 rhLonger = rh;
597 if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
598 return longer < rhLonger;
599 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000600 }
caryclark@google.com185c7c42012-10-19 18:26:24 +0000601 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
caryclark@google.com31143cf2012-11-09 22:14:19 +0000602 || (rh.fVerb == SkPath::kLine_Verb
603 && approximately_zero(rx) && approximately_zero(ry))) {
caryclark@google.com185c7c42012-10-19 18:26:24 +0000604 // See general unsortable comment below. This case can happen when
605 // one line has a non-zero change in t but no change in x and y.
606 fUnsortable = true;
607 rh.fUnsortable = true;
608 return this < &rh; // even with no solution, return a stable sort
609 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000610 if ((*rh.fSpans)[SkMin32(rh.fStart, rh.fEnd)].fTiny
611 || (*fSpans)[SkMin32(fStart, fEnd)].fTiny) {
612 fUnsortable = true;
613 rh.fUnsortable = true;
614 return this < &rh; // even with no solution, return a stable sort
615 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000616 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
617 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000618 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000619 // end of the shorter tangent to midway between the end points
620 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000621 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
622 double len = fTangent1.normalSquared();
623 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000624 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000625 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000626 int roots, rroots;
627 bool flip = false;
628 do {
629 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
630 double midX = (q[0].x + q[2].x) / 2;
631 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000632 ray[0] = q[1];
633 ray[1].x = midX;
634 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000635 SkASSERT(ray[0] != ray[1]);
636 roots = QuadRayIntersect(fPts, ray, i);
637 rroots = QuadRayIntersect(rh.fPts, ray, ri);
638 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000639 if (roots == 0 || rroots == 0) {
640 // FIXME: we don't have a solution in this case. The interim solution
641 // is to mark the edges as unsortable, exclude them from this and
642 // future computations, and allow the returned path to be fragmented
643 fUnsortable = true;
644 rh.fUnsortable = true;
645 return this < &rh; // even with no solution, return a stable sort
646 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000647 _Point loc;
648 double best = SK_ScalarInfinity;
649 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000650 int index;
651 for (index = 0; index < roots; ++index) {
652 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000653 dx = loc.x - ray[0].x;
654 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000655 dist = dx * dx + dy * dy;
656 if (best > dist) {
657 best = dist;
658 }
659 }
660 for (index = 0; index < rroots; ++index) {
661 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000662 dx = loc.x - ray[0].x;
663 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000664 dist = dx * dx + dy * dy;
665 if (best > dist) {
666 return fSide < 0;
667 }
668 }
669 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000670 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000671
caryclark@google.com47580692012-07-23 12:14:49 +0000672 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000673 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000674 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000675
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000676 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000677 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000678 }
679
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000680 int end() const {
681 return fEnd;
682 }
683
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000684 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000685 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000686 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000687
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000688 bool lengthen() {
689 int newEnd = fEnd;
690 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
691 fEnd = newEnd;
692 setSpans();
693 return true;
694 }
695 return false;
696 }
697
caryclark@google.coma461ff02012-10-11 12:54:23 +0000698 bool reverseLengthen() {
699 if (fReversed) {
700 return false;
701 }
702 int newEnd = fStart;
703 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
704 fEnd = newEnd;
705 fReversed = true;
706 setSpans();
707 return true;
708 }
709 return false;
710 }
711
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000712 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000713 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000714 fSegment = segment;
715 fStart = start;
716 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000717 fPts = orig;
718 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000719 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000720 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000721 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000722 setSpans();
723 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000724
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000725 void setSpans() {
726 double startT = (*fSpans)[fStart].fT;
727 double endT = (*fSpans)[fEnd].fT;
728 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000729 case SkPath::kLine_Verb:
730 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000731 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000732 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
733 fTangent1.lineEndPoints(l);
caryclark@google.comf839c032012-10-26 21:03:50 +0000734 fUnsortable = dx() == 0 && dy() == 0;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000735 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000736 break;
737 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000738 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000739 fTangent1.quadEndPoints(fQ, 0, 1);
740 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000741 break;
742 case SkPath::kCubic_Verb:
743 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000744 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000745 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000746 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000747 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000748 default:
749 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000750 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000751 if (fUnsortable) {
752 return;
753 }
754 SkASSERT(fStart != fEnd);
755 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
756 for (int index = fStart; index != fEnd; index += step) {
757 if ((*fSpans)[index].fUnsortableStart) {
758 fUnsortable = true;
759 return;
760 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000761#if 0
caryclark@google.comf839c032012-10-26 21:03:50 +0000762 if (index != fStart && (*fSpans)[index].fUnsortableEnd) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000763 SkASSERT(0);
caryclark@google.comf839c032012-10-26 21:03:50 +0000764 fUnsortable = true;
765 return;
766 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000767#endif
caryclark@google.comf839c032012-10-26 21:03:50 +0000768 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000769 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000770
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000771 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000772 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000773 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000774
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000775 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000776 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000777 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000778
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000779 const SkTDArray<Span>* spans() const {
780 return fSpans;
781 }
782
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000783 int start() const {
784 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000785 }
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000786
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000787 bool unsortable() const {
788 return fUnsortable;
789 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000790
caryclark@google.comc899ad92012-08-23 15:24:42 +0000791#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000792 const SkPoint* pts() const {
793 return fPts;
794 }
795
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000796 SkPath::Verb verb() const {
797 return fVerb;
798 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000799
caryclark@google.comc899ad92012-08-23 15:24:42 +0000800 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000801 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000802 }
803#endif
804
caryclark@google.com15fa1382012-05-07 20:49:36 +0000805private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000806 const SkPoint* fPts;
807 Quadratic fQ;
808 SkPath::Verb fVerb;
809 double fSide;
810 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000811 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000812 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000813 int fStart;
814 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000815 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000816 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000817};
818
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000819// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000820struct Bounds : public SkRect {
821 static bool Intersects(const Bounds& a, const Bounds& b) {
822 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
823 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
824 }
825
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000826 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
827 if (left < fLeft) {
828 fLeft = left;
829 }
830 if (top < fTop) {
831 fTop = top;
832 }
833 if (right > fRight) {
834 fRight = right;
835 }
836 if (bottom > fBottom) {
837 fBottom = bottom;
838 }
839 }
840
841 void add(const Bounds& toAdd) {
842 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
843 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000844
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000845 void add(const SkPoint& pt) {
846 if (pt.fX < fLeft) fLeft = pt.fX;
847 if (pt.fY < fTop) fTop = pt.fY;
848 if (pt.fX > fRight) fRight = pt.fX;
849 if (pt.fY > fBottom) fBottom = pt.fY;
850 }
851
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000852 bool isEmpty() {
853 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000854 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000855 || isnan(fLeft) || isnan(fRight)
856 || isnan(fTop) || isnan(fBottom);
857 }
858
859 void setCubicBounds(const SkPoint a[4]) {
860 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000861 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000862 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000863 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
864 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000865 }
866
867 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000868 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000869 _Rect dRect;
870 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000871 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
872 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000873 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000874
875 void setPoint(const SkPoint& pt) {
876 fLeft = fRight = pt.fX;
877 fTop = fBottom = pt.fY;
878 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000879};
880
caryclark@google.com7ba591e2012-11-20 14:21:54 +0000881// OPTIMIZATION: does the following also work, and is it any faster?
882// return outerWinding * innerWinding > 0
883// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
caryclark@google.com2ddff932012-08-07 21:25:27 +0000884static bool useInnerWinding(int outerWinding, int innerWinding) {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000885 // SkASSERT(outerWinding != innerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +0000886 int absOut = abs(outerWinding);
887 int absIn = abs(innerWinding);
888 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
889 if (outerWinding * innerWinding < 0) {
890#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000891 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000892 outerWinding, innerWinding, result ? "true" : "false");
893#endif
894 }
895 return result;
896}
897
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000898#define F (false) // discard the edge
899#define T (true) // keep the edge
900
caryclark@google.comd0deb4f2012-12-17 13:58:08 +0000901static const bool gUnaryActiveEdge[2][2] = {
902// from=0 from=1
903// to=0,1 to=0,1
904 {F, T}, {T, F},
905};
906
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000907static const bool gActiveEdge[kShapeOp_Count][2][2][2][2] = {
908// miFrom=0 miFrom=1
909// miTo=0 miTo=1 miTo=0 miTo=1
910// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1
911// suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1
912 {{{{F, F}, {F, F}}, {{T, F}, {T, F}}}, {{{T, T}, {F, F}}, {{F, T}, {T, F}}}}, // mi - su
913 {{{{F, F}, {F, F}}, {{F, T}, {F, T}}}, {{{F, F}, {T, T}}, {{F, T}, {T, F}}}}, // mi & su
914 {{{{F, T}, {T, F}}, {{T, T}, {F, F}}}, {{{T, F}, {T, F}}, {{F, F}, {F, F}}}}, // mi | su
915 {{{{F, T}, {T, F}}, {{T, F}, {F, T}}}, {{{T, F}, {F, T}}, {{F, T}, {T, F}}}}, // mi ^ su
caryclark@google.com235f56a2012-09-14 14:19:30 +0000916};
917
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000918#undef F
919#undef T
caryclark@google.com235f56a2012-09-14 14:19:30 +0000920
caryclark@google.comf839c032012-10-26 21:03:50 +0000921// wrap path to keep track of whether the contour is initialized and non-empty
922class PathWrapper {
923public:
924 PathWrapper(SkPath& path)
925 : fPathPtr(&path)
caryclark@google.comd0deb4f2012-12-17 13:58:08 +0000926 , fCloses(0)
927 , fMoves(0)
caryclark@google.comf839c032012-10-26 21:03:50 +0000928 {
929 init();
930 }
931
932 void close() {
933 if (!fHasMove) {
934 return;
935 }
936 bool callClose = isClosed();
937 lineTo();
938 if (fEmpty) {
939 return;
940 }
941 if (callClose) {
942 #if DEBUG_PATH_CONSTRUCTION
943 SkDebugf("path.close();\n");
944 #endif
945 fPathPtr->close();
caryclark@google.comd0deb4f2012-12-17 13:58:08 +0000946 fCloses++;
caryclark@google.comf839c032012-10-26 21:03:50 +0000947 }
948 init();
949 }
950
951 void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
952 lineTo();
953 moveTo();
954#if DEBUG_PATH_CONSTRUCTION
955 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
956 pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
957#endif
958 fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
959 fDefer[0] = fDefer[1] = pt3;
960 fEmpty = false;
961 }
962
963 void deferredLine(const SkPoint& pt) {
964 if (pt == fDefer[1]) {
965 return;
966 }
967 if (changedSlopes(pt)) {
968 lineTo();
969 fDefer[0] = fDefer[1];
970 }
971 fDefer[1] = pt;
972 }
973
974 void deferredMove(const SkPoint& pt) {
975 fMoved = true;
976 fHasMove = true;
977 fEmpty = true;
978 fDefer[0] = fDefer[1] = pt;
979 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000980
caryclark@google.comf839c032012-10-26 21:03:50 +0000981 void deferredMoveLine(const SkPoint& pt) {
982 if (!fHasMove) {
983 deferredMove(pt);
984 }
985 deferredLine(pt);
986 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000987
caryclark@google.comf839c032012-10-26 21:03:50 +0000988 bool hasMove() const {
989 return fHasMove;
990 }
991
992 void init() {
993 fEmpty = true;
994 fHasMove = false;
995 fMoved = false;
996 }
997
998 bool isClosed() const {
999 return !fEmpty && fFirstPt == fDefer[1];
1000 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001001
caryclark@google.comf839c032012-10-26 21:03:50 +00001002 void lineTo() {
1003 if (fDefer[0] == fDefer[1]) {
1004 return;
1005 }
1006 moveTo();
1007 fEmpty = false;
1008#if DEBUG_PATH_CONSTRUCTION
1009 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
1010#endif
1011 fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
1012 fDefer[0] = fDefer[1];
1013 }
1014
1015 const SkPath* nativePath() const {
1016 return fPathPtr;
1017 }
1018
1019 void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
1020 lineTo();
1021 moveTo();
1022#if DEBUG_PATH_CONSTRUCTION
1023 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
1024 pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1025#endif
1026 fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1027 fDefer[0] = fDefer[1] = pt2;
1028 fEmpty = false;
1029 }
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001030
1031 bool someAssemblyRequired() const {
1032 return fCloses < fMoves;
1033 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001034
1035protected:
1036 bool changedSlopes(const SkPoint& pt) const {
1037 if (fDefer[0] == fDefer[1]) {
1038 return false;
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001039 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001040 SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
1041 SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
1042 SkScalar lineDx = pt.fX - fDefer[1].fX;
1043 SkScalar lineDy = pt.fY - fDefer[1].fY;
1044 return deferDx * lineDy != deferDy * lineDx;
1045 }
1046
1047 void moveTo() {
1048 if (!fMoved) {
1049 return;
1050 }
1051 fFirstPt = fDefer[0];
1052#if DEBUG_PATH_CONSTRUCTION
1053 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
1054#endif
1055 fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
1056 fMoved = false;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001057 fMoves++;
caryclark@google.comf839c032012-10-26 21:03:50 +00001058 }
1059
1060private:
1061 SkPath* fPathPtr;
1062 SkPoint fDefer[2];
1063 SkPoint fFirstPt;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001064 int fCloses;
1065 int fMoves;
caryclark@google.comf839c032012-10-26 21:03:50 +00001066 bool fEmpty;
1067 bool fHasMove;
1068 bool fMoved;
1069};
1070
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001071class Segment {
1072public:
1073 Segment() {
1074#if DEBUG_DUMP
1075 fID = ++gSegmentID;
1076#endif
1077 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001078
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001079 bool operator<(const Segment& rh) const {
1080 return fBounds.fTop < rh.fBounds.fTop;
1081 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001082
caryclark@google.com4eeda372012-12-06 21:47:48 +00001083 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001084 if (activeAngleInner(index, done, angles)) {
1085 return true;
1086 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001087 double referenceT = fTs[index].fT;
1088 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001089 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001090 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001091 return true;
1092 }
1093 }
1094 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001095 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001096 return true;
1097 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001098 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001099 return false;
1100 }
1101
caryclark@google.com4eeda372012-12-06 21:47:48 +00001102 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001103 Span* span = &fTs[index];
1104 Segment* other = span->fOther;
1105 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001106 return other->activeAngleInner(oIndex, done, angles);
1107 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001108
caryclark@google.com4eeda372012-12-06 21:47:48 +00001109 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001110 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001111 if (next > 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001112 Span& upSpan = fTs[index];
1113 if (upSpan.fWindValue || upSpan.fOppValue) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001114 addAngle(angles, index, next);
caryclark@google.comf839c032012-10-26 21:03:50 +00001115 if (upSpan.fDone || upSpan.fUnsortableEnd) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001116 done++;
1117 } else if (upSpan.fWindSum != SK_MinS32) {
1118 return true;
1119 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001120 } else if (!upSpan.fDone) {
1121 upSpan.fDone = true;
1122 fDoneSpans++;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001123 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001124 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001125 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001126 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +00001127 if (prev >= 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001128 Span& downSpan = fTs[prev];
1129 if (downSpan.fWindValue || downSpan.fOppValue) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001130 addAngle(angles, index, prev);
1131 if (downSpan.fDone) {
1132 done++;
1133 } else if (downSpan.fWindSum != SK_MinS32) {
1134 return true;
1135 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001136 } else if (!downSpan.fDone) {
1137 downSpan.fDone = true;
1138 fDoneSpans++;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001139 }
1140 }
1141 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001142 }
1143
caryclark@google.comf839c032012-10-26 21:03:50 +00001144 void activeLeftTop(SkPoint& result) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001145 SkASSERT(!done());
1146 int count = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001147 result.fY = SK_ScalarMax;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001148 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00001149 bool lastUnsortable = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001150 for (int index = 0; index < count; ++index) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001151 const Span& span = fTs[index];
1152 if (span.fUnsortableStart | lastUnsortable) {
1153 goto next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001154 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001155 if (!span.fDone | !lastDone) {
1156 const SkPoint& xy = xyAtT(index);
1157 if (result.fY < xy.fY) {
1158 goto next;
1159 }
1160 if (result.fY == xy.fY && result.fX < xy.fX) {
1161 goto next;
1162 }
1163 result = xy;
1164 }
1165 next:
1166 lastDone = span.fDone;
1167 lastUnsortable = span.fUnsortableEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001168 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001169 SkASSERT(result.fY < SK_ScalarMax);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001170 }
1171
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001172 bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, ShapeOp op) {
1173 int sumMiWinding = updateWinding(endIndex, index);
1174 int sumSuWinding = updateOppWinding(endIndex, index);
1175 if (fOperand) {
1176 SkTSwap<int>(sumMiWinding, sumSuWinding);
1177 }
1178 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
1179 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, sumMiWinding, sumSuWinding,
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001180 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001181 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001182
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001183 bool activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, ShapeOp op,
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00001184 int& sumMiWinding, int& sumSuWinding,
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001185 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
1186 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
1187 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001188 bool miFrom;
1189 bool miTo;
1190 bool suFrom;
1191 bool suTo;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001192 if (operand()) {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001193 miFrom = (oppMaxWinding & xorMiMask) != 0;
1194 miTo = (oppSumWinding & xorMiMask) != 0;
1195 suFrom = (maxWinding & xorSuMask) != 0;
1196 suTo = (sumWinding & xorSuMask) != 0;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001197 } else {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001198 miFrom = (maxWinding & xorMiMask) != 0;
1199 miTo = (sumWinding & xorMiMask) != 0;
1200 suFrom = (oppMaxWinding & xorSuMask) != 0;
1201 suTo = (oppSumWinding & xorSuMask) != 0;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001202 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001203 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
1204 SkASSERT(result != -1);
1205 return result;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001206 }
1207
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001208 bool activeWinding(int index, int endIndex) {
1209 int sumWinding = updateWinding(endIndex, index);
1210 int maxWinding;
1211 return activeWinding(index, endIndex, maxWinding, sumWinding);
1212 }
1213
1214 bool activeWinding(int index, int endIndex, int& maxWinding, int& sumWinding) {
1215 setUpWinding(index, endIndex, maxWinding, sumWinding);
1216 bool from = maxWinding != 0;
1217 bool to = sumWinding != 0;
1218 bool result = gUnaryActiveEdge[from][to];
1219 SkASSERT(result != -1);
1220 return result;
1221 }
1222
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001223 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001224 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001225 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001226#if DEBUG_ANGLE
caryclark@google.com31143cf2012-11-09 22:14:19 +00001227 if (angles.count() > 1 && !fTs[start].fTiny) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001228 SkPoint angle0Pt, newPt;
1229 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
1230 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
1231 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
1232 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
1233 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
1234 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001235#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001236 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001237 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001238
caryclark@google.com2ddff932012-08-07 21:25:27 +00001239 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001240 double oEnd) {
1241 int tIndex = -1;
1242 int tCount = fTs.count();
1243 int oIndex = -1;
1244 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001245 do {
1246 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001247 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001248 int tIndexStart = tIndex;
1249 do {
1250 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001251 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001252 int oIndexStart = oIndex;
1253 double nextT;
1254 do {
1255 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001256 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001257 double oNextT;
1258 do {
1259 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001260 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001261 // at this point, spans before and after are at:
1262 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1263 // if tIndexStart == 0, no prior span
1264 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001265
caryclark@google.comcc905052012-07-25 20:59:42 +00001266 // advance the span with zero winding
1267 // if the following span exists (not past the end, non-zero winding)
1268 // connect the two edges
1269 if (!fTs[tIndexStart].fWindValue) {
1270 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1271 #if DEBUG_CONCIDENT
1272 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1273 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001274 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1275 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001276 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001277 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001278 }
1279 if (nextT < 1 && fTs[tIndex].fWindValue) {
1280 #if DEBUG_CONCIDENT
1281 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1282 __FUNCTION__, fID, other.fID, tIndex,
1283 fTs[tIndex].fT, xyAtT(tIndex).fX,
1284 xyAtT(tIndex).fY);
1285 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001286 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001287 }
1288 } else {
1289 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1290 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1291 #if DEBUG_CONCIDENT
1292 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1293 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001294 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1295 other.xyAtT(oIndexStart).fY);
1296 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001297 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001298 }
1299 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1300 #if DEBUG_CONCIDENT
1301 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1302 __FUNCTION__, fID, other.fID, oIndex,
1303 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1304 other.xyAtT(oIndex).fY);
1305 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1306 #endif
1307 }
1308 }
1309 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001310
caryclark@google.comcc905052012-07-25 20:59:42 +00001311 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1312 double oEnd) {
1313 // walk this to outsideTs[0]
1314 // walk other to outsideTs[1]
1315 // if either is > 0, add a pointer to the other, copying adjacent winding
1316 int tIndex = -1;
1317 int oIndex = -1;
1318 double tStart = outsideTs[0];
1319 double oStart = outsideTs[1];
1320 do {
1321 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001322 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001323 do {
1324 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001325 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00001326 if (tIndex > 0 || oIndex > 0 || fOperand != other.fOperand) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001327 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001328 }
1329 tStart = fTs[tIndex].fT;
1330 oStart = other.fTs[oIndex].fT;
1331 do {
1332 double nextT;
1333 do {
1334 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001335 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001336 tStart = nextT;
1337 do {
1338 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001339 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001340 oStart = nextT;
caryclark@google.com4eeda372012-12-06 21:47:48 +00001341 if (tStart == 1 && oStart == 1 && fOperand == other.fOperand) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001342 break;
1343 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001344 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001345 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001346 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001347
caryclark@google.com4eeda372012-12-06 21:47:48 +00001348 void addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
1349 init(pts, SkPath::kCubic_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001350 fBounds.setCubicBounds(pts);
1351 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001352
caryclark@google.comf839c032012-10-26 21:03:50 +00001353 /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001354 SkPoint edge[4];
caryclark@google.comf839c032012-10-26 21:03:50 +00001355 const SkPoint* ePtr;
1356 int lastT = fTs.count() - 1;
1357 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
1358 ePtr = fPts;
1359 } else {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001360 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.comf839c032012-10-26 21:03:50 +00001361 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1362 ePtr = edge;
1363 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001364 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001365 bool reverse = ePtr == fPts && start != 0;
1366 if (reverse) {
1367 path.deferredMoveLine(ePtr[fVerb]);
1368 switch (fVerb) {
1369 case SkPath::kLine_Verb:
1370 path.deferredLine(ePtr[0]);
1371 break;
1372 case SkPath::kQuad_Verb:
1373 path.quadTo(ePtr[1], ePtr[0]);
1374 break;
1375 case SkPath::kCubic_Verb:
1376 path.cubicTo(ePtr[2], ePtr[1], ePtr[0]);
1377 break;
1378 default:
1379 SkASSERT(0);
1380 }
1381 // return ePtr[0];
1382 } else {
1383 path.deferredMoveLine(ePtr[0]);
1384 switch (fVerb) {
1385 case SkPath::kLine_Verb:
1386 path.deferredLine(ePtr[1]);
1387 break;
1388 case SkPath::kQuad_Verb:
1389 path.quadTo(ePtr[1], ePtr[2]);
1390 break;
1391 case SkPath::kCubic_Verb:
1392 path.cubicTo(ePtr[1], ePtr[2], ePtr[3]);
1393 break;
1394 default:
1395 SkASSERT(0);
1396 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001397 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001398 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001399 // return ePtr[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001400 }
1401
caryclark@google.com4eeda372012-12-06 21:47:48 +00001402 void addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
1403 init(pts, SkPath::kLine_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001404 fBounds.set(pts, 2);
1405 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001406
caryclark@google.comf839c032012-10-26 21:03:50 +00001407#if 0
1408 const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001409 const SkPoint& pt = xyAtT(tIndex);
1410 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001411 path.deferredMove(pt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001412 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001413 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001414 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001415#endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001416
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001417 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001418 void addOtherT(int index, double otherT, int otherIndex) {
1419 Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00001420 #if PIN_ADD_T
caryclark@google.com185c7c42012-10-19 18:26:24 +00001421 if (precisely_less_than_zero(otherT)) {
1422 otherT = 0;
1423 } else if (precisely_greater_than_one(otherT)) {
1424 otherT = 1;
1425 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001426 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001427 span.fOtherT = otherT;
1428 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001429 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001430
caryclark@google.com4eeda372012-12-06 21:47:48 +00001431 void addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
1432 init(pts, SkPath::kQuad_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001433 fBounds.setQuadBounds(pts);
1434 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001435
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001436 // Defer all coincident edge processing until
1437 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001438
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001439// no need to be tricky; insert in normal T order
1440// resolve overlapping ts when considering coincidence later
1441
1442 // add non-coincident intersection. Resulting edges are sorted in T.
1443 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001444 // FIXME: in the pathological case where there is a ton of intercepts,
1445 // binary search?
1446 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001447 size_t tCount = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001448 #if PIN_ADD_T
caryclark@google.comc899ad92012-08-23 15:24:42 +00001449 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001450 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001451 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001452 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001453 newT = 1;
1454 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001455 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001456 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001457 // OPTIMIZATION: if there are three or more identical Ts, then
1458 // the fourth and following could be further insertion-sorted so
1459 // that all the edges are clockwise or counterclockwise.
1460 // This could later limit segment tests to the two adjacent
1461 // neighbors, although it doesn't help with determining which
1462 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001463 if (newT < fTs[index].fT) {
1464 insertedAt = index;
1465 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001466 }
1467 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001468 Span* span;
1469 if (insertedAt >= 0) {
1470 span = fTs.insert(insertedAt);
1471 } else {
1472 insertedAt = tCount;
1473 span = fTs.append();
1474 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001475 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001476 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001477 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001478 span->fWindSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001479 span->fOppSum = SK_MinS32;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001480 span->fWindValue = 1;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001481 span->fOppValue = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00001482 span->fTiny = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001483 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001484 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001485 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001486 span->fUnsortableStart = false;
1487 span->fUnsortableEnd = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00001488 if (span - fTs.begin() > 0 && !span[-1].fDone
1489 && !precisely_negative(newT - span[-1].fT)
1490 // && approximately_negative(newT - span[-1].fT)
1491 && xyAtT(&span[-1]) == xyAtT(span)) {
1492 span[-1].fTiny = true;
1493 span[-1].fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001494 if (approximately_negative(newT - span[-1].fT)) {
1495 if (approximately_greater_than_one(newT)) {
1496 span[-1].fUnsortableStart = true;
1497 span[-2].fUnsortableEnd = true;
1498 }
1499 if (approximately_less_than_zero(span[-1].fT)) {
1500 span->fUnsortableStart = true;
1501 span[-1].fUnsortableEnd = true;
1502 }
1503 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001504 ++fDoneSpans;
1505 }
1506 if (fTs.end() - span > 1 && !span->fDone
1507 && !precisely_negative(span[1].fT - newT)
1508 // && approximately_negative(span[1].fT - newT)
1509 && xyAtT(&span[1]) == xyAtT(span)) {
1510 span->fTiny = true;
1511 span->fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001512 if (approximately_negative(span[1].fT - newT)) {
1513 if (approximately_greater_than_one(span[1].fT)) {
1514 span->fUnsortableStart = true;
1515 span[-1].fUnsortableEnd = true;
1516 }
1517 if (approximately_less_than_zero(newT)) {
1518 span[1].fUnsortableStart = true;
1519 span->fUnsortableEnd = true;
1520 }
1521 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001522 ++fDoneSpans;
1523 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001524 return insertedAt;
1525 }
1526
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001527 // set spans from start to end to decrement by one
1528 // note this walks other backwards
1529 // FIMXE: there's probably an edge case that can be constructed where
1530 // two span in one segment are separated by float epsilon on one span but
1531 // not the other, if one segment is very small. For this
1532 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001533 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001534 // sorted? It feels better in such a case to match the span's other span
1535 // pointer since both coincident segments must contain the same spans.
1536 void addTCancel(double startT, double endT, Segment& other,
1537 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001538 SkASSERT(!approximately_negative(endT - startT));
1539 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001540 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001541 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001542 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001543 ++index;
1544 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001545 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001546 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001547 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001548 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001549 Span* test = &fTs[index];
1550 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001551 SkTDArray<double> outsideTs;
1552 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001553 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001554 bool decrement = test->fWindValue && oTest->fWindValue && !binary;
caryclark@google.comcc905052012-07-25 20:59:42 +00001555 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001556 double testT = test->fT;
1557 double oTestT = oTest->fT;
1558 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001559 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001560 if (decrement) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001561 decrementSpan(span);
caryclark@google.com200c2112012-08-03 15:05:04 +00001562 } else if (track && span->fT < 1 && oTestT < 1) {
1563 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001564 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001565 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001566 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001567 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001568 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1569 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1570 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001571 while (approximately_negative(otherTMatchStart - oSpan->fT)
1572 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001573 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001574 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001575 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001576 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001577 other.decrementSpan(oSpan);
1578 } else if (track && oSpan->fT < 1 && testT < 1) {
1579 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001580 }
1581 if (!oIndex) {
1582 break;
1583 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001584 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001585 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001586 test = span;
1587 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001588 } while (!approximately_negative(endT - test->fT));
1589 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001590 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001591 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001592 double tStart = outsideTs[0];
1593 double oStart = outsideTs[1];
1594 addCancelOutsides(tStart, oStart, other, oEndT);
1595 int count = outsideTs.count();
1596 if (count > 2) {
1597 double tStart = outsideTs[count - 2];
1598 double oStart = outsideTs[count - 1];
1599 addCancelOutsides(tStart, oStart, other, oEndT);
1600 }
caryclark@google.com18063442012-07-25 12:05:18 +00001601 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001602 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001603 double tStart = oOutsideTs[0];
1604 double oStart = oOutsideTs[1];
1605 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001606 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001607 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00001608
caryclark@google.com4eeda372012-12-06 21:47:48 +00001609 int bumpCoincidentThis(const Span* oTest, bool opp, int index,
1610 SkTDArray<double>& outsideTs) {
1611 int oWindValue = oTest->fWindValue;
1612 int oOppValue = oTest->fOppValue;
1613 if (opp) {
1614 SkTSwap<int>(oWindValue, oOppValue);
1615 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001616 Span* const test = &fTs[index];
1617 Span* end = test;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001618 const double oStartT = oTest->fT;
1619 do {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001620 if (bumpSpan(end, oWindValue, oOppValue)) {
1621 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001622 }
1623 end = &fTs[++index];
1624 } while (approximately_negative(end->fT - test->fT));
1625 return index;
1626 }
1627
1628 // because of the order in which coincidences are resolved, this and other
1629 // may not have the same intermediate points. Compute the corresponding
1630 // intermediate T values (using this as the master, other as the follower)
1631 // and walk other conditionally -- hoping that it catches up in the end
caryclark@google.com4eeda372012-12-06 21:47:48 +00001632 int bumpCoincidentOther(const Span* test, double oEndT, int& oIndex,
1633 SkTDArray<double>& oOutsideTs) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001634 Span* const oTest = &fTs[oIndex];
1635 Span* oEnd = oTest;
1636 const double startT = test->fT;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001637 const double oStartT = oTest->fT;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001638 while (!approximately_negative(oEndT - oEnd->fT)
caryclark@google.com4eeda372012-12-06 21:47:48 +00001639 && approximately_negative(oEnd->fT - oStartT)) {
1640 zeroSpan(oEnd);
1641 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001642 oEnd = &fTs[++oIndex];
1643 }
1644 return oIndex;
1645 }
1646
1647 // FIXME: need to test this case:
1648 // contourA has two segments that are coincident
1649 // contourB has two segments that are coincident in the same place
1650 // each ends up with +2/0 pairs for winding count
1651 // since logic below doesn't transfer count (only increments/decrements) can this be
1652 // resolved to +4/0 ?
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001653
1654 // set spans from start to end to increment the greater by one and decrement
1655 // the lesser
caryclark@google.com4eeda372012-12-06 21:47:48 +00001656 void addTCoincident(double startT, double endT, Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001657 SkASSERT(!approximately_negative(endT - startT));
1658 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001659 bool opp = fOperand ^ other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001660 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001661 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001662 ++index;
1663 }
1664 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001665 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001666 ++oIndex;
1667 }
1668 Span* test = &fTs[index];
1669 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001670 SkTDArray<double> outsideTs;
1671 SkTDArray<double> oOutsideTs;
1672 do {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001673 // if either span has an opposite value and the operands don't match, resolve first
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001674 // SkASSERT(!test->fDone || !oTest->fDone);
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00001675 if (test->fDone || oTest->fDone) {
1676 index = advanceCoincidentThis(oTest, opp, index);
1677 oIndex = other.advanceCoincidentOther(test, oEndT, oIndex);
1678 } else {
1679 index = bumpCoincidentThis(oTest, opp, index, outsideTs);
1680 oIndex = other.bumpCoincidentOther(test, oEndT, oIndex, oOutsideTs);
1681 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001682 test = &fTs[index];
1683 oTest = &other.fTs[oIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001684 } while (!approximately_negative(endT - test->fT));
1685 SkASSERT(approximately_negative(oTest->fT - oEndT));
1686 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00001687 if (!done() && outsideTs.count()) {
1688 addCoinOutsides(outsideTs, other, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001689 }
1690 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001691 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001692 }
1693 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001694
caryclark@google.comcc905052012-07-25 20:59:42 +00001695 // FIXME: this doesn't prevent the same span from being added twice
1696 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001697 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001698 int tCount = fTs.count();
1699 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1700 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001701 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001702 break;
1703 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001704 if (approximately_negative(span.fT - t) && span.fOther == &other
1705 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001706#if DEBUG_ADD_T_PAIR
1707 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1708 __FUNCTION__, fID, t, other.fID, otherT);
1709#endif
1710 return;
1711 }
1712 }
caryclark@google.com47580692012-07-23 12:14:49 +00001713#if DEBUG_ADD_T_PAIR
1714 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1715 __FUNCTION__, fID, t, other.fID, otherT);
1716#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001717 int insertedAt = addT(t, &other);
1718 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001719 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001720 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001721 matchWindingValue(insertedAt, t, borrowWind);
1722 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001723 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001724
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001725 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001726 // add edge leading into junction
caryclark@google.com4eeda372012-12-06 21:47:48 +00001727 int min = SkMin32(end, start);
1728 if (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001729 addAngle(angles, end, start);
1730 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001731 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001732 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001733 int tIndex = nextExactSpan(end, step);
caryclark@google.com4eeda372012-12-06 21:47:48 +00001734 min = SkMin32(end, tIndex);
1735 if (tIndex >= 0 && (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001736 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001737 }
1738 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001739
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00001740 int advanceCoincidentThis(const Span* oTest, bool opp, int index) {
1741 Span* const test = &fTs[index];
1742 Span* end = test;
1743 do {
1744 end = &fTs[++index];
1745 } while (approximately_negative(end->fT - test->fT));
1746 return index;
1747 }
1748
1749 int advanceCoincidentOther(const Span* test, double oEndT, int& oIndex) {
1750 Span* const oTest = &fTs[oIndex];
1751 Span* oEnd = oTest;
1752 const double oStartT = oTest->fT;
1753 while (!approximately_negative(oEndT - oEnd->fT)
1754 && approximately_negative(oEnd->fT - oStartT)) {
1755 oEnd = &fTs[++oIndex];
1756 }
1757 return oIndex;
1758 }
1759
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001760 const Bounds& bounds() const {
1761 return fBounds;
1762 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001763
caryclark@google.com31143cf2012-11-09 22:14:19 +00001764 void buildAngles(int index, SkTDArray<Angle>& angles, bool includeOpp) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001765 double referenceT = fTs[index].fT;
1766 int lesser = index;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001767 while (--lesser >= 0 && (includeOpp || fTs[lesser].fOther->fOperand == fOperand)
1768 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00001769 buildAnglesInner(lesser, angles);
1770 }
1771 do {
1772 buildAnglesInner(index, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001773 } while (++index < fTs.count() && (includeOpp || fTs[index].fOther->fOperand == fOperand)
1774 && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001775 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001776
1777 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1778 Span* span = &fTs[index];
1779 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001780 // if there is only one live crossing, and no coincidence, continue
1781 // in the same direction
1782 // if there is coincidence, the only choice may be to reverse direction
1783 // find edge on either side of intersection
1784 int oIndex = span->fOtherIndex;
1785 // if done == -1, prior span has already been processed
1786 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001787 int next = other->nextExactSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001788 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001789 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001790 next = other->nextExactSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001791 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001792 // add candidate into and away from junction
1793 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001794 }
1795
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001796 int computeSum(int startIndex, int endIndex, bool binary) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001797 SkTDArray<Angle> angles;
1798 addTwoAngles(startIndex, endIndex, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001799 buildAngles(endIndex, angles, false);
caryclark@google.comd1688742012-09-18 20:08:37 +00001800 // OPTIMIZATION: check all angles to see if any have computed wind sum
1801 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001802 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001803 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001804#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001805 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001806#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001807 if (!sortable) {
1808 return SK_MinS32;
1809 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001810 int angleCount = angles.count();
1811 const Angle* angle;
1812 const Segment* base;
1813 int winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001814 int oWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001815 int firstIndex = 0;
1816 do {
1817 angle = sorted[firstIndex];
1818 base = angle->segment();
1819 winding = base->windSum(angle);
1820 if (winding != SK_MinS32) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001821 oWinding = base->oppSum(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001822 break;
1823 }
1824 if (++firstIndex == angleCount) {
1825 return SK_MinS32;
1826 }
1827 } while (true);
1828 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001829 int spanWinding = base->spanSign(angle);
1830 bool inner = useInnerWinding(winding + spanWinding, winding);
1831 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001832 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001833 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001834 inner ? winding + spanWinding : winding);
1835 #endif
1836 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001837 winding += spanWinding;
1838 }
1839 #if DEBUG_SORT
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001840 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001841 #endif
1842 int nextIndex = firstIndex + 1;
1843 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001844 winding -= base->spanSign(angle);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001845 oWinding -= base->oppSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001846 do {
1847 if (nextIndex == angleCount) {
1848 nextIndex = 0;
1849 }
1850 angle = sorted[nextIndex];
1851 Segment* segment = angle->segment();
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001852 bool opp = base->fOperand ^ segment->fOperand;
1853 int maxWinding, oMaxWinding;
1854 int spanSign = segment->spanSign(angle);
1855 int oppoSign = segment->oppSign(angle);
1856 if (opp) {
1857 oMaxWinding = oWinding;
1858 oWinding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001859 maxWinding = winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001860 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001861 winding -= oppoSign;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001862 }
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001863 } else {
1864 maxWinding = winding;
1865 winding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001866 oMaxWinding = oWinding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001867 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001868 oWinding -= oppoSign;
1869 }
1870 }
1871 if (segment->windSum(angle) == SK_MinS32) {
1872 if (opp) {
1873 if (useInnerWinding(oMaxWinding, oWinding)) {
1874 oMaxWinding = oWinding;
1875 }
1876 if (oppoSign && useInnerWinding(maxWinding, winding)) {
1877 maxWinding = winding;
1878 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001879 (void) segment->markAndChaseWinding(angle, oMaxWinding, maxWinding);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001880 } else {
1881 if (useInnerWinding(maxWinding, winding)) {
1882 maxWinding = winding;
1883 }
1884 if (oppoSign && useInnerWinding(oMaxWinding, oWinding)) {
1885 oMaxWinding = oWinding;
1886 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001887 (void) segment->markAndChaseWinding(angle, maxWinding,
1888 binary ? oMaxWinding : 0);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001889 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001890 }
1891 } while (++nextIndex != lastIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001892 int minIndex = SkMin32(startIndex, endIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001893 return windSum(minIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001894 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001895
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001896 int crossedSpanX(const SkPoint& basePt, SkScalar& bestX, double& hitT, bool opp) const {
1897 int bestT = -1;
1898 SkScalar left = bounds().fLeft;
1899 SkScalar right = bounds().fRight;
1900 int end = 0;
1901 do {
1902 int start = end;
1903 end = nextSpan(start, 1);
1904 if ((opp ? fTs[start].fOppValue : fTs[start].fWindValue) == 0) {
1905 continue;
1906 }
1907 SkPoint edge[4];
1908 double startT = fTs[start].fT;
1909 double endT = fTs[end].fT;
1910 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
1911 // intersect ray starting at basePt with edge
1912 Intersections intersections;
1913 // FIXME: always use original and limit results to T values within
1914 // start t and end t.
1915 // OPTIMIZE: use specialty function that intersects ray with curve,
1916 // returning t values only for curve (we don't care about t on ray)
1917 int pts = (*HSegmentIntersect[fVerb])(edge, left, right, basePt.fY,
1918 false, intersections);
1919 if (pts == 0) {
1920 continue;
1921 }
1922 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1923 // if the intersection is edge on, wait for another one
1924 continue;
1925 }
1926 for (int index = 0; index < pts; ++index) {
1927 double foundT = intersections.fT[0][index];
1928 double testT = startT + (endT - startT) * foundT;
1929 SkScalar testX = (*SegmentXAtT[fVerb])(fPts, testT);
1930 if (bestX < testX && testX < basePt.fX) {
1931 if (fVerb > SkPath::kLine_Verb
1932 && !approximately_less_than_zero(foundT)
1933 && !approximately_greater_than_one(foundT)) {
1934 SkScalar dy = (*SegmentDYAtT[fVerb])(fPts, testT);
1935 if (approximately_zero(dy)) {
1936 continue;
1937 }
1938 }
1939 bestX = testX;
1940 bestT = foundT < 1 ? start : end;
1941 hitT = testT;
1942 }
1943 }
1944 } while (fTs[end].fT != 1);
1945 return bestT;
1946 }
1947
1948 int crossedSpanY(const SkPoint& basePt, SkScalar& bestY, double& hitT, bool opp) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001949 int bestT = -1;
1950 SkScalar top = bounds().fTop;
1951 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001952 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001953 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001954 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001955 end = nextSpan(start, 1);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001956 if ((opp ? fTs[start].fOppValue : fTs[start].fWindValue) == 0) {
caryclark@google.com47580692012-07-23 12:14:49 +00001957 continue;
1958 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001959 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001960 double startT = fTs[start].fT;
1961 double endT = fTs[end].fT;
1962 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001963 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001964 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001965 // FIXME: always use original and limit results to T values within
1966 // start t and end t.
1967 // OPTIMIZE: use specialty function that intersects ray with curve,
1968 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001969 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1970 false, intersections);
1971 if (pts == 0) {
1972 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001973 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001974 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1975 // if the intersection is edge on, wait for another one
1976 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001977 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001978 for (int index = 0; index < pts; ++index) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001979 double foundT = intersections.fT[0][index];
1980 double testT = startT + (endT - startT) * foundT;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001981 SkScalar testY = (*SegmentYAtT[fVerb])(fPts, testT);
1982 if (bestY < testY && testY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001983 if (fVerb > SkPath::kLine_Verb
1984 && !approximately_less_than_zero(foundT)
1985 && !approximately_greater_than_one(foundT)) {
1986 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1987 if (approximately_zero(dx)) {
1988 continue;
1989 }
1990 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001991 bestY = testY;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001992 bestT = foundT < 1 ? start : end;
1993 hitT = testT;
1994 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001995 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001996 } while (fTs[end].fT != 1);
1997 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001998 }
caryclark@google.com18063442012-07-25 12:05:18 +00001999
caryclark@google.com4eeda372012-12-06 21:47:48 +00002000 void decrementSpan(Span* span) {
caryclark@google.com18063442012-07-25 12:05:18 +00002001 SkASSERT(span->fWindValue > 0);
2002 if (--(span->fWindValue) == 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00002003 if (!span->fOppValue && !span->fDone) {
caryclark@google.comf839c032012-10-26 21:03:50 +00002004 span->fDone = true;
2005 ++fDoneSpans;
2006 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00002007 }
2008 }
2009
2010 bool bumpSpan(Span* span, int windDelta, int oppDelta) {
2011 SkASSERT(!span->fDone);
2012 span->fWindValue += windDelta;
2013 SkASSERT(span->fWindValue >= 0);
2014 span->fOppValue += oppDelta;
2015 SkASSERT(span->fOppValue >= 0);
2016 if (fXor) {
2017 span->fWindValue &= 1;
2018 }
2019 if (fOppXor) {
2020 span->fOppValue &= 1;
2021 }
2022 if (!span->fWindValue && !span->fOppValue) {
2023 span->fDone = true;
2024 ++fDoneSpans;
caryclark@google.com18063442012-07-25 12:05:18 +00002025 return true;
2026 }
2027 return false;
2028 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002029
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00002030 // OPTIMIZE
2031 // when the edges are initially walked, they don't automatically get the prior and next
2032 // edges assigned to positions t=0 and t=1. Doing that would remove the need for this check,
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002033 // and would additionally remove the need for similar checks in condition edges. It would
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00002034 // also allow intersection code to assume end of segment intersections (maybe?)
2035 bool complete() const {
2036 int count = fTs.count();
2037 return count > 1 && fTs[0].fT == 0 && fTs[--count].fT == 1;
2038 }
caryclark@google.com18063442012-07-25 12:05:18 +00002039
caryclark@google.com15fa1382012-05-07 20:49:36 +00002040 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002041 SkASSERT(fDoneSpans <= fTs.count());
2042 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002043 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002044
caryclark@google.comf839c032012-10-26 21:03:50 +00002045 bool done(int min) const {
2046 return fTs[min].fDone;
2047 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002048
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002049 bool done(const Angle* angle) const {
2050 return done(SkMin32(angle->start(), angle->end()));
caryclark@google.com47580692012-07-23 12:14:49 +00002051 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002052
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002053 /*
2054 The M and S variable name parts stand for the operators.
2055 Mi stands for Minuend (see wiki subtraction, analogous to difference)
2056 Su stands for Subtrahend
2057 The Opp variable name part designates that the value is for the Opposite operator.
2058 Opposite values result from combining coincident spans.
2059 */
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002060
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002061 Segment* findNextOp(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd,
2062 bool& unsortable, ShapeOp op, const int xorMiMask, const int xorSuMask) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002063 const int startIndex = nextStart;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002064 const int endIndex = nextEnd;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002065 SkASSERT(startIndex != endIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002066 const int count = fTs.count();
2067 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
2068 const int step = SkSign32(endIndex - startIndex);
2069 const int end = nextExactSpan(startIndex, step);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002070 SkASSERT(end >= 0);
2071 Span* endSpan = &fTs[end];
2072 Segment* other;
2073 if (isSimple(end)) {
2074 // mark the smaller of startIndex, endIndex done, and all adjacent
2075 // spans with the same T value (but not 'other' spans)
2076 #if DEBUG_WINDING
2077 SkDebugf("%s simple\n", __FUNCTION__);
2078 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002079 int min = SkMin32(startIndex, endIndex);
2080 if (fTs[min].fDone) {
2081 return NULL;
2082 }
2083 markDoneBinary(min);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002084 other = endSpan->fOther;
2085 nextStart = endSpan->fOtherIndex;
2086 double startT = other->fTs[nextStart].fT;
2087 nextEnd = nextStart;
2088 do {
2089 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002090 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002091 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00002092 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2093 return other;
2094 }
2095 // more than one viable candidate -- measure angles to find best
2096 SkTDArray<Angle> angles;
2097 SkASSERT(startIndex - endIndex != 0);
2098 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2099 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002100 buildAngles(end, angles, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002101 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002102 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002103 int angleCount = angles.count();
2104 int firstIndex = findStartingEdge(sorted, startIndex, end);
2105 SkASSERT(firstIndex >= 0);
2106 #if DEBUG_SORT
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002107 debugShowSort(__FUNCTION__, sorted, firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002108 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002109 if (!sortable) {
2110 unsortable = true;
2111 return NULL;
2112 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002113 SkASSERT(sorted[firstIndex]->segment() == this);
2114 #if DEBUG_WINDING
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002115 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
2116 sorted[firstIndex]->sign());
caryclark@google.com235f56a2012-09-14 14:19:30 +00002117 #endif
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002118 int sumMiWinding = updateWinding(endIndex, startIndex);
2119 int sumSuWinding = updateOppWinding(endIndex, startIndex);
2120 if (operand()) {
2121 SkTSwap<int>(sumMiWinding, sumSuWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002122 }
2123 int nextIndex = firstIndex + 1;
2124 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2125 const Angle* foundAngle = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002126 bool foundDone = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002127 // iterate through the angle, and compute everyone's winding
caryclark@google.com235f56a2012-09-14 14:19:30 +00002128 Segment* nextSegment;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002129 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002130 SkASSERT(nextIndex != firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002131 if (nextIndex == angleCount) {
2132 nextIndex = 0;
2133 }
2134 const Angle* nextAngle = sorted[nextIndex];
2135 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002136 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
2137 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
2138 nextAngle->end(), op, sumMiWinding, sumSuWinding,
2139 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
2140 if (activeAngle && (!foundAngle || foundDone)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002141 foundAngle = nextAngle;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002142 foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002143 }
2144 if (nextSegment->done()) {
2145 continue;
2146 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002147 if (nextSegment->windSum(nextAngle) != SK_MinS32) {
2148 continue;
2149 }
2150 Span* last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding,
2151 oppSumWinding, activeAngle, nextAngle);
2152 if (last) {
2153 *chase.append() = last;
2154#if DEBUG_WINDING
2155 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2156 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2157#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002158 }
2159 } while (++nextIndex != lastIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002160 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com235f56a2012-09-14 14:19:30 +00002161 if (!foundAngle) {
2162 return NULL;
2163 }
2164 nextStart = foundAngle->start();
2165 nextEnd = foundAngle->end();
2166 nextSegment = foundAngle->segment();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002167
caryclark@google.com235f56a2012-09-14 14:19:30 +00002168 #if DEBUG_WINDING
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002169 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2170 __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002171 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002172 return nextSegment;
2173 }
caryclark@google.com47580692012-07-23 12:14:49 +00002174
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002175 // so the span needs to contain the pairing info found here
2176 // this should include the winding computed for the edge, and
2177 // what edge it connects to, and whether it is discarded
2178 // (maybe discarded == abs(winding) > 1) ?
2179 // only need derivatives for duration of sorting, add a new struct
2180 // for pairings, remove extra spans that have zero length and
2181 // reference an unused other
2182 // for coincident, the last span on the other may be marked done
2183 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002184
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002185 // if loop is exhausted, contour may be closed.
2186 // FIXME: pass in close point so we can check for closure
2187
2188 // given a segment, and a sense of where 'inside' is, return the next
2189 // segment. If this segment has an intersection, or ends in multiple
2190 // segments, find the mate that continues the outside.
2191 // note that if there are multiples, but no coincidence, we can limit
2192 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00002193
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002194 // mark found segments as done
2195
caryclark@google.com15fa1382012-05-07 20:49:36 +00002196 // start is the index of the beginning T of this edge
2197 // it is guaranteed to have an end which describes a non-zero length (?)
2198 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00002199 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002200 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
2201 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002202 const int startIndex = nextStart;
2203 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002204 int outerWinding = winding;
2205 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00002206 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002207 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
2208 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00002209 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002210 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002211 outerWinding = innerWinding;
2212 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002213 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002214 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002215 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2216 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002217 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002218 int end = nextExactSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002219 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002220 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002221 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002222 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002223 // mark the smaller of startIndex, endIndex done, and all adjacent
2224 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002225 #if DEBUG_WINDING
2226 SkDebugf("%s simple\n", __FUNCTION__);
2227 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002228 int min = SkMin32(startIndex, endIndex);
2229 if (fTs[min].fDone) {
2230 return NULL;
2231 }
2232 markDone(min, outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002233 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002234 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00002235 double startT = other->fTs[nextStart].fT;
2236 nextEnd = nextStart;
2237 do {
2238 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002239 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002240 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00002241 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00002242 return other;
2243 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002244 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00002245 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002246 SkASSERT(startIndex - endIndex != 0);
2247 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002248 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002249 buildAngles(end, angles, false);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002250 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002251 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002252 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002253 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002254 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002255 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002256 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002257 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002258 if (!sortable) {
2259 unsortable = true;
2260 return NULL;
2261 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002262 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002263 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002264 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00002265 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00002266 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002267 int nextIndex = firstIndex + 1;
2268 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2269 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00002270 // FIXME: found done logic probably fails if there are more than 4
2271 // sorted angles. It should bias towards the first and last undone
2272 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002273 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00002274 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002275 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002276 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00002277 bool altFlipped = false;
2278 bool foundFlipped = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002279 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002280 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00002281 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002282 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002283 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002284 nextIndex = 0;
2285 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002286 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00002287 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00002288 if (sumWinding) {
2289 lastNonZeroSum = sumWinding;
2290 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002291 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002292 bool nextDone = nextSegment->done(nextAngle);
2293 bool nextTiny = nextSegment->tiny(nextAngle);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002294 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002295 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002296 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002297 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002298 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2299 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002300 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002301 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002302 if (!active) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002303 // FIXME: shouldn't this call mark and chase done ?
caryclark@google.com59823f72012-08-09 18:17:47 +00002304 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002305 // FIXME: shouldn't this call mark and chase winding ?
caryclark@google.com47580692012-07-23 12:14:49 +00002306 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002307 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002308 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002309 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002310 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002311 return NULL;
2312 }
caryclark@google.com47580692012-07-23 12:14:49 +00002313 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002314 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002315 foundDone = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002316 foundFlipped = altFlipped;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002317 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002318 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002319 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002320
caryclark@google.com24bec792012-08-20 12:43:57 +00002321 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002322 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002323 if (foundAngle && foundDone2) {
2324 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002325 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002326 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002327 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002328 foundDone2 = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002329 foundFlipped = altFlipped;
2330 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002331 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002332 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002333 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002334 }
2335 // if the winding is non-zero, nextAngle does not connect to
2336 // current chain. If we haven't done so already, mark the angle
2337 // as done, record the winding value, and mark connected unambiguous
2338 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002339 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002340 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002341 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002342 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002343 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002344 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002345 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002346 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002347 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002348 }
2349 if (last) {
2350 *chase.append() = last;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002351 #if DEBUG_WINDING
2352 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2353 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2354 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002355 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002356 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002357 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002358 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002359 if (!foundAngle) {
2360 return NULL;
2361 }
2362 nextStart = foundAngle->start();
2363 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002364 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002365 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002366 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2367 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002368 if (winding) {
2369 #if DEBUG_WINDING
2370 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2371 if (foundSum == SK_MinS32) {
2372 SkDebugf("?");
2373 } else {
2374 SkDebugf("%d", foundSum);
2375 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002376 SkDebugf("\n");
2377 #endif
2378 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002379 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002380 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002381 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002382 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002383 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002384 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002385
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002386 Segment* findNextWinding(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd,
2387 bool& unsortable) {
2388 const int startIndex = nextStart;
2389 const int endIndex = nextEnd;
2390 SkASSERT(startIndex != endIndex);
2391 const int count = fTs.count();
2392 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
2393 const int step = SkSign32(endIndex - startIndex);
2394 const int end = nextExactSpan(startIndex, step);
2395 SkASSERT(end >= 0);
2396 Span* endSpan = &fTs[end];
2397 Segment* other;
2398 if (isSimple(end)) {
2399 // mark the smaller of startIndex, endIndex done, and all adjacent
2400 // spans with the same T value (but not 'other' spans)
2401 #if DEBUG_WINDING
2402 SkDebugf("%s simple\n", __FUNCTION__);
2403 #endif
2404 int min = SkMin32(startIndex, endIndex);
2405 if (fTs[min].fDone) {
2406 return NULL;
2407 }
2408 markDoneUnary(min);
2409 other = endSpan->fOther;
2410 nextStart = endSpan->fOtherIndex;
2411 double startT = other->fTs[nextStart].fT;
2412 nextEnd = nextStart;
2413 do {
2414 nextEnd += step;
2415 }
2416 while (precisely_zero(startT - other->fTs[nextEnd].fT));
2417 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2418 return other;
2419 }
2420 // more than one viable candidate -- measure angles to find best
2421 SkTDArray<Angle> angles;
2422 SkASSERT(startIndex - endIndex != 0);
2423 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2424 addTwoAngles(startIndex, end, angles);
2425 buildAngles(end, angles, true);
2426 SkTDArray<Angle*> sorted;
2427 bool sortable = SortAngles(angles, sorted);
2428 int angleCount = angles.count();
2429 int firstIndex = findStartingEdge(sorted, startIndex, end);
2430 SkASSERT(firstIndex >= 0);
2431 #if DEBUG_SORT
2432 debugShowSort(__FUNCTION__, sorted, firstIndex);
2433 #endif
2434 if (!sortable) {
2435 unsortable = true;
2436 return NULL;
2437 }
2438 SkASSERT(sorted[firstIndex]->segment() == this);
2439 #if DEBUG_WINDING
2440 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
2441 sorted[firstIndex]->sign());
2442 #endif
2443 int sumWinding = updateWinding(endIndex, startIndex);
2444 int outside = sumWinding & 1; // associate pairs together to avoid figure eights
2445 int nextIndex = firstIndex + 1;
2446 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2447 const Angle* foundAngle = NULL;
2448 bool foundDone = false;
2449 // iterate through the angle, and compute everyone's winding
2450 Segment* nextSegment;
2451 do {
2452 SkASSERT(nextIndex != firstIndex);
2453 if (nextIndex == angleCount) {
2454 nextIndex = 0;
2455 }
2456 const Angle* nextAngle = sorted[nextIndex];
2457 nextSegment = nextAngle->segment();
2458 int maxWinding;
2459 bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
2460 maxWinding, sumWinding);
2461 if (activeAngle && (!foundAngle || foundDone) && outside != (sumWinding & 1)) {
2462 foundAngle = nextAngle;
2463 foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle);
2464 }
2465 if (nextSegment->done()) {
2466 continue;
2467 }
2468 if (nextSegment->windSum(nextAngle) != SK_MinS32) {
2469 continue;
2470 }
2471 Span* last = nextSegment->markAngle(maxWinding, sumWinding, activeAngle, nextAngle);
2472 if (last) {
2473 *chase.append() = last;
2474#if DEBUG_WINDING
2475 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2476 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2477#endif
2478 }
2479 } while (++nextIndex != lastIndex);
2480 markDoneUnary(SkMin32(startIndex, endIndex));
2481 if (!foundAngle) {
2482 return NULL;
2483 }
2484 nextStart = foundAngle->start();
2485 nextEnd = foundAngle->end();
2486 nextSegment = foundAngle->segment();
2487
2488 #if DEBUG_WINDING
2489 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2490 __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
2491 #endif
2492 return nextSegment;
2493 }
2494
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002495 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002496 const int startIndex = nextStart;
2497 const int endIndex = nextEnd;
2498 SkASSERT(startIndex != endIndex);
2499 int count = fTs.count();
2500 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2501 : startIndex > 0);
2502 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002503 int end = nextExactSpan(startIndex, step);
caryclark@google.com24bec792012-08-20 12:43:57 +00002504 SkASSERT(end >= 0);
2505 Span* endSpan = &fTs[end];
2506 Segment* other;
caryclark@google.com24bec792012-08-20 12:43:57 +00002507 if (isSimple(end)) {
2508 #if DEBUG_WINDING
2509 SkDebugf("%s simple\n", __FUNCTION__);
2510 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002511 int min = SkMin32(startIndex, endIndex);
2512 if (fTs[min].fDone) {
2513 return NULL;
2514 }
2515 markDone(min, 1);
caryclark@google.com24bec792012-08-20 12:43:57 +00002516 other = endSpan->fOther;
2517 nextStart = endSpan->fOtherIndex;
2518 double startT = other->fTs[nextStart].fT;
2519 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002520 if ((approximately_less_than_zero(startT) && step < 0)
2521 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002522 step = -step;
2523 SkDEBUGCODE(firstLoop = false;)
2524 }
2525 do {
2526 nextEnd = nextStart;
2527 do {
2528 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002529 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002530 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002531 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2532 break;
2533 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002534 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002535 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002536 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002537 SkDEBUGCODE(firstLoop = false;)
2538 step = -step;
2539 } while (true);
2540 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2541 return other;
2542 }
2543 SkTDArray<Angle> angles;
2544 SkASSERT(startIndex - endIndex != 0);
2545 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2546 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002547 buildAngles(end, angles, false);
caryclark@google.com24bec792012-08-20 12:43:57 +00002548 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002549 bool sortable = SortAngles(angles, sorted);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002550 if (!sortable) {
2551 unsortable = true;
2552 return NULL;
2553 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002554 int angleCount = angles.count();
2555 int firstIndex = findStartingEdge(sorted, startIndex, end);
2556 SkASSERT(firstIndex >= 0);
2557 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002558 debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002559 #endif
2560 SkASSERT(sorted[firstIndex]->segment() == this);
2561 int nextIndex = firstIndex + 1;
2562 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2563 const Angle* nextAngle;
2564 Segment* nextSegment;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002565 bool foundAngle = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002566 do {
2567 if (nextIndex == angleCount) {
2568 nextIndex = 0;
2569 }
2570 nextAngle = sorted[nextIndex];
2571 nextSegment = nextAngle->segment();
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002572 if (!nextSegment->done(nextAngle) || nextSegment->tiny(nextAngle)) {
2573 foundAngle = true;
caryclark@google.com24bec792012-08-20 12:43:57 +00002574 break;
2575 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002576 } while (++nextIndex != lastIndex);
2577 markDone(SkMin32(startIndex, endIndex), 1);
2578 if (!foundAngle) {
2579 nextIndex = firstIndex + 1 == angleCount ? 0 : firstIndex + 1;
2580 nextAngle = sorted[nextIndex];
2581 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002582 nextStart = nextAngle->start();
2583 nextEnd = nextAngle->end();
2584 return nextSegment;
2585 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002586
2587 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2588 int angleCount = sorted.count();
2589 int firstIndex = -1;
2590 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2591 const Angle* angle = sorted[angleIndex];
2592 if (angle->segment() == this && angle->start() == end &&
2593 angle->end() == start) {
2594 firstIndex = angleIndex;
2595 break;
2596 }
2597 }
2598 return firstIndex;
2599 }
2600
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002601 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com4eeda372012-12-06 21:47:48 +00002602 void findTooCloseToCall() {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002603 int count = fTs.count();
2604 if (count < 3) { // require t=0, x, 1 at minimum
2605 return;
2606 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002607 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002608 int moCount;
2609 Span* match;
2610 Segment* mOther;
2611 do {
2612 match = &fTs[matchIndex];
2613 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002614 // FIXME: allow quads, cubics to be near coincident?
2615 if (mOther->fVerb == SkPath::kLine_Verb) {
2616 moCount = mOther->fTs.count();
2617 if (moCount >= 3) {
2618 break;
2619 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002620 }
2621 if (++matchIndex >= count) {
2622 return;
2623 }
2624 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002625 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002626 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002627 // look for a pair of nearby T values that map to the same (x,y) value
2628 // if found, see if the pair of other segments share a common point. If
2629 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002630 for (int index = matchIndex + 1; index < count; ++index) {
2631 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002632 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002633 continue;
2634 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002635 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002636 if (tOther->fVerb != SkPath::kLine_Verb) {
2637 continue; // FIXME: allow quads, cubics to be near coincident?
2638 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002639 int toCount = tOther->fTs.count();
2640 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002641 continue;
2642 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002643 const SkPoint* testPt = &xyAtT(test);
2644 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002645 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002646 moCount = toCount;
2647 match = test;
2648 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002649 matchPt = testPt;
2650 continue;
2651 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002652 int moStart = -1;
2653 int moEnd = -1;
2654 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002655 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002656 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002657 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002658 continue;
2659 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002660 if (moSpan.fOther == this) {
2661 if (moSpan.fOtherT == match->fT) {
2662 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002663 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002664 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002665 continue;
2666 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002667 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002668 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2669 moStart = -1;
2670 break;
2671 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002672 SkASSERT(moEnd == -1);
2673 moEnd = moIndex;
2674 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002675 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002676 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002677 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002678 continue;
2679 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002680 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002681 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002682 continue;
2683 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002684 int toStart = -1;
2685 int toEnd = -1;
2686 double toStartT, toEndT;
2687 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2688 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002689 if (toSpan.fDone) {
2690 continue;
2691 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002692 if (toSpan.fOther == this) {
2693 if (toSpan.fOtherT == test->fT) {
2694 toStart = toIndex;
2695 toStartT = toSpan.fT;
2696 }
2697 continue;
2698 }
2699 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002700 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2701 moStart = -1;
2702 break;
2703 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002704 SkASSERT(toEnd == -1);
2705 toEnd = toIndex;
2706 toEndT = toSpan.fT;
2707 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002708 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002709 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2710 if (toStart <= 0 || toEnd <= 0) {
2711 continue;
2712 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002713 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002714 continue;
2715 }
2716 // test to see if the segment between there and here is linear
2717 if (!mOther->isLinear(moStart, moEnd)
2718 || !tOther->isLinear(toStart, toEnd)) {
2719 continue;
2720 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002721 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002722 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002723 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002724 } else {
caryclark@google.com4eeda372012-12-06 21:47:48 +00002725 mOther->addTCoincident(moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002726 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002727 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002728 }
2729
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002730 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002731 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002732 // a) mark spans with either end unsortable as done, or
2733 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2734 // when encountering an unsortable span
2735
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002736 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2737 // and use more concise logic like the old edge walker code?
2738 // FIXME: this needs to deal with coincident edges
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002739 Segment* findTop(int& tIndex, int& endIndex, bool& unsortable, bool onlySortable) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002740 // iterate through T intersections and return topmost
2741 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002742 SkASSERT(!done());
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002743 int firstT = -1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002744 SkPoint topPt;
2745 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002746 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002747 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002748 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002749 bool lastUnsortable = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002750 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002751 const Span& span = fTs[index];
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002752 if (onlySortable && (span.fUnsortableStart || lastUnsortable)) {
caryclark@google.comf839c032012-10-26 21:03:50 +00002753 goto next;
2754 }
2755 if (!span.fDone | !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002756 const SkPoint& intercept = xyAtT(&span);
2757 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2758 && topPt.fX > intercept.fX)) {
2759 topPt = intercept;
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002760 firstT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002761 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002762 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002763 next:
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002764 lastDone = span.fDone;
caryclark@google.comf839c032012-10-26 21:03:50 +00002765 lastUnsortable = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002766 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002767 SkASSERT(firstT >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002768 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002769 int step = 1;
2770 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002771 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002772 step = -1;
2773 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002774 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002775 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002776 // if the topmost T is not on end, or is three-way or more, find left
2777 // look for left-ness from tLeft to firstT (matching y of other)
2778 SkTDArray<Angle> angles;
2779 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002780 addTwoAngles(end, firstT, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002781 buildAngles(firstT, angles, true);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002782 SkTDArray<Angle*> sorted;
caryclark@google.comf839c032012-10-26 21:03:50 +00002783 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002784 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002785 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002786 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002787 if (onlySortable && !sortable) {
2788 unsortable = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002789 return NULL;
2790 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002791 // skip edges that have already been processed
2792 firstT = -1;
2793 Segment* leftSegment;
2794 do {
2795 const Angle* angle = sorted[++firstT];
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002796 SkASSERT(!onlySortable || !angle->unsortable());
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002797 leftSegment = angle->segment();
2798 tIndex = angle->end();
2799 endIndex = angle->start();
2800 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002801 SkASSERT(!leftSegment->fTs[SkMin32(tIndex, endIndex)].fTiny);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002802 return leftSegment;
2803 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002804
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002805 // FIXME: not crazy about this
2806 // when the intersections are performed, the other index is into an
2807 // incomplete array. as the array grows, the indices become incorrect
2808 // while the following fixes the indices up again, it isn't smart about
2809 // skipping segments whose indices are already correct
2810 // assuming we leave the code that wrote the index in the first place
2811 void fixOtherTIndex() {
2812 int iCount = fTs.count();
2813 for (int i = 0; i < iCount; ++i) {
2814 Span& iSpan = fTs[i];
2815 double oT = iSpan.fOtherT;
2816 Segment* other = iSpan.fOther;
2817 int oCount = other->fTs.count();
2818 for (int o = 0; o < oCount; ++o) {
2819 Span& oSpan = other->fTs[o];
2820 if (oT == oSpan.fT && this == oSpan.fOther) {
2821 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002822 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002823 }
2824 }
2825 }
2826 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002827
caryclark@google.com4eeda372012-12-06 21:47:48 +00002828 void init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002829 fDoneSpans = 0;
2830 fOperand = operand;
caryclark@google.com4eeda372012-12-06 21:47:48 +00002831 fXor = evenOdd;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002832 fPts = pts;
2833 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002834 }
2835
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002836 void initWinding(int start, int end, int winding, int oppWinding) {
2837 int local = spanSign(start, end);
2838 if (local * winding >= 0) {
2839 winding += local;
2840 }
2841 local = oppSign(start, end);
2842 if (local * oppWinding >= 0) {
2843 oppWinding += local;
2844 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00002845 (void) markAndChaseWinding(start, end, winding, oppWinding);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002846 }
2847
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002848 bool intersected() const {
2849 return fTs.count() > 0;
2850 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002851
2852 bool isConnected(int startIndex, int endIndex) const {
2853 return fTs[startIndex].fWindSum != SK_MinS32
2854 || fTs[endIndex].fWindSum != SK_MinS32;
2855 }
2856
caryclark@google.com235f56a2012-09-14 14:19:30 +00002857 bool isHorizontal() const {
2858 return fBounds.fTop == fBounds.fBottom;
2859 }
2860
caryclark@google.com15fa1382012-05-07 20:49:36 +00002861 bool isLinear(int start, int end) const {
2862 if (fVerb == SkPath::kLine_Verb) {
2863 return true;
2864 }
2865 if (fVerb == SkPath::kQuad_Verb) {
2866 SkPoint qPart[3];
2867 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2868 return QuadIsLinear(qPart);
2869 } else {
2870 SkASSERT(fVerb == SkPath::kCubic_Verb);
2871 SkPoint cPart[4];
2872 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2873 return CubicIsLinear(cPart);
2874 }
2875 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002876
2877 // OPTIMIZE: successive calls could start were the last leaves off
2878 // or calls could specialize to walk forwards or backwards
2879 bool isMissing(double startT) const {
2880 size_t tCount = fTs.count();
2881 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002882 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002883 return false;
2884 }
2885 }
2886 return true;
2887 }
2888
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002889 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002890 int count = fTs.count();
2891 if (count == 2) {
2892 return true;
2893 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002894 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002895 if (approximately_less_than_zero(t)) {
2896 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002897 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002898 if (approximately_greater_than_one(t)) {
2899 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002900 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002901 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002902 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002903
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002904 bool isVertical() const {
2905 return fBounds.fLeft == fBounds.fRight;
2906 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002907
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002908 SkScalar leftMost(int start, int end) const {
2909 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2910 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002911
caryclark@google.com495f8e42012-05-31 13:13:11 +00002912 // this span is excluded by the winding rule -- chase the ends
2913 // as long as they are unambiguous to mark connections as done
2914 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002915 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002916 int index = angle->start();
2917 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002918 return markAndChaseDone(index, endIndex, winding);
2919 }
2920
caryclark@google.com31143cf2012-11-09 22:14:19 +00002921 Span* markAndChaseDone(int index, int endIndex, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002922 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002923 int min = SkMin32(index, endIndex);
2924 markDone(min, winding);
2925 Span* last;
2926 Segment* other = this;
2927 while ((other = other->nextChase(index, step, min, last))) {
2928 other->markDone(min, winding);
2929 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002930 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002931 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002932
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002933 Span* markAndChaseDoneBinary(const Angle* angle, int winding, int oppWinding) {
2934 int index = angle->start();
2935 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002936 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002937 int min = SkMin32(index, endIndex);
2938 markDoneBinary(min, winding, oppWinding);
2939 Span* last;
2940 Segment* other = this;
2941 while ((other = other->nextChase(index, step, min, last))) {
2942 other->markDoneBinary(min, winding, oppWinding);
2943 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002944 return last;
2945 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002946
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002947 Span* markAndChaseDoneBinary(int index, int endIndex) {
2948 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002949 int min = SkMin32(index, endIndex);
2950 markDoneBinary(min);
2951 Span* last;
2952 Segment* other = this;
2953 while ((other = other->nextChase(index, step, min, last))) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002954 if (other->done()) {
2955 return NULL;
2956 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00002957 other->markDoneBinary(min);
2958 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002959 return last;
2960 }
2961
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002962 Span* markAndChaseDoneUnary(const Angle* angle, int winding) {
2963 int index = angle->start();
2964 int endIndex = angle->end();
2965 return markAndChaseDone(index, endIndex, winding);
2966 }
2967
caryclark@google.com4eeda372012-12-06 21:47:48 +00002968 Span* markAndChaseWinding(const Angle* angle, const int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002969 int index = angle->start();
2970 int endIndex = angle->end();
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002971 int step = SkSign32(endIndex - index);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002972 int min = SkMin32(index, endIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002973 markWinding(min, winding);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002974 Span* last;
2975 Segment* other = this;
2976 while ((other = other->nextChase(index, step, min, last))) {
2977 if (other->fTs[min].fWindSum != SK_MinS32) {
2978 SkASSERT(other->fTs[min].fWindSum == winding);
2979 return NULL;
2980 }
2981 other->markWinding(min, winding);
2982 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002983 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002984 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002985
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002986 Span* markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002987 int min = SkMin32(index, endIndex);
2988 int step = SkSign32(endIndex - index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002989 markWinding(min, winding, oppWinding);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002990 Span* last;
2991 Segment* other = this;
2992 while ((other = other->nextChase(index, step, min, last))) {
2993 if (other->fTs[min].fWindSum != SK_MinS32) {
2994 SkASSERT(other->fTs[min].fWindSum == winding);
2995 return NULL;
2996 }
2997 other->markWinding(min, winding, oppWinding);
2998 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002999 return last;
3000 }
3001
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003002 Span* markAndChaseWinding(const Angle* angle, int winding, int oppWinding) {
3003 int start = angle->start();
3004 int end = angle->end();
3005 return markAndChaseWinding(start, end, winding, oppWinding);
3006 }
3007
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003008 Span* markAngle(int maxWinding, int sumWinding, bool activeAngle, const Angle* angle) {
3009 SkASSERT(angle->segment() == this);
3010 if (useInnerWinding(maxWinding, sumWinding)) {
3011 maxWinding = sumWinding;
3012 }
3013 Span* last;
3014 if (activeAngle) {
3015 last = markAndChaseWinding(angle, maxWinding);
3016 } else {
3017 last = markAndChaseDoneUnary(angle, maxWinding);
3018 }
3019 return last;
3020 }
3021
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003022 Span* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
3023 bool activeAngle, const Angle* angle) {
3024 SkASSERT(angle->segment() == this);
3025 if (useInnerWinding(maxWinding, sumWinding)) {
3026 maxWinding = sumWinding;
3027 }
3028 if (oppMaxWinding != oppSumWinding && useInnerWinding(oppMaxWinding, oppSumWinding)) {
3029 oppMaxWinding = oppSumWinding;
3030 }
3031 Span* last;
3032 if (activeAngle) {
3033 last = markAndChaseWinding(angle, maxWinding, oppMaxWinding);
3034 } else {
3035 last = markAndChaseDoneBinary(angle, maxWinding, oppMaxWinding);
3036 }
3037 return last;
3038 }
3039
caryclark@google.com495f8e42012-05-31 13:13:11 +00003040 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003041 // This may be called when the segment is already marked done. While this
3042 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00003043 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003044 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00003045 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003046 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00003047 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003048 double referenceT = fTs[index].fT;
3049 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00003050 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3051 markOneDone(__FUNCTION__, lesser, winding);
3052 }
3053 do {
3054 markOneDone(__FUNCTION__, index, winding);
3055 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00003056 }
3057
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003058 void markDoneBinary(int index, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003059 // SkASSERT(!done());
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00003060 SkASSERT(winding || oppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003061 double referenceT = fTs[index].fT;
3062 int lesser = index;
3063 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003064 markOneDoneBinary(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003065 }
3066 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003067 markOneDoneBinary(__FUNCTION__, index, winding, oppWinding);
3068 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3069 }
3070
3071 void markDoneBinary(int index) {
3072 double referenceT = fTs[index].fT;
3073 int lesser = index;
3074 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3075 markOneDoneBinary(__FUNCTION__, lesser);
3076 }
3077 do {
3078 markOneDoneBinary(__FUNCTION__, index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003079 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003080 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003081
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003082 void markDoneUnary(int index, int winding) {
3083 // SkASSERT(!done());
3084 SkASSERT(winding);
3085 double referenceT = fTs[index].fT;
3086 int lesser = index;
3087 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3088 markOneDoneUnary(__FUNCTION__, lesser, winding);
3089 }
3090 do {
3091 markOneDoneUnary(__FUNCTION__, index, winding);
3092 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3093 }
3094
3095 void markDoneUnary(int index) {
3096 double referenceT = fTs[index].fT;
3097 int lesser = index;
3098 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3099 markOneDoneUnary(__FUNCTION__, lesser);
3100 }
3101 do {
3102 markOneDoneUnary(__FUNCTION__, index);
3103 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3104 }
3105
caryclark@google.com24bec792012-08-20 12:43:57 +00003106 void markOneDone(const char* funName, int tIndex, int winding) {
3107 Span* span = markOneWinding(funName, tIndex, winding);
3108 if (!span) {
3109 return;
3110 }
3111 span->fDone = true;
3112 fDoneSpans++;
3113 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003114
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003115 void markOneDoneBinary(const char* funName, int tIndex) {
3116 Span* span = verifyOneWinding(funName, tIndex);
3117 if (!span) {
3118 return;
3119 }
3120 span->fDone = true;
3121 fDoneSpans++;
3122 }
3123
3124 void markOneDoneBinary(const char* funName, int tIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003125 Span* span = markOneWinding(funName, tIndex, winding, oppWinding);
3126 if (!span) {
3127 return;
3128 }
3129 span->fDone = true;
3130 fDoneSpans++;
3131 }
3132
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003133 void markOneDoneUnary(const char* funName, int tIndex) {
3134 Span* span = verifyOneWindingU(funName, tIndex);
3135 if (!span) {
3136 return;
3137 }
3138 span->fDone = true;
3139 fDoneSpans++;
3140 }
3141
3142 void markOneDoneUnary(const char* funName, int tIndex, int winding) {
3143 Span* span = markOneWinding(funName, tIndex, winding);
3144 if (!span) {
3145 return;
3146 }
3147 span->fDone = true;
3148 fDoneSpans++;
3149 }
3150
caryclark@google.com24bec792012-08-20 12:43:57 +00003151 Span* markOneWinding(const char* funName, int tIndex, int winding) {
3152 Span& span = fTs[tIndex];
3153 if (span.fDone) {
3154 return NULL;
3155 }
3156 #if DEBUG_MARK_DONE
3157 debugShowNewWinding(funName, span, winding);
3158 #endif
3159 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00003160 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00003161 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00003162 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00003163 span.fWindSum = winding;
3164 return &span;
3165 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00003166
caryclark@google.com31143cf2012-11-09 22:14:19 +00003167 Span* markOneWinding(const char* funName, int tIndex, int winding, int oppWinding) {
3168 Span& span = fTs[tIndex];
3169 if (span.fDone) {
3170 return NULL;
3171 }
3172 #if DEBUG_MARK_DONE
3173 debugShowNewWinding(funName, span, winding, oppWinding);
3174 #endif
3175 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
3176 #ifdef SK_DEBUG
3177 SkASSERT(abs(winding) <= gDebugMaxWindSum);
3178 #endif
3179 span.fWindSum = winding;
3180 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
3181 #ifdef SK_DEBUG
3182 SkASSERT(abs(oppWinding) <= gDebugMaxWindSum);
3183 #endif
3184 span.fOppSum = oppWinding;
3185 return &span;
3186 }
3187
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003188 Span* verifyOneWinding(const char* funName, int tIndex) {
3189 Span& span = fTs[tIndex];
3190 if (span.fDone) {
3191 return NULL;
3192 }
3193 #if DEBUG_MARK_DONE
3194 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
3195 #endif
3196 SkASSERT(span.fWindSum != SK_MinS32);
3197 SkASSERT(span.fOppSum != SK_MinS32);
3198 return &span;
3199 }
3200
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003201 Span* verifyOneWindingU(const char* funName, int tIndex) {
3202 Span& span = fTs[tIndex];
3203 if (span.fDone) {
3204 return NULL;
3205 }
3206 #if DEBUG_MARK_DONE
3207 debugShowNewWinding(funName, span, span.fWindSum);
3208 #endif
3209 SkASSERT(span.fWindSum != SK_MinS32);
3210 return &span;
3211 }
3212
caryclark@google.comf839c032012-10-26 21:03:50 +00003213 // note that just because a span has one end that is unsortable, that's
3214 // not enough to mark it done. The other end may be sortable, allowing the
3215 // span to be added.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003216 void markUnsortable(int start, int end) {
3217 Span* span = &fTs[start];
3218 if (start < end) {
3219 span->fUnsortableStart = true;
3220 } else {
3221 --span;
3222 span->fUnsortableEnd = true;
3223 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003224 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003225 return;
3226 }
3227 span->fDone = true;
3228 fDoneSpans++;
3229 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003230
caryclark@google.com59823f72012-08-09 18:17:47 +00003231 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003232 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00003233 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003234 double referenceT = fTs[index].fT;
3235 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00003236 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3237 markOneWinding(__FUNCTION__, lesser, winding);
3238 }
3239 do {
3240 markOneWinding(__FUNCTION__, index, winding);
3241 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00003242 }
3243
3244 void markWinding(int index, int winding, int oppWinding) {
3245 // SkASSERT(!done());
caryclark@google.com4eeda372012-12-06 21:47:48 +00003246 SkASSERT(winding || oppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003247 double referenceT = fTs[index].fT;
3248 int lesser = index;
3249 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3250 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003251 }
3252 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003253 markOneWinding(__FUNCTION__, index, winding, oppWinding);
3254 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003255 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003256
caryclark@google.com2ddff932012-08-07 21:25:27 +00003257 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003258 int nextDoorWind = SK_MaxS32;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003259 int nextOppWind = SK_MaxS32;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003260 if (tIndex > 0) {
3261 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003262 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003263 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003264 nextOppWind = below.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003265 }
3266 }
3267 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
3268 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003269 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003270 nextDoorWind = above.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003271 nextOppWind = above.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003272 }
3273 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003274 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
3275 const Span& below = fTs[tIndex - 1];
3276 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003277 nextOppWind = below.fOppValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003278 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003279 if (nextDoorWind != SK_MaxS32) {
3280 Span& newSpan = fTs[tIndex];
3281 newSpan.fWindValue = nextDoorWind;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003282 newSpan.fOppValue = nextOppWind;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003283 if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003284 newSpan.fDone = true;
3285 ++fDoneSpans;
3286 }
3287 }
3288 }
3289
caryclark@google.com9764cc62012-07-12 19:29:45 +00003290 // return span if when chasing, two or more radiating spans are not done
3291 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
3292 // candidate and the remaining spans have windValue == 0 (canceled by
3293 // coincidence). The coincident edges could either be removed altogether,
3294 // or this code could be more complicated in detecting this case. Worth it?
3295 bool multipleSpans(int end) const {
3296 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003297 }
3298
caryclark@google.com4eeda372012-12-06 21:47:48 +00003299 Segment* nextChase(int& index, const int step, int& min, Span*& last) const {
3300 int end = nextExactSpan(index, step);
3301 SkASSERT(end >= 0);
3302 if (multipleSpans(end)) {
3303 last = &fTs[end];
3304 return NULL;
3305 }
3306 const Span& endSpan = fTs[end];
3307 Segment* other = endSpan.fOther;
3308 index = endSpan.fOtherIndex;
3309 int otherEnd = other->nextExactSpan(index, step);
3310 min = SkMin32(index, otherEnd);
3311 return other;
3312 }
3313
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003314 // This has callers for two different situations: one establishes the end
3315 // of the current span, and one establishes the beginning of the next span
3316 // (thus the name). When this is looking for the end of the current span,
3317 // coincidence is found when the beginning Ts contain -step and the end
3318 // contains step. When it is looking for the beginning of the next, the
3319 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003320 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003321 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003322 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00003323 int count = fTs.count();
3324 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00003325 while (step > 0 ? ++to < count : --to >= 0) {
3326 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003327 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003328 continue;
3329 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003330 return to;
3331 }
3332 return -1;
3333 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00003334
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003335 // FIXME
3336 // this returns at any difference in T, vs. a preset minimum. It may be
3337 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003338 // OPTIMIZATION splitting this into separate loops for up/down steps
3339 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003340 int nextExactSpan(int from, int step) const {
3341 const Span& fromSpan = fTs[from];
3342 int count = fTs.count();
3343 int to = from;
3344 while (step > 0 ? ++to < count : --to >= 0) {
3345 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00003346 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003347 continue;
3348 }
3349 return to;
3350 }
3351 return -1;
3352 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003353
caryclark@google.com235f56a2012-09-14 14:19:30 +00003354 bool operand() const {
3355 return fOperand;
3356 }
3357
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003358 int oppSign(const Angle* angle) const {
3359 SkASSERT(angle->segment() == this);
3360 return oppSign(angle->start(), angle->end());
3361 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003362
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003363 int oppSign(int startIndex, int endIndex) const {
3364 int result = startIndex < endIndex ? -fTs[startIndex].fOppValue
3365 : fTs[endIndex].fOppValue;
3366#if DEBUG_WIND_BUMP
3367 SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
3368#endif
3369 return result;
3370 }
3371
caryclark@google.com31143cf2012-11-09 22:14:19 +00003372 int oppSum(int tIndex) const {
3373 return fTs[tIndex].fOppSum;
3374 }
3375
3376 int oppSum(const Angle* angle) const {
3377 int lesser = SkMin32(angle->start(), angle->end());
3378 return fTs[lesser].fOppSum;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003379 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003380
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003381 int oppValue(int tIndex) const {
3382 return fTs[tIndex].fOppValue;
3383 }
3384
caryclark@google.come7bd5f42012-12-13 19:47:53 +00003385 int oppValue(const Angle* angle) const {
3386 int lesser = SkMin32(angle->start(), angle->end());
3387 return fTs[lesser].fOppValue;
3388 }
3389
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003390 const SkPoint* pts() const {
3391 return fPts;
3392 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003393
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003394 void reset() {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003395 init(NULL, (SkPath::Verb) -1, false, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003396 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
3397 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003398 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00003399
caryclark@google.com4eeda372012-12-06 21:47:48 +00003400 void setOppXor(bool isOppXor) {
3401 fOppXor = isOppXor;
3402 }
3403
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003404 void setUpWinding(int index, int endIndex, int& maxWinding, int& sumWinding) {
3405 int deltaSum = spanSign(index, endIndex);
3406 maxWinding = sumWinding;
3407 sumWinding = sumWinding -= deltaSum;
3408 }
3409
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003410 void setUpWindings(int index, int endIndex, int& sumMiWinding, int& sumSuWinding,
3411 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
3412 int deltaSum = spanSign(index, endIndex);
3413 int oppDeltaSum = oppSign(index, endIndex);
3414 if (operand()) {
3415 maxWinding = sumSuWinding;
3416 sumWinding = sumSuWinding -= deltaSum;
3417 oppMaxWinding = sumMiWinding;
3418 oppSumWinding = sumMiWinding -= oppDeltaSum;
3419 } else {
3420 maxWinding = sumMiWinding;
3421 sumWinding = sumMiWinding -= deltaSum;
3422 oppMaxWinding = sumSuWinding;
3423 oppSumWinding = sumSuWinding -= oppDeltaSum;
3424 }
3425 }
3426
caryclark@google.comf839c032012-10-26 21:03:50 +00003427 // This marks all spans unsortable so that this info is available for early
3428 // exclusion in find top and others. This could be optimized to only mark
3429 // adjacent spans that unsortable. However, this makes it difficult to later
3430 // determine starting points for edge detection in find top and the like.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003431 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003432 bool sortable = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003433 int angleCount = angles.count();
3434 int angleIndex;
3435 angleList.setReserve(angleCount);
3436 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003437 Angle& angle = angles[angleIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00003438 *angleList.append() = &angle;
3439 sortable &= !angle.unsortable();
3440 }
3441 if (sortable) {
3442 QSort<Angle>(angleList.begin(), angleList.end() - 1);
3443 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3444 if (angles[angleIndex].unsortable()) {
3445 sortable = false;
3446 break;
3447 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003448 }
3449 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003450 if (!sortable) {
3451 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3452 Angle& angle = angles[angleIndex];
3453 angle.segment()->markUnsortable(angle.start(), angle.end());
3454 }
3455 }
3456 return sortable;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003457 }
3458
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003459 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003460 const Span& span(int tIndex) const {
3461 return fTs[tIndex];
3462 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003463
caryclark@google.com235f56a2012-09-14 14:19:30 +00003464 int spanSign(const Angle* angle) const {
3465 SkASSERT(angle->segment() == this);
3466 return spanSign(angle->start(), angle->end());
3467 }
3468
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003469 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003470 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue
3471 : fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003472#if DEBUG_WIND_BUMP
3473 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
3474#endif
3475 return result;
3476 }
3477
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003478 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003479 double t(int tIndex) const {
3480 return fTs[tIndex].fT;
3481 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003482
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003483 bool tiny(const Angle* angle) const {
3484 int start = angle->start();
3485 int end = angle->end();
caryclark@google.comf839c032012-10-26 21:03:50 +00003486 const Span& mSpan = fTs[SkMin32(start, end)];
3487 return mSpan.fTiny;
3488 }
3489
caryclark@google.com18063442012-07-25 12:05:18 +00003490 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
3491 double start) {
3492 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003493 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00003494 *outsideTs.append() = end;
3495 *outsideTs.append() = start;
3496 }
3497 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00003498
caryclark@google.com24bec792012-08-20 12:43:57 +00003499 void undoneSpan(int& start, int& end) {
3500 size_t tCount = fTs.count();
3501 size_t index;
3502 for (index = 0; index < tCount; ++index) {
3503 if (!fTs[index].fDone) {
3504 break;
3505 }
3506 }
3507 SkASSERT(index < tCount - 1);
3508 start = index;
3509 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003510 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00003511 SkASSERT(index < tCount);
3512 SkASSERT(index < tCount);
3513 end = index;
3514 }
caryclark@google.com18063442012-07-25 12:05:18 +00003515
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003516 bool unsortable(int index) const {
3517 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
3518 }
3519
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003520 void updatePts(const SkPoint pts[]) {
3521 fPts = pts;
3522 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003523
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003524 int updateOppWinding(int index, int endIndex) const {
3525 int lesser = SkMin32(index, endIndex);
3526 int oppWinding = oppSum(lesser);
3527 int oppSpanWinding = oppSign(index, endIndex);
3528 if (oppSpanWinding && useInnerWinding(oppWinding - oppSpanWinding, oppWinding)) {
3529 oppWinding -= oppSpanWinding;
3530 }
3531 return oppWinding;
3532 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00003533
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003534 int updateOppWinding(const Angle* angle) const {
3535 int startIndex = angle->start();
3536 int endIndex = angle->end();
3537 return updateOppWinding(endIndex, startIndex);
3538 }
3539
3540 int updateOppWindingReverse(const Angle* angle) const {
3541 int startIndex = angle->start();
3542 int endIndex = angle->end();
3543 return updateOppWinding(startIndex, endIndex);
3544 }
3545
3546 int updateWinding(int index, int endIndex) const {
3547 int lesser = SkMin32(index, endIndex);
3548 int winding = windSum(lesser);
3549 int spanWinding = spanSign(index, endIndex);
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00003550 if (winding && useInnerWinding(winding - spanWinding, winding)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003551 winding -= spanWinding;
3552 }
3553 return winding;
3554 }
3555
3556 int updateWinding(const Angle* angle) const {
3557 int startIndex = angle->start();
3558 int endIndex = angle->end();
3559 return updateWinding(endIndex, startIndex);
3560 }
3561
3562 int updateWindingReverse(const Angle* angle) const {
3563 int startIndex = angle->start();
3564 int endIndex = angle->end();
3565 return updateWinding(startIndex, endIndex);
3566 }
3567
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003568 SkPath::Verb verb() const {
3569 return fVerb;
3570 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003571
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003572 int windingAtT(double tHit, int tIndex, bool crossOpp) const {
3573 if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
3574 return SK_MinS32;
3575 }
3576 int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
3577 SkASSERT(winding != SK_MinS32);
3578 int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
3579 #if DEBUG_WINDING
3580 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
3581 windVal);
3582 #endif
3583 // see if a + change in T results in a +/- change in X (compute x'(T))
3584 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, tHit);
3585 if (fVerb > SkPath::kLine_Verb && approximately_zero(dx)) {
3586 dx = fPts[2].fX - fPts[1].fX - dx;
3587 }
3588 #if DEBUG_WINDING
3589 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
3590 #endif
3591 SkASSERT(dx != 0);
3592 if (winding * dx > 0) { // if same signs, result is negative
3593 winding += dx > 0 ? -windVal : windVal;
3594 #if DEBUG_WINDING
3595 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
3596 #endif
3597 }
3598 return winding;
3599 }
3600
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003601 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003602 return fTs[tIndex].fWindSum;
3603 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003604
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003605 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003606 int start = angle->start();
3607 int end = angle->end();
3608 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003609 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003610 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003611
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003612 int windValue(int tIndex) const {
3613 return fTs[tIndex].fWindValue;
3614 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003615
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003616 int windValue(const Angle* angle) const {
3617 int start = angle->start();
3618 int end = angle->end();
3619 int index = SkMin32(start, end);
3620 return windValue(index);
3621 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003622
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003623 SkScalar xAtT(const Span* span) const {
3624 return xyAtT(span).fX;
3625 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003626
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003627 const SkPoint& xyAtT(int index) const {
3628 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003629 }
3630
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003631 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003632 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003633 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003634 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003635 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003636 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003637 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003638 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003639 }
3640 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00003641 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003642 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003643
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003644 SkScalar yAtT(int index) const {
3645 return yAtT(&fTs[index]);
3646 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003647
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003648 SkScalar yAtT(const Span* span) const {
3649 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003650 }
3651
caryclark@google.com4eeda372012-12-06 21:47:48 +00003652 void zeroCoincidentOpp(Span* oTest, int index) {
3653 Span* const test = &fTs[index];
3654 Span* end = test;
3655 do {
3656 end->fOppValue = 0;
3657 end = &fTs[++index];
3658 } while (approximately_negative(end->fT - test->fT));
3659 }
3660
3661 void zeroCoincidentOther(Span* test, const double tRatio, const double oEndT, int oIndex) {
3662 Span* const oTest = &fTs[oIndex];
3663 Span* oEnd = oTest;
3664 const double startT = test->fT;
3665 const double oStartT = oTest->fT;
3666 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
3667 while (!approximately_negative(oEndT - oEnd->fT)
3668 && approximately_negative(oEnd->fT - otherTMatch)) {
3669 oEnd->fOppValue = 0;
3670 oEnd = &fTs[++oIndex];
3671 }
3672 }
3673
3674 void zeroSpan(Span* span) {
3675 SkASSERT(span->fWindValue > 0 || span->fOppValue > 0);
caryclark@google.com6ec15262012-11-16 20:16:50 +00003676 span->fWindValue = 0;
caryclark@google.com729e1c42012-11-21 21:36:34 +00003677 span->fOppValue = 0;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003678 SkASSERT(!span->fDone);
3679 span->fDone = true;
3680 ++fDoneSpans;
caryclark@google.com6ec15262012-11-16 20:16:50 +00003681 }
3682
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003683#if DEBUG_DUMP
3684 void dump() const {
3685 const char className[] = "Segment";
3686 const int tab = 4;
3687 for (int i = 0; i < fTs.count(); ++i) {
3688 SkPoint out;
3689 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
3690 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003691 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003692 tab + sizeof(className), className, fID,
3693 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003694 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003695 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003696 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003697 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00003698 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003699 }
3700#endif
3701
caryclark@google.com47580692012-07-23 12:14:49 +00003702#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00003703 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003704 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00003705 for (int i = 0; i < fTs.count(); ++i) {
3706 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3707 return;
3708 }
3709 }
3710 SkASSERT(0);
3711 }
3712#endif
3713
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003714#if DEBUG_DUMP
3715 int debugID() const {
3716 return fID;
3717 }
3718#endif
3719
caryclark@google.com24bec792012-08-20 12:43:57 +00003720#if DEBUG_WINDING
3721 void debugShowSums() const {
3722 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
3723 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
3724 for (int i = 0; i < fTs.count(); ++i) {
3725 const Span& span = fTs[i];
3726 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
3727 if (span.fWindSum == SK_MinS32) {
3728 SkDebugf("?");
3729 } else {
3730 SkDebugf("%d", span.fWindSum);
3731 }
3732 SkDebugf("]");
3733 }
3734 SkDebugf("\n");
3735 }
3736#endif
3737
caryclark@google.comcc905052012-07-25 20:59:42 +00003738#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003739 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00003740 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003741 int lastWind = -1;
3742 int lastOpp = -1;
3743 double lastT = -1;
3744 int i;
3745 for (i = 0; i < fTs.count(); ++i) {
3746 bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
3747 || lastOpp != fTs[i].fOppValue;
3748 if (change && lastWind >= 0) {
3749 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3750 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3751 }
3752 if (change) {
3753 SkDebugf(" [o=%d", fTs[i].fOther->fID);
3754 lastWind = fTs[i].fWindValue;
3755 lastOpp = fTs[i].fOppValue;
3756 lastT = fTs[i].fT;
3757 } else {
3758 SkDebugf(",%d", fTs[i].fOther->fID);
3759 }
3760 }
3761 if (i <= 0) {
3762 return;
3763 }
3764 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3765 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3766 if (fOperand) {
3767 SkDebugf(" operand");
3768 }
3769 if (done()) {
3770 SkDebugf(" done");
caryclark@google.com47580692012-07-23 12:14:49 +00003771 }
3772 SkDebugf("\n");
3773 }
3774#endif
3775
caryclark@google.com027de222012-07-12 12:52:50 +00003776#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003777 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00003778 if (done()) {
3779 return;
3780 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003781#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3782 int lastId = -1;
3783 double lastT = -1;
3784#endif
caryclark@google.com027de222012-07-12 12:52:50 +00003785 for (int i = 0; i < fTs.count(); ++i) {
3786 if (fTs[i].fDone) {
3787 continue;
3788 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003789#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3790 if (lastId == fID && lastT == fTs[i].fT) {
3791 continue;
3792 }
3793 lastId = fID;
3794 lastT = fTs[i].fT;
3795#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003796 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00003797 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3798 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3799 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3800 }
3801 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00003802 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00003803 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00003804 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003805 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3806 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3807 if (fTs[i].fWindSum == SK_MinS32) {
3808 SkDebugf("?");
3809 } else {
3810 SkDebugf("%d", fTs[i].fWindSum);
3811 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003812 SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
caryclark@google.com027de222012-07-12 12:52:50 +00003813 }
3814 }
skia.committer@gmail.comb0a327e2012-11-21 02:02:25 +00003815
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003816 // This isn't useful yet -- but leaving it in for now in case i think of something
3817 // to use it for
3818 void validateActiveSpans() const {
3819 if (done()) {
3820 return;
3821 }
3822 int tCount = fTs.count();
3823 for (int index = 0; index < tCount; ++index) {
3824 if (fTs[index].fDone) {
3825 continue;
3826 }
3827 // count number of connections which are not done
3828 int first = index;
3829 double baseT = fTs[index].fT;
3830 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
3831 --first;
3832 }
3833 int last = index;
3834 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
3835 ++last;
3836 }
3837 int connections = 0;
3838 connections += first > 0 && !fTs[first - 1].fDone;
3839 for (int test = first; test <= last; ++test) {
3840 connections += !fTs[test].fDone;
3841 const Segment* other = fTs[test].fOther;
3842 int oIndex = fTs[test].fOtherIndex;
3843 connections += !other->fTs[oIndex].fDone;
3844 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3845 }
3846 // SkASSERT(!(connections & 1));
3847 }
3848 }
caryclark@google.com027de222012-07-12 12:52:50 +00003849#endif
3850
caryclark@google.com0c803d02012-08-06 11:15:47 +00003851#if DEBUG_MARK_DONE
3852 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
3853 const SkPoint& pt = xyAtT(&span);
3854 SkDebugf("%s id=%d", fun, fID);
3855 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3856 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3857 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3858 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003859 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3860 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3861 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
3862 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00003863 if (span.fWindSum == SK_MinS32) {
3864 SkDebugf("?");
3865 } else {
3866 SkDebugf("%d", span.fWindSum);
3867 }
3868 SkDebugf(" windValue=%d\n", span.fWindValue);
3869 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003870
3871 void debugShowNewWinding(const char* fun, const Span& span, int winding, int oppWinding) {
3872 const SkPoint& pt = xyAtT(&span);
3873 SkDebugf("%s id=%d", fun, fID);
3874 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3875 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3876 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3877 }
3878 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3879 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3880 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d newOppSum=%d oppSum=",
3881 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
3882 winding, oppWinding);
3883 if (span.fOppSum == SK_MinS32) {
3884 SkDebugf("?");
3885 } else {
3886 SkDebugf("%d", span.fOppSum);
3887 }
3888 SkDebugf(" windSum=");
3889 if (span.fWindSum == SK_MinS32) {
3890 SkDebugf("?");
3891 } else {
3892 SkDebugf("%d", span.fWindSum);
3893 }
3894 SkDebugf(" windValue=%d\n", span.fWindValue);
3895 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003896#endif
3897
caryclark@google.com47580692012-07-23 12:14:49 +00003898#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003899 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com31143cf2012-11-09 22:14:19 +00003900 const int contourWinding, const int oppContourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003901 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003902 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003903 int lastSum = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003904 int oppLastSum = oppContourWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003905 const Angle* firstAngle = angles[first];
3906 int windSum = lastSum - spanSign(firstAngle);
3907 int oppoSign = oppSign(firstAngle);
3908 int oppWindSum = oppLastSum - oppoSign;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003909 SkDebugf("%s %s contourWinding=%d oppContourWinding=%d sign=%d\n", fun, __FUNCTION__,
3910 contourWinding, oppContourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003911 int index = first;
3912 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003913 do {
3914 const Angle& angle = *angles[index];
3915 const Segment& segment = *angle.segment();
3916 int start = angle.start();
3917 int end = angle.end();
3918 const Span& sSpan = segment.fTs[start];
3919 const Span& eSpan = segment.fTs[end];
3920 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com31143cf2012-11-09 22:14:19 +00003921 bool opp = segment.fOperand ^ fOperand;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003922 if (!firstTime) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003923 oppoSign = segment.oppSign(&angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003924 if (opp) {
3925 oppLastSum = oppWindSum;
3926 oppWindSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003927 if (oppoSign) {
3928 lastSum = windSum;
3929 windSum -= oppoSign;
3930 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003931 } else {
3932 lastSum = windSum;
3933 windSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003934 if (oppoSign) {
3935 oppLastSum = oppWindSum;
3936 oppWindSum -= oppoSign;
3937 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003938 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00003939 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003940 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 +00003941 " sign=%d windValue=%d windSum=",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003942 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003943 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003944 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
3945 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
3946 mSpan.fWindValue);
3947 if (mSpan.fWindSum == SK_MinS32) {
3948 SkDebugf("?");
3949 } else {
3950 SkDebugf("%d", mSpan.fWindSum);
3951 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003952 int last, wind;
3953 if (opp) {
3954 last = oppLastSum;
3955 wind = oppWindSum;
3956 } else {
3957 last = lastSum;
3958 wind = windSum;
3959 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003960 if (!oppoSign) {
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003961 SkDebugf(" %d->%d (max=%d)", last, wind,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003962 useInnerWinding(last, wind) ? wind : last);
3963 } else {
3964 SkDebugf(" %d->%d (%d->%d)", last, wind, opp ? lastSum : oppLastSum,
3965 opp ? windSum : oppWindSum);
3966 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003967 SkDebugf(" done=%d tiny=%d opp=%d\n", mSpan.fDone, mSpan.fTiny, opp);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003968#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00003969 angle.debugShow(segment.xyAtT(&sSpan));
3970#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003971 ++index;
3972 if (index == angles.count()) {
3973 index = 0;
3974 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003975 if (firstTime) {
3976 firstTime = false;
3977 }
caryclark@google.com47580692012-07-23 12:14:49 +00003978 } while (index != first);
3979 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003980
3981 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first) {
3982 const Angle* firstAngle = angles[first];
3983 const Segment* segment = firstAngle->segment();
3984 int winding = segment->updateWinding(firstAngle);
3985 int oppWinding = segment->updateOppWinding(firstAngle);
3986 debugShowSort(fun, angles, first, winding, oppWinding);
3987 }
3988
caryclark@google.com47580692012-07-23 12:14:49 +00003989#endif
3990
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003991#if DEBUG_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003992 static char as_digit(int value) {
3993 return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
3994 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00003995#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003996
caryclark@google.com729e1c42012-11-21 21:36:34 +00003997#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003998 int debugShowWindingValues(int slotCount, int ofInterest) const {
3999 if (!(1 << fID & ofInterest)) {
4000 return 0;
4001 }
4002 int sum = 0;
4003 SkTDArray<char> slots;
4004 slots.setCount(slotCount * 2);
4005 memset(slots.begin(), ' ', slotCount * 2);
4006 for (int i = 0; i < fTs.count(); ++i) {
4007 // if (!(1 << fTs[i].fOther->fID & ofInterest)) {
4008 // continue;
4009 // }
4010 sum += fTs[i].fWindValue;
4011 slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
4012 sum += fTs[i].fOppValue;
4013 slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
4014 }
4015 SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
4016 slots.begin() + slotCount);
4017 return sum;
4018 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00004019#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004020
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004021private:
4022 const SkPoint* fPts;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004023 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004024 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com4eeda372012-12-06 21:47:48 +00004025 // OPTIMIZATION: could pack donespans, verb, operand, xor into 1 int-sized value
caryclark@google.com24bec792012-08-20 12:43:57 +00004026 int fDoneSpans; // quick check that segment is finished
caryclark@google.com4eeda372012-12-06 21:47:48 +00004027 // OPTIMIZATION: force the following to be byte-sized
4028 SkPath::Verb fVerb;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004029 bool fOperand;
caryclark@google.com4eeda372012-12-06 21:47:48 +00004030 bool fXor; // set if original contour had even-odd fill
4031 bool fOppXor; // set if opposite operand had even-odd fill
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004032#if DEBUG_DUMP
4033 int fID;
4034#endif
4035};
4036
caryclark@google.comb9738012012-07-03 19:53:30 +00004037class Contour;
4038
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004039struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00004040 Contour* fContours[2];
4041 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004042 double fTs[2][2];
4043};
4044
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004045class Contour {
4046public:
4047 Contour() {
4048 reset();
4049#if DEBUG_DUMP
4050 fID = ++gContourID;
4051#endif
4052 }
4053
4054 bool operator<(const Contour& rh) const {
4055 return fBounds.fTop == rh.fBounds.fTop
4056 ? fBounds.fLeft < rh.fBounds.fLeft
4057 : fBounds.fTop < rh.fBounds.fTop;
4058 }
4059
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004060 void addCoincident(int index, Contour* other, int otherIndex,
4061 const Intersections& ts, bool swap) {
4062 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004063 coincidence.fContours[0] = this; // FIXME: no need to store
caryclark@google.comb9738012012-07-03 19:53:30 +00004064 coincidence.fContours[1] = other;
4065 coincidence.fSegments[0] = index;
4066 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00004067 if (fSegments[index].verb() == SkPath::kLine_Verb &&
4068 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
4069 // FIXME: coincident lines use legacy Ts instead of coincident Ts
4070 coincidence.fTs[swap][0] = ts.fT[0][0];
4071 coincidence.fTs[swap][1] = ts.fT[0][1];
4072 coincidence.fTs[!swap][0] = ts.fT[1][0];
4073 coincidence.fTs[!swap][1] = ts.fT[1][1];
4074 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
4075 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
4076 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
4077 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
4078 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
4079 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
4080 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004081 }
4082
4083 void addCross(const Contour* crosser) {
4084#ifdef DEBUG_CROSS
4085 for (int index = 0; index < fCrosses.count(); ++index) {
4086 SkASSERT(fCrosses[index] != crosser);
4087 }
4088#endif
4089 *fCrosses.append() = crosser;
4090 }
4091
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004092 void addCubic(const SkPoint pts[4]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004093 fSegments.push_back().addCubic(pts, fOperand, fXor);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004094 fContainsCurves = true;
4095 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004096
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004097 int addLine(const SkPoint pts[2]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004098 fSegments.push_back().addLine(pts, fOperand, fXor);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004099 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004100 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004101
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004102 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
4103 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
4104 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004105
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004106 int addQuad(const SkPoint pts[3]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004107 fSegments.push_back().addQuad(pts, fOperand, fXor);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004108 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004109 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004110 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004111
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004112 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
4113 containsIntercepts();
4114 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
4115 }
4116
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004117 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004118 return fBounds;
4119 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004120
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004121 void complete() {
4122 setBounds();
4123 fContainsIntercepts = false;
4124 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004125
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004126 void containsIntercepts() {
4127 fContainsIntercepts = true;
4128 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004129
caryclark@google.come7bd5f42012-12-13 19:47:53 +00004130 const Segment* crossedSegmentX(const SkPoint& basePt, SkScalar& bestX,
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00004131 int& tIndex, double& hitT, bool opp) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00004132 int segmentCount = fSegments.count();
4133 const Segment* bestSegment = NULL;
4134 for (int test = 0; test < segmentCount; ++test) {
4135 Segment* testSegment = &fSegments[test];
4136 const SkRect& bounds = testSegment->bounds();
4137 if (bounds.fRight <= bestX) {
4138 continue;
4139 }
4140 if (bounds.fLeft >= basePt.fX) {
4141 continue;
4142 }
4143 if (bounds.fTop > basePt.fY) {
4144 continue;
4145 }
4146 if (bounds.fBottom < basePt.fY) {
4147 continue;
4148 }
4149 if (bounds.fTop == bounds.fBottom) {
4150 continue;
4151 }
4152 double testHitT;
4153 int testT = testSegment->crossedSpanX(basePt, bestX, testHitT, opp);
4154 if (testT >= 0) {
4155 bestSegment = testSegment;
4156 tIndex = testT;
4157 hitT = testHitT;
4158 }
4159 }
4160 return bestSegment;
4161 }
4162
4163 const Segment* crossedSegmentY(const SkPoint& basePt, SkScalar& bestY,
4164 int &tIndex, double& hitT, bool opp) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004165 int segmentCount = fSegments.count();
4166 const Segment* bestSegment = NULL;
4167 for (int test = 0; test < segmentCount; ++test) {
4168 Segment* testSegment = &fSegments[test];
4169 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004170 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004171 continue;
4172 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004173 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004174 continue;
4175 }
4176 if (bounds.fLeft > basePt.fX) {
4177 continue;
4178 }
4179 if (bounds.fRight < basePt.fX) {
4180 continue;
4181 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004182 if (bounds.fLeft == bounds.fRight) {
4183 continue;
4184 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004185 double testHitT;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00004186 int testT = testSegment->crossedSpanY(basePt, bestY, testHitT, opp);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004187 if (testT >= 0) {
4188 bestSegment = testSegment;
4189 tIndex = testT;
4190 hitT = testHitT;
4191 }
4192 }
4193 return bestSegment;
4194 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004195
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004196 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004197 for (int index = 0; index < fCrosses.count(); ++index) {
4198 if (fCrosses[index] == crosser) {
4199 return true;
4200 }
4201 }
4202 return false;
4203 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00004204
caryclark@google.comf839c032012-10-26 21:03:50 +00004205 const SkPoint& end() const {
4206 const Segment& segment = fSegments.back();
4207 return segment.pts()[segment.verb()];
4208 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004209
caryclark@google.com4eeda372012-12-06 21:47:48 +00004210 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004211 int segmentCount = fSegments.count();
4212 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004213 fSegments[sIndex].findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004214 }
4215 }
4216
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004217 void fixOtherTIndex() {
4218 int segmentCount = fSegments.count();
4219 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
4220 fSegments[sIndex].fixOtherTIndex();
4221 }
4222 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00004223
caryclark@google.com31143cf2012-11-09 22:14:19 +00004224 bool operand() const {
4225 return fOperand;
4226 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004227
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004228 void reset() {
4229 fSegments.reset();
4230 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00004231 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004232 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004233
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004234 void resolveCoincidence(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004235 int count = fCoincidences.count();
4236 for (int index = 0; index < count; ++index) {
4237 Coincidence& coincidence = fCoincidences[index];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004238 SkASSERT(coincidence.fContours[0] == this);
caryclark@google.comb9738012012-07-03 19:53:30 +00004239 int thisIndex = coincidence.fSegments[0];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004240 Segment& thisOne = fSegments[thisIndex];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004241 Contour* otherContour = coincidence.fContours[1];
caryclark@google.comb9738012012-07-03 19:53:30 +00004242 int otherIndex = coincidence.fSegments[1];
caryclark@google.comb9738012012-07-03 19:53:30 +00004243 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00004244 if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004245 continue;
4246 }
caryclark@google.com47580692012-07-23 12:14:49 +00004247 #if DEBUG_CONCIDENT
4248 thisOne.debugShowTs();
4249 other.debugShowTs();
4250 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004251 double startT = coincidence.fTs[0][0];
4252 double endT = coincidence.fTs[0][1];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004253 bool cancelers = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004254 if (startT > endT) {
4255 SkTSwap<double>(startT, endT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004256 cancelers ^= true; // FIXME: just assign true
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004257 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004258 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004259 double oStartT = coincidence.fTs[1][0];
4260 double oEndT = coincidence.fTs[1][1];
4261 if (oStartT > oEndT) {
4262 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004263 cancelers ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004264 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004265 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00004266 bool opp = fOperand ^ otherContour->fOperand;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004267 if (cancelers && !opp) {
4268 // make sure startT and endT have t entries
caryclark@google.com2ddff932012-08-07 21:25:27 +00004269 if (startT > 0 || oEndT < 1
4270 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
4271 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004272 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00004273 if (oStartT > 0 || endT < 1
4274 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
4275 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004276 }
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00004277 if (!thisOne.done() && !other.done()) {
4278 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
4279 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004280 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004281 if (startT > 0 || oStartT > 0
4282 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00004283 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004284 }
caryclark@google.com200c2112012-08-03 15:05:04 +00004285 if (endT < 1 || oEndT < 1
4286 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00004287 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004288 }
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00004289 if (!thisOne.done() && !other.done()) {
4290 thisOne.addTCoincident(startT, endT, other, oStartT, oEndT);
4291 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004292 }
caryclark@google.com47580692012-07-23 12:14:49 +00004293 #if DEBUG_CONCIDENT
4294 thisOne.debugShowTs();
4295 other.debugShowTs();
4296 #endif
caryclark@google.com729e1c42012-11-21 21:36:34 +00004297 #if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004298 debugShowWindingValues(contourList);
4299 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004300 }
4301 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004302
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004303 const SkTArray<Segment>& segments() {
4304 return fSegments;
4305 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004306
caryclark@google.com235f56a2012-09-14 14:19:30 +00004307 void setOperand(bool isOp) {
4308 fOperand = isOp;
4309 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00004310
caryclark@google.com4eeda372012-12-06 21:47:48 +00004311 void setOppXor(bool isOppXor) {
4312 fOppXor = isOppXor;
4313 int segmentCount = fSegments.count();
4314 for (int test = 0; test < segmentCount; ++test) {
4315 fSegments[test].setOppXor(isOppXor);
4316 }
4317 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004318
caryclark@google.com235f56a2012-09-14 14:19:30 +00004319 void setXor(bool isXor) {
4320 fXor = isXor;
4321 }
4322
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004323 void sortSegments() {
4324 int segmentCount = fSegments.count();
4325 fSortedSegments.setReserve(segmentCount);
4326 for (int test = 0; test < segmentCount; ++test) {
4327 *fSortedSegments.append() = &fSegments[test];
4328 }
4329 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
4330 fFirstSorted = 0;
4331 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004332
caryclark@google.comf839c032012-10-26 21:03:50 +00004333 const SkPoint& start() const {
4334 return fSegments.front().pts()[0];
4335 }
4336
4337 void toPath(PathWrapper& path) const {
4338 int segmentCount = fSegments.count();
4339 const SkPoint& pt = fSegments.front().pts()[0];
4340 path.deferredMove(pt);
4341 for (int test = 0; test < segmentCount; ++test) {
4342 fSegments[test].addCurveTo(0, 1, path, true);
4343 }
4344 path.close();
4345 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00004346
caryclark@google.comf839c032012-10-26 21:03:50 +00004347 void toPartialBackward(PathWrapper& path) const {
4348 int segmentCount = fSegments.count();
4349 for (int test = segmentCount - 1; test >= 0; --test) {
4350 fSegments[test].addCurveTo(1, 0, path, true);
4351 }
4352 }
4353
4354 void toPartialForward(PathWrapper& path) const {
4355 int segmentCount = fSegments.count();
4356 for (int test = 0; test < segmentCount; ++test) {
4357 fSegments[test].addCurveTo(0, 1, path, true);
4358 }
4359 }
4360
4361#if 0 // FIXME: obsolete, remove
caryclark@google.com15fa1382012-05-07 20:49:36 +00004362 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
4363 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00004364 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00004365 // segments' top, and not a true sort, so it could be ameniable to regular
4366 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004367 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00004368 int segmentCount = fSegments.count();
4369 SkASSERT(segmentCount > 0);
4370 int best = -1;
4371 Segment* bestSegment = NULL;
4372 while (++best < segmentCount) {
4373 Segment* testSegment = &fSegments[best];
4374 if (testSegment->done()) {
4375 continue;
4376 }
4377 bestSegment = testSegment;
4378 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004379 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004380 if (!bestSegment) {
4381 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004382 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004383 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00004384 for (int test = best + 1; test < segmentCount; ++test) {
4385 Segment* testSegment = &fSegments[test];
4386 if (testSegment->done()) {
4387 continue;
4388 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004389 if (testSegment->bounds().fTop > bestTop) {
4390 continue;
4391 }
4392 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00004393 if (bestTop > testTop) {
4394 bestTop = testTop;
4395 bestSegment = testSegment;
4396 }
4397 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004398 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004399 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004400 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004401#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004402
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00004403 Segment* topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004404 int segmentCount = fSortedSegments.count();
4405 SkASSERT(segmentCount > 0);
4406 Segment* bestSegment = NULL;
caryclark@google.comf839c032012-10-26 21:03:50 +00004407 int sortedIndex = fFirstSorted;
4408 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
4409 Segment* testSegment = fSortedSegments[sortedIndex];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004410 if (testSegment->done()) {
caryclark@google.comf839c032012-10-26 21:03:50 +00004411 if (sortedIndex == fFirstSorted) {
4412 ++fFirstSorted;
4413 }
4414 continue;
4415 }
4416 SkPoint testXY;
4417 testSegment->activeLeftTop(testXY);
4418 if (testXY.fY < topLeft.fY) {
4419 continue;
4420 }
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00004421 if (testXY.fY == topLeft.fY && testXY.fX <= topLeft.fX) {
caryclark@google.comf839c032012-10-26 21:03:50 +00004422 continue;
4423 }
4424 if (bestXY.fY < testXY.fY) {
4425 continue;
4426 }
4427 if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004428 continue;
4429 }
4430 bestSegment = testSegment;
caryclark@google.comf839c032012-10-26 21:03:50 +00004431 bestXY = testXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004432 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004433 return bestSegment;
4434 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004435
caryclark@google.com24bec792012-08-20 12:43:57 +00004436 Segment* undoneSegment(int& start, int& end) {
4437 int segmentCount = fSegments.count();
4438 for (int test = 0; test < segmentCount; ++test) {
4439 Segment* testSegment = &fSegments[test];
4440 if (testSegment->done()) {
4441 continue;
4442 }
4443 testSegment->undoneSpan(start, end);
4444 return testSegment;
4445 }
4446 return NULL;
4447 }
4448
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004449 int updateSegment(int index, const SkPoint* pts) {
4450 Segment& segment = fSegments[index];
4451 segment.updatePts(pts);
4452 return segment.verb() + 1;
4453 }
4454
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004455#if DEBUG_TEST
4456 SkTArray<Segment>& debugSegments() {
4457 return fSegments;
4458 }
4459#endif
4460
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004461#if DEBUG_DUMP
4462 void dump() {
4463 int i;
4464 const char className[] = "Contour";
4465 const int tab = 4;
4466 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
4467 for (i = 0; i < fSegments.count(); ++i) {
4468 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
4469 className, i);
4470 fSegments[i].dump();
4471 }
4472 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
4473 tab + sizeof(className), className,
4474 fBounds.fLeft, fBounds.fTop,
4475 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004476 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
4477 className, fContainsIntercepts);
4478 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
4479 className, fContainsCurves);
4480 }
4481#endif
4482
caryclark@google.com027de222012-07-12 12:52:50 +00004483#if DEBUG_ACTIVE_SPANS
4484 void debugShowActiveSpans() {
4485 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004486 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00004487 }
4488 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004489
4490 void validateActiveSpans() {
4491 for (int index = 0; index < fSegments.count(); ++index) {
4492 fSegments[index].validateActiveSpans();
4493 }
4494 }
caryclark@google.com027de222012-07-12 12:52:50 +00004495#endif
4496
caryclark@google.com729e1c42012-11-21 21:36:34 +00004497#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004498 int debugShowWindingValues(int totalSegments, int ofInterest) {
4499 int count = fSegments.count();
4500 int sum = 0;
4501 for (int index = 0; index < count; ++index) {
4502 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
4503 }
4504 // SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
4505 return sum;
4506 }
4507
4508 static void debugShowWindingValues(SkTDArray<Contour*>& contourList) {
4509 // int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
4510 // int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
4511 int ofInterest = 1 << 5 | 1 << 8;
4512 int total = 0;
4513 int index;
4514 for (index = 0; index < contourList.count(); ++index) {
4515 total += contourList[index]->segments().count();
4516 }
4517 int sum = 0;
4518 for (index = 0; index < contourList.count(); ++index) {
4519 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
4520 }
4521 // SkDebugf("%s total=%d\n", __FUNCTION__, sum);
4522 }
4523#endif
4524
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004525protected:
4526 void setBounds() {
4527 int count = fSegments.count();
4528 if (count == 0) {
4529 SkDebugf("%s empty contour\n", __FUNCTION__);
4530 SkASSERT(0);
4531 // FIXME: delete empty contour?
4532 return;
4533 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004534 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004535 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004536 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004537 }
4538 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004539
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004540private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004541 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004542 SkTDArray<Segment*> fSortedSegments;
4543 int fFirstSorted;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004544 SkTDArray<Coincidence> fCoincidences;
4545 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004546 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004547 bool fContainsIntercepts;
4548 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004549 bool fOperand; // true for the second argument to a binary operator
4550 bool fXor;
caryclark@google.com4eeda372012-12-06 21:47:48 +00004551 bool fOppXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004552#if DEBUG_DUMP
4553 int fID;
4554#endif
4555};
4556
4557class EdgeBuilder {
4558public:
4559
caryclark@google.comf839c032012-10-26 21:03:50 +00004560EdgeBuilder(const PathWrapper& path, SkTArray<Contour>& contours)
4561 : fPath(path.nativePath())
4562 , fContours(contours)
4563{
4564 init();
4565}
4566
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004567EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00004568 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004569 , fContours(contours)
4570{
caryclark@google.comf839c032012-10-26 21:03:50 +00004571 init();
4572}
4573
4574void init() {
4575 fCurrentContour = NULL;
4576 fOperand = false;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004577 fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004578#if DEBUG_DUMP
4579 gContourID = 0;
4580 gSegmentID = 0;
4581#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00004582 fSecondHalf = preFetch();
4583}
4584
4585void addOperand(const SkPath& path) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004586 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
4587 fPathVerbs.pop();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004588 fPath = &path;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004589 fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004590 preFetch();
4591}
4592
4593void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004594 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004595 complete();
4596 if (fCurrentContour && !fCurrentContour->segments().count()) {
4597 fContours.pop_back();
4598 }
4599 // correct pointers in contours since fReducePts may have moved as it grew
4600 int cIndex = 0;
4601 int extraCount = fExtra.count();
4602 SkASSERT(extraCount == 0 || fExtra[0] == -1);
4603 int eIndex = 0;
4604 int rIndex = 0;
4605 while (++eIndex < extraCount) {
4606 int offset = fExtra[eIndex];
4607 if (offset < 0) {
4608 ++cIndex;
4609 continue;
4610 }
4611 fCurrentContour = &fContours[cIndex];
4612 rIndex += fCurrentContour->updateSegment(offset - 1,
4613 &fReducePts[rIndex]);
4614 }
4615 fExtra.reset(); // we're done with this
4616}
4617
4618ShapeOpMask xorMask() const {
caryclark@google.com729e1c42012-11-21 21:36:34 +00004619 return fXorMask[fOperand];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004620}
4621
4622protected:
4623
4624void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004625 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004626 fCurrentContour->complete();
4627 fCurrentContour = NULL;
4628 }
4629}
4630
caryclark@google.com235f56a2012-09-14 14:19:30 +00004631// FIXME:remove once we can access path pts directly
4632int preFetch() {
4633 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004634 SkPoint pts[4];
4635 SkPath::Verb verb;
4636 do {
4637 verb = iter.next(pts);
4638 *fPathVerbs.append() = verb;
4639 if (verb == SkPath::kMove_Verb) {
4640 *fPathPts.append() = pts[0];
4641 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
4642 fPathPts.append(verb, &pts[1]);
4643 }
4644 } while (verb != SkPath::kDone_Verb);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004645 return fPathVerbs.count() - 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004646}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004647
caryclark@google.com235f56a2012-09-14 14:19:30 +00004648void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004649 SkPath::Verb reducedVerb;
4650 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004651 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004652 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004653 const SkPoint* finalCurveStart = NULL;
4654 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004655 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004656 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
4657 switch (verb) {
4658 case SkPath::kMove_Verb:
4659 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004660 if (!fCurrentContour) {
4661 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004662 fCurrentContour->setOperand(fOperand);
caryclark@google.com729e1c42012-11-21 21:36:34 +00004663 fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004664 *fExtra.append() = -1; // start new contour
4665 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004666 finalCurveEnd = pointsPtr++;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004667 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004668 case SkPath::kLine_Verb:
4669 // skip degenerate points
4670 if (pointsPtr[-1].fX != pointsPtr[0].fX
4671 || pointsPtr[-1].fY != pointsPtr[0].fY) {
4672 fCurrentContour->addLine(&pointsPtr[-1]);
4673 }
4674 break;
4675 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00004676
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004677 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
4678 if (reducedVerb == 0) {
4679 break; // skip degenerate points
4680 }
4681 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00004682 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004683 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004684 break;
4685 }
4686 fCurrentContour->addQuad(&pointsPtr[-1]);
4687 break;
4688 case SkPath::kCubic_Verb:
4689 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
4690 if (reducedVerb == 0) {
4691 break; // skip degenerate points
4692 }
4693 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004694 *fExtra.append() =
4695 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004696 break;
4697 }
4698 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004699 *fExtra.append() =
4700 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004701 break;
4702 }
4703 fCurrentContour->addCubic(&pointsPtr[-1]);
4704 break;
4705 case SkPath::kClose_Verb:
4706 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004707 if (finalCurveStart && finalCurveEnd
4708 && *finalCurveStart != *finalCurveEnd) {
4709 *fReducePts.append() = *finalCurveStart;
4710 *fReducePts.append() = *finalCurveEnd;
4711 *fExtra.append() =
4712 fCurrentContour->addLine(fReducePts.end() - 2);
4713 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004714 complete();
caryclark@google.com31143cf2012-11-09 22:14:19 +00004715 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004716 default:
4717 SkDEBUGFAIL("bad verb");
4718 return;
4719 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004720 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004721 pointsPtr += verb;
4722 SkASSERT(fCurrentContour);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004723 nextVerb:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004724 if (verbPtr == endOfFirstHalf) {
4725 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004726 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004727 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004728}
4729
4730private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004731 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004732 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004733 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004734 Contour* fCurrentContour;
4735 SkTArray<Contour>& fContours;
4736 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004737 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com729e1c42012-11-21 21:36:34 +00004738 ShapeOpMask fXorMask[2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004739 int fSecondHalf;
4740 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004741};
4742
4743class Work {
4744public:
4745 enum SegmentType {
4746 kHorizontalLine_Segment = -1,
4747 kVerticalLine_Segment = 0,
4748 kLine_Segment = SkPath::kLine_Verb,
4749 kQuad_Segment = SkPath::kQuad_Verb,
4750 kCubic_Segment = SkPath::kCubic_Verb,
4751 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00004752
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004753 void addCoincident(Work& other, const Intersections& ts, bool swap) {
4754 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
4755 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004756
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004757 // FIXME: does it make sense to write otherIndex now if we're going to
4758 // fix it up later?
4759 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004760 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004761 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004762
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004763 // Avoid collapsing t values that are close to the same since
4764 // we walk ts to describe consecutive intersections. Since a pair of ts can
4765 // be nearly equal, any problems caused by this should be taken care
4766 // of later.
4767 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004768 int addT(double newT, const Work& other) {
4769 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004770 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004771
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004772 bool advance() {
4773 return ++fIndex < fLast;
4774 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004775
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004776 SkScalar bottom() const {
4777 return bounds().fBottom;
4778 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004779
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004780 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004781 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004782 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004783
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004784 const SkPoint* cubic() const {
4785 return fCubic;
4786 }
4787
4788 void init(Contour* contour) {
4789 fContour = contour;
4790 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004791 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004792 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004793
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004794 bool isAdjacent(const Work& next) {
4795 return fContour == next.fContour && fIndex + 1 == next.fIndex;
4796 }
4797
4798 bool isFirstLast(const Work& next) {
4799 return fContour == next.fContour && fIndex == 0
4800 && next.fIndex == fLast - 1;
4801 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004802
4803 SkScalar left() const {
4804 return bounds().fLeft;
4805 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004806
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004807 void promoteToCubic() {
4808 fCubic[0] = pts()[0];
4809 fCubic[2] = pts()[1];
4810 fCubic[3] = pts()[2];
4811 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
4812 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
4813 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
4814 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
4815 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004816
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004817 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004818 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004819 }
4820
4821 SkScalar right() const {
4822 return bounds().fRight;
4823 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004824
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004825 ptrdiff_t segmentIndex() const {
4826 return fIndex;
4827 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004828
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004829 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004830 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004831 SegmentType type = (SegmentType) segment.verb();
4832 if (type != kLine_Segment) {
4833 return type;
4834 }
4835 if (segment.isHorizontal()) {
4836 return kHorizontalLine_Segment;
4837 }
4838 if (segment.isVertical()) {
4839 return kVerticalLine_Segment;
4840 }
4841 return kLine_Segment;
4842 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004843
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004844 bool startAfter(const Work& after) {
4845 fIndex = after.fIndex;
4846 return advance();
4847 }
4848
4849 SkScalar top() const {
4850 return bounds().fTop;
4851 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004852
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004853 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004854 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004855 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004856
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004857 SkScalar x() const {
4858 return bounds().fLeft;
4859 }
4860
4861 bool xFlipped() const {
4862 return x() != pts()[0].fX;
4863 }
4864
4865 SkScalar y() const {
4866 return bounds().fTop;
4867 }
4868
4869 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004870 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004871 }
4872
4873protected:
4874 Contour* fContour;
4875 SkPoint fCubic[4];
4876 int fIndex;
4877 int fLast;
4878};
4879
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004880#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004881static void debugShowLineIntersection(int pts, const Work& wt,
4882 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004883 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004884 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004885 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
4886 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4887 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
4888 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004889 return;
4890 }
4891 SkPoint wtOutPt, wnOutPt;
4892 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4893 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004894 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 +00004895 __FUNCTION__,
4896 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4897 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
4898 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004899 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004900 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00004901 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004902 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4903 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4904 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004905 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
4906 }
4907 SkDebugf("\n");
4908}
4909
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004910static void debugShowQuadLineIntersection(int pts, const Work& wt,
4911 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4912 if (!pts) {
4913 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
caryclark@google.com0b7da432012-10-31 19:00:20 +00004914 " (%1.9g,%1.9g %1.9g,%1.9g)\n",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004915 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4916 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004917 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004918 return;
4919 }
4920 SkPoint wtOutPt, wnOutPt;
4921 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4922 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4923 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4924 __FUNCTION__,
4925 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4926 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4927 wtOutPt.fX, wtOutPt.fY);
4928 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004929 QuadXYAtT(wt.pts(), wtTs[1], &wtOutPt);
4930 SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004931 }
4932 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4933 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4934 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4935 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004936 LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
4937 SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004938 }
4939 SkDebugf("\n");
4940}
4941
caryclark@google.coma461ff02012-10-11 12:54:23 +00004942static void debugShowQuadIntersection(int pts, const Work& wt,
4943 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4944 if (!pts) {
4945 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
4946 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
4947 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00004948 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00004949 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004950 wn.pts()[2].fX, wn.pts()[2].fY );
caryclark@google.coma461ff02012-10-11 12:54:23 +00004951 return;
4952 }
4953 SkPoint wtOutPt, wnOutPt;
4954 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4955 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4956 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4957 __FUNCTION__,
4958 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4959 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4960 wtOutPt.fX, wtOutPt.fY);
4961 if (pts == 2) {
4962 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
4963 }
4964 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4965 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4966 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
4967 wnOutPt.fX, wnOutPt.fY);
4968 if (pts == 2) {
4969 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004970 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004971 SkDebugf("\n");
4972}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004973#else
4974static void debugShowLineIntersection(int , const Work& ,
4975 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004976}
caryclark@google.coma461ff02012-10-11 12:54:23 +00004977
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004978static void debugShowQuadLineIntersection(int , const Work& ,
4979 const Work& , const double [2], const double [2]) {
4980}
4981
caryclark@google.coma461ff02012-10-11 12:54:23 +00004982static void debugShowQuadIntersection(int , const Work& ,
4983 const Work& , const double [2], const double [2]) {
4984}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004985#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004986
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004987static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004988
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004989 if (test != next) {
4990 if (test->bounds().fBottom < next->bounds().fTop) {
4991 return false;
4992 }
4993 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
4994 return true;
4995 }
4996 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004997 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004998 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004999 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005000 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005001 Work wn;
5002 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005003 if (test == next && !wn.startAfter(wt)) {
5004 continue;
5005 }
5006 do {
5007 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
5008 continue;
5009 }
5010 int pts;
5011 Intersections ts;
5012 bool swap = false;
5013 switch (wt.segmentType()) {
5014 case Work::kHorizontalLine_Segment:
5015 swap = true;
5016 switch (wn.segmentType()) {
5017 case Work::kHorizontalLine_Segment:
5018 case Work::kVerticalLine_Segment:
5019 case Work::kLine_Segment: {
5020 pts = HLineIntersect(wn.pts(), wt.left(),
5021 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005022 debugShowLineIntersection(pts, wt, wn,
5023 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005024 break;
5025 }
5026 case Work::kQuad_Segment: {
5027 pts = HQuadIntersect(wn.pts(), wt.left(),
5028 wt.right(), wt.y(), wt.xFlipped(), ts);
5029 break;
5030 }
5031 case Work::kCubic_Segment: {
5032 pts = HCubicIntersect(wn.pts(), wt.left(),
5033 wt.right(), wt.y(), wt.xFlipped(), ts);
5034 break;
5035 }
5036 default:
5037 SkASSERT(0);
5038 }
5039 break;
5040 case Work::kVerticalLine_Segment:
5041 swap = true;
5042 switch (wn.segmentType()) {
5043 case Work::kHorizontalLine_Segment:
5044 case Work::kVerticalLine_Segment:
5045 case Work::kLine_Segment: {
5046 pts = VLineIntersect(wn.pts(), wt.top(),
5047 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005048 debugShowLineIntersection(pts, wt, wn,
5049 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005050 break;
5051 }
5052 case Work::kQuad_Segment: {
5053 pts = VQuadIntersect(wn.pts(), wt.top(),
5054 wt.bottom(), wt.x(), wt.yFlipped(), ts);
5055 break;
5056 }
5057 case Work::kCubic_Segment: {
5058 pts = VCubicIntersect(wn.pts(), wt.top(),
5059 wt.bottom(), wt.x(), wt.yFlipped(), ts);
5060 break;
5061 }
5062 default:
5063 SkASSERT(0);
5064 }
5065 break;
5066 case Work::kLine_Segment:
5067 switch (wn.segmentType()) {
5068 case Work::kHorizontalLine_Segment:
5069 pts = HLineIntersect(wt.pts(), wn.left(),
5070 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005071 debugShowLineIntersection(pts, wt, wn,
5072 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005073 break;
5074 case Work::kVerticalLine_Segment:
5075 pts = VLineIntersect(wt.pts(), wn.top(),
5076 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005077 debugShowLineIntersection(pts, wt, wn,
5078 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005079 break;
5080 case Work::kLine_Segment: {
5081 pts = LineIntersect(wt.pts(), wn.pts(), ts);
5082 debugShowLineIntersection(pts, wt, wn,
5083 ts.fT[1], ts.fT[0]);
5084 break;
5085 }
5086 case Work::kQuad_Segment: {
5087 swap = true;
5088 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005089 debugShowQuadLineIntersection(pts, wn, wt,
5090 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005091 break;
5092 }
5093 case Work::kCubic_Segment: {
5094 swap = true;
5095 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
5096 break;
5097 }
5098 default:
5099 SkASSERT(0);
5100 }
5101 break;
5102 case Work::kQuad_Segment:
5103 switch (wn.segmentType()) {
5104 case Work::kHorizontalLine_Segment:
5105 pts = HQuadIntersect(wt.pts(), wn.left(),
5106 wn.right(), wn.y(), wn.xFlipped(), ts);
5107 break;
5108 case Work::kVerticalLine_Segment:
5109 pts = VQuadIntersect(wt.pts(), wn.top(),
5110 wn.bottom(), wn.x(), wn.yFlipped(), ts);
5111 break;
5112 case Work::kLine_Segment: {
5113 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005114 debugShowQuadLineIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00005115 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005116 break;
5117 }
5118 case Work::kQuad_Segment: {
5119 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00005120 debugShowQuadIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00005121 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005122 break;
5123 }
5124 case Work::kCubic_Segment: {
5125 wt.promoteToCubic();
5126 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
5127 break;
5128 }
5129 default:
5130 SkASSERT(0);
5131 }
5132 break;
5133 case Work::kCubic_Segment:
5134 switch (wn.segmentType()) {
5135 case Work::kHorizontalLine_Segment:
5136 pts = HCubicIntersect(wt.pts(), wn.left(),
5137 wn.right(), wn.y(), wn.xFlipped(), ts);
5138 break;
5139 case Work::kVerticalLine_Segment:
5140 pts = VCubicIntersect(wt.pts(), wn.top(),
5141 wn.bottom(), wn.x(), wn.yFlipped(), ts);
5142 break;
5143 case Work::kLine_Segment: {
5144 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
5145 break;
5146 }
5147 case Work::kQuad_Segment: {
5148 wn.promoteToCubic();
5149 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
5150 break;
5151 }
5152 case Work::kCubic_Segment: {
5153 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
5154 break;
5155 }
5156 default:
5157 SkASSERT(0);
5158 }
5159 break;
5160 default:
5161 SkASSERT(0);
5162 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005163 if (!foundCommonContour && pts > 0) {
5164 test->addCross(next);
5165 next->addCross(test);
5166 foundCommonContour = true;
5167 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005168 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00005169 if (pts == 2) {
5170 if (wn.segmentType() <= Work::kLine_Segment
5171 && wt.segmentType() <= Work::kLine_Segment) {
5172 wt.addCoincident(wn, ts, swap);
5173 continue;
5174 }
5175 if (wn.segmentType() == Work::kQuad_Segment
5176 && wt.segmentType() == Work::kQuad_Segment
5177 && ts.coincidentUsed() == 2) {
5178 wt.addCoincident(wn, ts, swap);
5179 continue;
5180 }
5181
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00005182 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00005183 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005184 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
5185 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005186 int testTAt = wt.addT(ts.fT[swap][pt], wn);
5187 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005188 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
5189 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005190 }
5191 } while (wn.advance());
5192 } while (wt.advance());
5193 return true;
5194}
5195
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005196// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005197// see if coincidence is formed by clipping non-concident segments
caryclark@google.com4eeda372012-12-06 21:47:48 +00005198static void coincidenceCheck(SkTDArray<Contour*>& contourList, int total) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005199 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00005200 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005201 Contour* contour = contourList[cIndex];
caryclark@google.com7ba591e2012-11-20 14:21:54 +00005202 contour->resolveCoincidence(contourList);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005203 }
5204 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5205 Contour* contour = contourList[cIndex];
caryclark@google.com4eeda372012-12-06 21:47:48 +00005206 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005207 }
5208}
5209
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005210static int contourRangeCheckX(SkTDArray<Contour*>& contourList, double mid,
5211 const Segment* current, int index, int endIndex, bool opp) {
5212 const SkPoint& basePt = current->xyAtT(endIndex);
5213 int contourCount = contourList.count();
5214 SkScalar bestX = SK_ScalarMin;
5215 const Segment* test = NULL;
5216 int tIndex;
5217 double tHit;
5218 bool crossOpp;
5219 for (int cTest = 0; cTest < contourCount; ++cTest) {
5220 Contour* contour = contourList[cTest];
5221 bool testOpp = contour->operand() ^ current->operand() ^ opp;
5222 if (basePt.fX < contour->bounds().fLeft) {
5223 continue;
5224 }
5225 if (bestX > contour->bounds().fRight) {
5226 continue;
5227 }
5228 const Segment* next = contour->crossedSegmentX(basePt, bestX, tIndex, tHit, testOpp);
5229 if (next) {
5230 test = next;
5231 crossOpp = testOpp;
5232 }
5233 }
5234 if (!test) {
5235 return 0;
5236 }
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005237 return test->windingAtT(tHit, tIndex, crossOpp);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005238}
5239
5240static int contourRangeCheckY(SkTDArray<Contour*>& contourList, double mid,
5241 const Segment* current, int index, int endIndex, bool opp) {
5242 const SkPoint& basePt = current->xyAtT(endIndex);
5243 int contourCount = contourList.count();
5244 SkScalar bestY = SK_ScalarMin;
5245 const Segment* test = NULL;
5246 int tIndex;
5247 double tHit;
5248 bool crossOpp;
5249 for (int cTest = 0; cTest < contourCount; ++cTest) {
5250 Contour* contour = contourList[cTest];
5251 bool testOpp = contour->operand() ^ current->operand() ^ opp;
5252 if (basePt.fY < contour->bounds().fTop) {
5253 continue;
5254 }
5255 if (bestY > contour->bounds().fBottom) {
5256 continue;
5257 }
5258 const Segment* next = contour->crossedSegmentY(basePt, bestY, tIndex, tHit, testOpp);
5259 if (next) {
5260 test = next;
5261 crossOpp = testOpp;
5262 }
5263 }
5264 if (!test) {
5265 return 0;
5266 }
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005267 return test->windingAtT(tHit, tIndex, crossOpp);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005268}
5269
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005270// project a ray from the top of the contour up and see if it hits anything
5271// note: when we compute line intersections, we keep track of whether
5272// two contours touch, so we need only look at contours not touching this one.
5273// OPTIMIZATION: sort contourList vertically to avoid linear walk
5274static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com31143cf2012-11-09 22:14:19 +00005275 const Segment* current, int index, int endIndex, bool opp) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005276 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005277 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005278 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00005279 const Segment* test = NULL;
5280 int tIndex;
5281 double tHit;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005282 bool crossOpp;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005283 for (int cTest = 0; cTest < contourCount; ++cTest) {
5284 Contour* contour = contourList[cTest];
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005285 bool testOpp = contour->operand() ^ current->operand() ^ opp;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005286 if (basePt.fY < contour->bounds().fTop) {
5287 continue;
5288 }
5289 if (bestY > contour->bounds().fBottom) {
5290 continue;
5291 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005292 const Segment* next = contour->crossedSegmentY(basePt, bestY, tIndex, tHit, testOpp);
caryclark@google.com47580692012-07-23 12:14:49 +00005293 if (next) {
5294 test = next;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005295 crossOpp = testOpp;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005296 }
caryclark@google.com47580692012-07-23 12:14:49 +00005297 }
5298 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00005299 return 0;
5300 }
5301 int winding, windValue;
5302 // If the ray hit the end of a span, we need to construct the wheel of
5303 // angles to find the span closest to the ray -- even if there are just
5304 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005305 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00005306 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00005307 SkTDArray<Angle> angles;
5308 int end = test->nextSpan(tIndex, 1);
5309 if (end < 0) {
5310 end = test->nextSpan(tIndex, -1);
5311 }
5312 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00005313 SkASSERT(angles.count() > 0);
5314 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
5315#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00005316 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00005317#endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005318 return SK_MinS32;
caryclark@google.com59823f72012-08-09 18:17:47 +00005319 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005320 test->buildAngles(tIndex, angles, false);
caryclark@google.com47580692012-07-23 12:14:49 +00005321 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00005322 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00005323 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00005324 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00005325 // hour after 6 o'clock
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005326 bool sortable = Segment::SortAngles(angles, sorted);
5327 if (!sortable) {
5328 return SK_MinS32;
5329 }
caryclark@google.com47580692012-07-23 12:14:49 +00005330#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005331 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00005332#endif
5333 // walk the sorted angle fan to find the lowest angle
5334 // above the base point. Currently, the first angle in the sorted array
5335 // is 12 noon or an earlier hour (the next counterclockwise)
5336 int count = sorted.count();
5337 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005338 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00005339 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005340 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00005341 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00005342 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005343 if (angle->unsortable()) {
5344 continue;
5345 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005346 if (baseMatches && angle->isHorizontal()) {
5347 continue;
5348 }
5349 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00005350 test = angle->segment();
5351 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
5352 const SkPoint* pts = test->pts();
5353 indexDx = pts[2].fX - pts[1].fX - indexDx;
5354 }
caryclark@google.com47580692012-07-23 12:14:49 +00005355 if (indexDx < 0) {
5356 left = index;
5357 } else if (indexDx > 0) {
5358 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00005359 int previous = index - 1;
5360 if (previous < 0) {
5361 previous = count - 1;
5362 }
5363 const Angle* prev = sorted[previous];
5364 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
5365#if DEBUG_SORT
5366 SkDebugf("%s use prev\n", __FUNCTION__);
5367#endif
5368 right = previous;
5369 }
caryclark@google.com47580692012-07-23 12:14:49 +00005370 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005371 } else {
5372 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005373 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005374 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005375 if ((left < 0 || right < 0) && mid >= 0) {
5376 angle = sorted[mid];
5377 Segment* midSeg = angle->segment();
5378 int end = angle->end();
5379 if (midSeg->unsortable(end)) {
5380 return SK_MinS32;
5381 }
5382 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005383 if (left < 0 && right < 0) {
5384 left = mid;
5385 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005386 if (left < 0 && right < 0) {
5387 SkASSERT(0);
5388 return SK_MinS32; // unsortable
5389 }
caryclark@google.com47580692012-07-23 12:14:49 +00005390 if (left < 0) {
5391 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005392 } else if (left >= 0 && mid >= 0 && right >= 0
5393 && sorted[mid]->sign() == sorted[right]->sign()) {
5394 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005395 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005396 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00005397 test = angle->segment();
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005398 winding = crossOpp ? test->oppSum(angle) : test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00005399 SkASSERT(winding != SK_MinS32);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005400 windValue = crossOpp ? test->oppValue(angle) : test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00005401#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005402 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
5403 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00005404#endif
5405 } else {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005406 winding = crossOpp ? test->oppSum(tIndex) : test->windSum(tIndex);
5407 if (winding == SK_MinS32) {
5408 return SK_MinS32; // unsortable
5409 }
5410 windValue = crossOpp ? test->oppValue(tIndex) : test->windValue(tIndex);
caryclark@google.com47580692012-07-23 12:14:49 +00005411#if DEBUG_WINDING
5412 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
5413 windValue);
5414#endif
5415 }
5416 // see if a + change in T results in a +/- change in X (compute x'(T))
5417 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00005418 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
5419 const SkPoint* pts = test->pts();
5420 dx = pts[2].fX - pts[1].fX - dx;
5421 }
caryclark@google.com47580692012-07-23 12:14:49 +00005422#if DEBUG_WINDING
5423 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
5424#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00005425 SkASSERT(dx != 0);
5426 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00005427 winding += dx > 0 ? -windValue : windValue;
5428#if DEBUG_WINDING
5429 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
5430#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005431 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005432 return winding;
5433}
rmistry@google.comd6176b02012-08-23 18:14:13 +00005434
caryclark@google.com24bec792012-08-20 12:43:57 +00005435static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
5436 int contourCount = contourList.count();
5437 Segment* result;
5438 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5439 Contour* contour = contourList[cIndex];
5440 result = contour->undoneSegment(start, end);
5441 if (result) {
5442 return result;
5443 }
5444 }
5445 return NULL;
5446}
5447
5448
5449
caryclark@google.com31143cf2012-11-09 22:14:19 +00005450static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005451 while (chase.count()) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005452 Span* span;
5453 chase.pop(&span);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005454 const Span& backPtr = span->fOther->span(span->fOtherIndex);
5455 Segment* segment = backPtr.fOther;
5456 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005457 SkTDArray<Angle> angles;
5458 int done = 0;
5459 if (segment->activeAngle(tIndex, done, angles)) {
5460 Angle* last = angles.end() - 1;
5461 tIndex = last->start();
5462 endIndex = last->end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005463 #if TRY_ROTATE
5464 *chase.insert(0) = span;
5465 #else
5466 *chase.append() = span;
5467 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005468 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005469 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00005470 if (done == angles.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00005471 continue;
5472 }
5473 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005474 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00005475#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005476 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00005477#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005478 if (!sortable) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005479 continue;
5480 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00005481 // find first angle, initialize winding to computed fWindSum
5482 int firstIndex = -1;
5483 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005484 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005485 do {
5486 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005487 segment = angle->segment();
5488 winding = segment->windSum(angle);
5489 } while (winding == SK_MinS32);
5490 int spanWinding = segment->spanSign(angle->start(), angle->end());
5491 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00005492 SkDebugf("%s winding=%d spanWinding=%d\n",
5493 __FUNCTION__, winding, spanWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00005494 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005495 // turn span winding into contour winding
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005496 if (spanWinding * winding < 0) {
5497 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005498 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005499 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005500 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005501 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005502 // we care about first sign and whether wind sum indicates this
5503 // edge is inside or outside. Maybe need to pass span winding
5504 // or first winding or something into this function?
5505 // advance to first undone angle, then return it and winding
5506 // (to set whether edges are active or not)
5507 int nextIndex = firstIndex + 1;
5508 int angleCount = sorted.count();
5509 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005510 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00005511 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00005512 do {
5513 SkASSERT(nextIndex != firstIndex);
5514 if (nextIndex == angleCount) {
5515 nextIndex = 0;
5516 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005517 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00005518 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005519 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00005520 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005521 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00005522 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
5523 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005524 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005525 tIndex = angle->start();
5526 endIndex = angle->end();
5527 int lesser = SkMin32(tIndex, endIndex);
5528 const Span& nextSpan = segment->span(lesser);
5529 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005530#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00005531 // FIXME: this be wrong. assign startWinding if edge is in
5532 // same direction. If the direction is opposite, winding to
5533 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00005534 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005535 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005536 }
caryclark@google.com59823f72012-08-09 18:17:47 +00005537 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005538#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005539 break;
5540 }
5541 } while (++nextIndex != lastIndex);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005542 #if TRY_ROTATE
5543 *chase.insert(0) = span;
5544 #else
5545 *chase.append() = span;
5546 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005547 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005548 }
5549 return NULL;
5550}
5551
caryclark@google.com027de222012-07-12 12:52:50 +00005552#if DEBUG_ACTIVE_SPANS
5553static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005554 int index;
5555 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00005556 contourList[index]->debugShowActiveSpans();
5557 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005558 for (index = 0; index < contourList.count(); ++ index) {
5559 contourList[index]->validateActiveSpans();
5560 }
caryclark@google.com027de222012-07-12 12:52:50 +00005561}
5562#endif
5563
caryclark@google.com27c449a2012-07-27 18:26:38 +00005564static bool windingIsActive(int winding, int spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005565 // FIXME: !spanWinding test must be superflorous, true?
caryclark@google.com27c449a2012-07-27 18:26:38 +00005566 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
5567 && (!winding || !spanWinding || winding == -spanWinding);
5568}
5569
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005570static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005571 int& endIndex, SkPoint& topLeft, bool& unsortable, bool onlySortable) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005572 Segment* result;
5573 do {
caryclark@google.comf839c032012-10-26 21:03:50 +00005574 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005575 int contourCount = contourList.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00005576 Segment* topStart = NULL;
5577 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5578 Contour* contour = contourList[cIndex];
5579 const Bounds& bounds = contour->bounds();
5580 if (bounds.fBottom < topLeft.fY) {
5581 continue;
5582 }
5583 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
5584 continue;
5585 }
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005586 Segment* test = contour->topSortableSegment(topLeft, bestXY);
caryclark@google.comf839c032012-10-26 21:03:50 +00005587 if (test) {
5588 topStart = test;
5589 }
5590 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005591 if (!topStart) {
5592 return NULL;
5593 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005594 topLeft = bestXY;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005595 result = topStart->findTop(index, endIndex, unsortable, onlySortable);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005596 } while (!result);
5597 return result;
5598}
caryclark@google.com31143cf2012-11-09 22:14:19 +00005599
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005600static int updateWindings(const Segment* current, int index, int endIndex, int& spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005601 int lesser = SkMin32(index, endIndex);
5602 spanWinding = current->spanSign(index, endIndex);
5603 int winding = current->windSum(lesser);
5604 bool inner = useInnerWinding(winding - spanWinding, winding);
5605#if DEBUG_WINDING
5606 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
5607 " inner=%d result=%d\n",
5608 __FUNCTION__, current->debugID(), current->t(lesser),
5609 spanWinding, winding, SkSign32(index - endIndex),
5610 useInnerWinding(winding - spanWinding, winding),
5611 inner ? winding - spanWinding : winding);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005612#endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005613 if (inner) {
5614 winding -= spanWinding;
5615 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005616 return winding;
5617}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005618
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005619typedef int (*RangeChecker)(SkTDArray<Contour*>& contourList, double mid,
5620 const Segment* current, int index, int endIndex, bool opp);
5621
5622static int rightAngleWinding(RangeChecker rangeChecker, SkTDArray<Contour*>& contourList,
5623 Segment* current, int index, int endIndex, bool opp) {
5624 double test = 0.9;
5625 int contourWinding;
5626 do {
5627 contourWinding = (*rangeChecker)(contourList, test, current, index, endIndex, opp);
5628 if (contourWinding != SK_MinS32) {
5629 return contourWinding;
5630 }
5631 test /= 2;
5632 } while (!approximately_negative(test));
5633 SkASSERT(0); // should be OK to comment out, but interested when this hits
5634 return contourWinding;
5635}
5636
5637static Segment* tryRightAngleRay(SkTDArray<Contour*>& contourList, int& index,
5638 int& endIndex, SkPoint& topLeft, bool& unsortable, RangeChecker& rangeChecker) {
5639 // the simple upward projection of the unresolved points hit unsortable angles
5640 // shoot rays at right angles to the segment to find its winding, ignoring angle cases
5641 topLeft.fX = topLeft.fY = SK_ScalarMin;
5642 Segment* current;
5643 do {
5644 current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, false);
5645 SkASSERT(current); // FIXME: return to caller that path cannot be simplified (yet)
5646 // find bounds
5647 Bounds bounds;
5648 bounds.setPoint(current->xyAtT(index));
5649 bounds.add(current->xyAtT(endIndex));
5650 SkScalar width = bounds.width();
5651 SkScalar height = bounds.height();
5652 if (width > height) {
5653 if (approximately_negative(width)) {
5654 continue; // edge is too small to resolve meaningfully
5655 }
5656 rangeChecker = contourRangeCheckY;
5657 } else {
5658 if (approximately_negative(height)) {
5659 continue; // edge is too small to resolve meaningfully
5660 }
5661 rangeChecker = contourRangeCheckX;
5662 }
5663 } while (!current);
5664 return current;
5665}
5666
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005667static Segment* findSortableTopOld(SkTDArray<Contour*>& contourList, bool& firstContour, int& index,
5668 int& endIndex, SkPoint& topLeft, int& contourWinding, bool& unsortable) {
5669 Segment* current;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005670 do {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005671 current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, true);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005672 if (!current) {
5673 break;
5674 }
5675 if (firstContour) {
5676 contourWinding = 0;
5677 firstContour = false;
5678 break;
5679 }
5680 int sumWinding = current->windSum(SkMin32(index, endIndex));
5681 // FIXME: don't I have to adjust windSum to get contourWinding?
5682 if (sumWinding == SK_MinS32) {
5683 sumWinding = current->computeSum(index, endIndex, false);
5684 }
5685 if (sumWinding == SK_MinS32) {
5686 contourWinding = innerContourCheck(contourList, current, index, endIndex, false);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005687 } else {
5688 contourWinding = sumWinding;
5689 int spanWinding = current->spanSign(index, endIndex);
5690 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
5691 if (inner) {
5692 contourWinding -= spanWinding;
5693 }
5694#if DEBUG_WINDING
5695 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n",
5696 __FUNCTION__, sumWinding, spanWinding, SkSign32(index - endIndex),
5697 inner, contourWinding);
5698#endif
5699 }
5700 } while (contourWinding == SK_MinS32);
5701 if (contourWinding != SK_MinS32) {
5702#if DEBUG_WINDING
5703 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
5704#endif
5705 return current;
5706 }
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005707 RangeChecker rangeChecker = NULL;
5708 current = tryRightAngleRay(contourList, index, endIndex, topLeft, unsortable, rangeChecker);
5709 contourWinding = rightAngleWinding(rangeChecker, contourList, current, index, endIndex, false);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005710 return current;
5711}
5712
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005713// Each segment may have an inside or an outside. Segments contained within
5714// winding may have insides on either side, and form a contour that should be
5715// ignored. Segments that are coincident with opposing direction segments may
5716// have outsides on either side, and should also disappear.
5717// 'Normal' segments will have one inside and one outside. Subsequent connections
5718// when winding should follow the intersection direction. If more than one edge
5719// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005720 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00005721 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005722// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005723static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005724 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005725 bool unsortable = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005726 bool topUnsortable = false;
5727 bool firstRetry = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005728 bool closable = true;
5729 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com15fa1382012-05-07 20:49:36 +00005730 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005731 int index, endIndex;
caryclark@google.com31143cf2012-11-09 22:14:19 +00005732 // iterates while top is unsortable
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005733 int contourWinding;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005734 Segment* current = findSortableTopOld(contourList, firstContour, index, endIndex, topLeft,
5735 contourWinding, topUnsortable);
5736 if (!current) {
5737 if (topUnsortable) {
5738 topUnsortable = false;
5739 SkASSERT(!firstRetry);
5740 firstRetry = true;
5741 topLeft.fX = topLeft.fY = SK_ScalarMin;
5742 continue;
caryclark@google.com200c2112012-08-03 15:05:04 +00005743 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005744 break;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005745 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005746 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005747 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005748 // FIXME: needs work. While it works in limited situations, it does
5749 // not always compute winding correctly. Active should be removed and instead
5750 // the initial winding should be correctly passed in so that if the
5751 // inner contour is wound the same way, it never finds an accumulated
5752 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00005753 // other than zero when resolving sorted angles.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005754 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005755 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005756 bool active = windingIsActive(winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005757 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00005758 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
5759 __FUNCTION__, active ? "true" : "false",
5760 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005761 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005762 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005763 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005764 int nextStart = index;
5765 int nextEnd = endIndex;
5766 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005767 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005768 if (!next) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005769 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00005770 && current->verb() != SkPath::kLine_Verb
5771 && !simple.isClosed()) {
5772 current->addCurveTo(index, endIndex, simple, true);
5773 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005774 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005775 break;
5776 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005777 #if DEBUG_FLOW
5778 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
5779 current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
5780 current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
5781 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00005782 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005783 current = next;
5784 index = nextStart;
5785 endIndex = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005786 } while (!simple.isClosed()
5787 && ((active && !unsortable) || !current->done()));
5788 if (active) {
5789 if (!simple.isClosed()) {
5790 SkASSERT(unsortable);
5791 int min = SkMin32(index, endIndex);
5792 if (!current->done(min)) {
5793 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005794 current->markDone(min, winding ? winding : spanWinding);
caryclark@google.comf839c032012-10-26 21:03:50 +00005795 }
5796 closable = false;
5797 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005798 simple.close();
5799 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005800 current = findChase(chaseArray, index, endIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005801 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00005802 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005803 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005804 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005805 break;
5806 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005807 winding = updateWindings(current, index, endIndex, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005808 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005809 } while (true);
caryclark@google.comf839c032012-10-26 21:03:50 +00005810 return closable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005811}
5812
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005813static Segment* findSortableTopNew(SkTDArray<Contour*>& contourList, bool& firstContour, int& index,
5814 int& endIndex, SkPoint& topLeft, bool& unsortable) {
5815 Segment* current;
5816 bool first = true;
5817 int contourWinding, oppContourWinding;
5818 do {
5819 current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, true);
5820 if (!current) {
5821 if (first) {
5822 return NULL;
5823 }
5824 break;
5825 }
5826 first = false;
5827 if (firstContour) {
5828 current->initWinding(index, endIndex, 0, 0);
5829 firstContour = false;
5830 return current;
5831 }
5832 int minIndex = SkMin32(index, endIndex);
5833 int sumWinding = current->windSum(minIndex);
5834 if (sumWinding == SK_MinS32) {
5835 sumWinding = current->computeSum(index, endIndex, true);
5836 if (sumWinding != SK_MinS32) {
5837 return current;
5838 }
5839 }
5840 contourWinding = innerContourCheck(contourList, current, index, endIndex, false);
5841 if (contourWinding == SK_MinS32) {
5842 continue;
5843 }
5844 oppContourWinding = innerContourCheck(contourList, current, index, endIndex, true);
5845 if (oppContourWinding != SK_MinS32) {
5846 break;
5847 }
5848 current->initWinding(index, endIndex, contourWinding, oppContourWinding);
5849 return current;
5850 } while (true);
5851 if (!current) {
5852 // the simple upward projection of the unresolved points hit unsortable angles
5853 // shoot rays at right angles to the segment to find its winding, ignoring angle cases
5854 int (*checker)(SkTDArray<Contour*>& contourList, double mid,
5855 const Segment* current, int index, int endIndex, bool opp);
5856 current = tryRightAngleRay(contourList, index, endIndex, topLeft, unsortable, checker);
5857 contourWinding = rightAngleWinding(checker, contourList, current, index, endIndex, false);
5858 oppContourWinding = rightAngleWinding(checker, contourList, current, index, endIndex, true);
5859 }
5860 current->initWinding(index, endIndex, contourWinding, oppContourWinding);
5861 return current;
5862}
5863
5864// rewrite that abandons keeping local track of winding
5865static bool bridgeWindingX(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
5866 bool firstContour = true;
5867 bool unsortable = false;
5868 bool topUnsortable = false;
5869 bool firstRetry = false;
5870 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
5871 do {
5872 int index, endIndex;
5873 Segment* current = findSortableTopNew(contourList, firstContour, index, endIndex, topLeft,
5874 topUnsortable);
5875 if (!current) {
5876 if (topUnsortable) {
5877 topUnsortable = false;
5878 SkASSERT(!firstRetry);
5879 firstRetry = true;
5880 topLeft.fX = topLeft.fY = SK_ScalarMin;
5881 continue;
5882 }
5883 break;
5884 }
5885 SkTDArray<Span*> chaseArray;
5886 do {
5887 if (current->activeWinding(index, endIndex)) {
5888 do {
5889 #if DEBUG_ACTIVE_SPANS
5890 if (!unsortable && current->done()) {
5891 debugShowActiveSpans(contourList);
5892 }
5893 #endif
5894 SkASSERT(unsortable || !current->done());
5895 int nextStart = index;
5896 int nextEnd = endIndex;
5897 Segment* next = current->findNextWinding(chaseArray, nextStart, nextEnd,
5898 unsortable);
5899 if (!next) {
5900 if (!unsortable && simple.hasMove()
5901 && current->verb() != SkPath::kLine_Verb
5902 && !simple.isClosed()) {
5903 current->addCurveTo(index, endIndex, simple, true);
5904 SkASSERT(simple.isClosed());
5905 }
5906 break;
5907 }
5908 current->addCurveTo(index, endIndex, simple, true);
5909 current = next;
5910 index = nextStart;
5911 endIndex = nextEnd;
5912 } while (!simple.isClosed() && ((!unsortable) || !current->done()));
5913 if (current->activeWinding(index, endIndex) && !simple.isClosed()) {
5914 SkASSERT(unsortable);
5915 int min = SkMin32(index, endIndex);
5916 if (!current->done(min)) {
5917 current->addCurveTo(index, endIndex, simple, true);
5918 current->markDoneUnary(min);
5919 }
5920 }
5921 simple.close();
5922 } else {
5923 Span* last = current->markAndChaseDoneBinary(index, endIndex);
5924 if (last) {
5925 *chaseArray.append() = last;
5926 }
5927 }
5928 current = findChase(chaseArray, index, endIndex);
5929 #if DEBUG_ACTIVE_SPANS
5930 debugShowActiveSpans(contourList);
5931 #endif
5932 if (!current) {
5933 break;
5934 }
5935 } while (true);
5936 } while (true);
5937 return simple.someAssemblyRequired();
5938}
5939
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005940// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005941static bool bridgeXor(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005942 Segment* current;
5943 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005944 bool unsortable = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005945 bool closable = true;
caryclark@google.com24bec792012-08-20 12:43:57 +00005946 while ((current = findUndone(contourList, start, end))) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005947 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005948 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005949 int nextStart = start;
5950 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005951 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00005952 if (!next) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005953 if (!unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00005954 && current->verb() != SkPath::kLine_Verb
5955 && !simple.isClosed()) {
5956 current->addCurveTo(start, end, simple, true);
5957 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005958 }
caryclark@google.com24bec792012-08-20 12:43:57 +00005959 break;
5960 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005961 #if DEBUG_FLOW
5962 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
5963 current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
5964 current->xyAtT(end).fX, current->xyAtT(end).fY);
5965 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00005966 current->addCurveTo(start, end, simple, true);
caryclark@google.com24bec792012-08-20 12:43:57 +00005967 current = next;
5968 start = nextStart;
5969 end = nextEnd;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005970 } while (!simple.isClosed() && (!unsortable || !current->done()));
5971 if (!simple.isClosed()) {
5972 SkASSERT(unsortable);
5973 int min = SkMin32(start, end);
5974 if (!current->done(min)) {
5975 current->addCurveTo(start, end, simple, true);
5976 current->markDone(min, 1);
5977 }
5978 closable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00005979 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005980 simple.close();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005981 #if DEBUG_ACTIVE_SPANS
5982 debugShowActiveSpans(contourList);
5983 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00005984 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005985 return closable;
caryclark@google.com24bec792012-08-20 12:43:57 +00005986}
5987
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005988static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
5989 int contourCount = contourList.count();
5990 for (int cTest = 0; cTest < contourCount; ++cTest) {
5991 Contour* contour = contourList[cTest];
5992 contour->fixOtherTIndex();
5993 }
5994}
5995
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005996static void sortSegments(SkTDArray<Contour*>& contourList) {
5997 int contourCount = contourList.count();
5998 for (int cTest = 0; cTest < contourCount; ++cTest) {
5999 Contour* contour = contourList[cTest];
6000 contour->sortSegments();
6001 }
6002}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00006003
caryclark@google.com4eeda372012-12-06 21:47:48 +00006004static void makeContourList(SkTArray<Contour>& contours, SkTDArray<Contour*>& list,
6005 bool evenOdd, bool oppEvenOdd) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00006006 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006007 if (count == 0) {
6008 return;
6009 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00006010 for (int index = 0; index < count; ++index) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00006011 Contour& contour = contours[index];
6012 contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
6013 *list.append() = &contour;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006014 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006015 QSort<Contour>(list.begin(), list.end() - 1);
6016}
6017
caryclark@google.comf839c032012-10-26 21:03:50 +00006018static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006019 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00006020}
6021
caryclark@google.comf839c032012-10-26 21:03:50 +00006022 /*
6023 check start and end of each contour
6024 if not the same, record them
6025 match them up
6026 connect closest
6027 reassemble contour pieces into new path
6028 */
6029static void assemble(const PathWrapper& path, PathWrapper& simple) {
6030#if DEBUG_PATH_CONSTRUCTION
6031 SkDebugf("%s\n", __FUNCTION__);
6032#endif
6033 SkTArray<Contour> contours;
6034 EdgeBuilder builder(path, contours);
6035 builder.finish();
6036 int count = contours.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00006037 int outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00006038 SkTDArray<int> runs; // indices of partial contours
caryclark@google.com0b7da432012-10-31 19:00:20 +00006039 for (outer = 0; outer < count; ++outer) {
6040 const Contour& eContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00006041 const SkPoint& eStart = eContour.start();
6042 const SkPoint& eEnd = eContour.end();
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006043#if DEBUG_ASSEMBLE
6044 SkDebugf("%s contour", __FUNCTION__);
6045 if (!approximatelyEqual(eStart, eEnd)) {
6046 SkDebugf("[%d]", runs.count());
6047 } else {
6048 SkDebugf(" ");
6049 }
skia.committer@gmail.com61b05dc2012-12-14 02:02:06 +00006050 SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006051 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
6052#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00006053 if (approximatelyEqual(eStart, eEnd)) {
6054 eContour.toPath(simple);
6055 continue;
6056 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00006057 *runs.append() = outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00006058 }
6059 count = runs.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00006060 if (count == 0) {
6061 return;
6062 }
caryclark@google.comf839c032012-10-26 21:03:50 +00006063 SkTDArray<int> sLink, eLink;
6064 sLink.setCount(count);
6065 eLink.setCount(count);
caryclark@google.com0b7da432012-10-31 19:00:20 +00006066 SkTDArray<double> sBest, eBest;
6067 sBest.setCount(count);
6068 eBest.setCount(count);
caryclark@google.comf839c032012-10-26 21:03:50 +00006069 int rIndex;
6070 for (rIndex = 0; rIndex < count; ++rIndex) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006071 outer = runs[rIndex];
6072 const Contour& oContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00006073 const SkPoint& oStart = oContour.start();
6074 const SkPoint& oEnd = oContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00006075 double dx = oEnd.fX - oStart.fX;
6076 double dy = oEnd.fY - oStart.fY;
6077 double dist = dx * dx + dy * dy;
6078 sBest[rIndex] = eBest[rIndex] = dist;
6079 sLink[rIndex] = eLink[rIndex] = rIndex;
6080 }
6081 for (rIndex = 0; rIndex < count - 1; ++rIndex) {
6082 outer = runs[rIndex];
6083 const Contour& oContour = contours[outer];
6084 const SkPoint& oStart = oContour.start();
6085 const SkPoint& oEnd = oContour.end();
6086 double bestStartDist = sBest[rIndex];
6087 double bestEndDist = eBest[rIndex];
6088 for (int iIndex = rIndex + 1; iIndex < count; ++iIndex) {
6089 int inner = runs[iIndex];
6090 const Contour& iContour = contours[inner];
caryclark@google.comf839c032012-10-26 21:03:50 +00006091 const SkPoint& iStart = iContour.start();
6092 const SkPoint& iEnd = iContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00006093 double dx = iStart.fX - oStart.fX;
6094 double dy = iStart.fY - oStart.fY;
6095 double dist = dx * dx + dy * dy;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006096 if (bestStartDist > dist && sBest[iIndex] > dist) {
6097 sBest[iIndex] = bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00006098 sLink[rIndex] = ~iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00006099 sLink[iIndex] = ~rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00006100 }
6101 dx = iEnd.fX - oStart.fX;
6102 dy = iEnd.fY - oStart.fY;
6103 dist = dx * dx + dy * dy;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006104 if (bestStartDist > dist && eBest[iIndex] > dist) {
6105 eBest[iIndex] = bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00006106 sLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00006107 eLink[iIndex] = rIndex;
6108 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00006109 dx = iStart.fX - oEnd.fX;
6110 dy = iStart.fY - oEnd.fY;
6111 dist = dx * dx + dy * dy;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006112 if (bestEndDist > dist && sBest[iIndex] > dist) {
6113 sBest[iIndex] = bestEndDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00006114 sLink[iIndex] = rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00006115 eLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00006116 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00006117 dx = iEnd.fX - oEnd.fX;
6118 dy = iEnd.fY - oEnd.fY;
6119 dist = dx * dx + dy * dy;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006120 if (bestEndDist > dist && eBest[iIndex] > dist) {
6121 eBest[iIndex] = bestEndDist = dist;
caryclark@google.com0b7da432012-10-31 19:00:20 +00006122 eLink[iIndex] = ~rIndex;
6123 eLink[rIndex] = ~iIndex;
6124 }
6125 }
caryclark@google.comf839c032012-10-26 21:03:50 +00006126 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006127#if DEBUG_ASSEMBLE
6128 for (rIndex = 0; rIndex < count; ++rIndex) {
6129 int s = sLink[rIndex];
6130 int e = eLink[rIndex];
6131 SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
6132 s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
caryclark@google.comf839c032012-10-26 21:03:50 +00006133 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006134#endif
6135 rIndex = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00006136 do {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006137 bool forward = true;
6138 bool first = true;
6139 int sIndex = sLink[rIndex];
6140 SkASSERT(sIndex != INT_MAX);
6141 sLink[rIndex] = INT_MAX;
6142 int eIndex;
6143 if (sIndex < 0) {
6144 eIndex = sLink[~sIndex];
6145 sLink[~sIndex] = INT_MAX;
6146 } else {
6147 eIndex = eLink[sIndex];
6148 eLink[sIndex] = INT_MAX;
6149 }
6150 SkASSERT(eIndex != INT_MAX);
6151#if DEBUG_ASSEMBLE
6152 SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
skia.committer@gmail.com61b05dc2012-12-14 02:02:06 +00006153 sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
6154 eIndex < 0 ? ~eIndex : eIndex);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006155#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00006156 do {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006157 outer = runs[rIndex];
6158 const Contour& contour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00006159 if (first) {
caryclark@google.comf839c032012-10-26 21:03:50 +00006160 first = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006161 const SkPoint* startPtr = &contour.start();
caryclark@google.comf839c032012-10-26 21:03:50 +00006162 simple.deferredMove(startPtr[0]);
6163 }
caryclark@google.comf839c032012-10-26 21:03:50 +00006164 if (forward) {
6165 contour.toPartialForward(simple);
caryclark@google.comf839c032012-10-26 21:03:50 +00006166 } else {
6167 contour.toPartialBackward(simple);
caryclark@google.comf839c032012-10-26 21:03:50 +00006168 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006169#if DEBUG_ASSEMBLE
6170 SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
skia.committer@gmail.com61b05dc2012-12-14 02:02:06 +00006171 eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006172 sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
6173#endif
6174 if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
caryclark@google.comf839c032012-10-26 21:03:50 +00006175 simple.close();
caryclark@google.comf839c032012-10-26 21:03:50 +00006176 break;
6177 }
caryclark@google.comf839c032012-10-26 21:03:50 +00006178 if (forward) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006179 eIndex = eLink[rIndex];
6180 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00006181 eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00006182 if (eIndex >= 0) {
6183 SkASSERT(sLink[eIndex] == rIndex);
6184 sLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00006185 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006186 SkASSERT(eLink[~eIndex] == ~rIndex);
6187 eLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00006188 }
6189 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006190 eIndex = sLink[rIndex];
6191 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00006192 sLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00006193 if (eIndex >= 0) {
6194 SkASSERT(eLink[eIndex] == rIndex);
6195 eLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00006196 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00006197 SkASSERT(sLink[~eIndex] == ~rIndex);
6198 sLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00006199 }
6200 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00006201 rIndex = eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00006202 if (rIndex < 0) {
6203 forward ^= 1;
6204 rIndex = ~rIndex;
6205 }
6206 } while (true);
6207 for (rIndex = 0; rIndex < count; ++rIndex) {
6208 if (sLink[rIndex] != INT_MAX) {
6209 break;
6210 }
6211 }
6212 } while (rIndex < count);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00006213#if DEBUG_ASSEMBLE
6214 for (rIndex = 0; rIndex < count; ++rIndex) {
6215 SkASSERT(sLink[rIndex] == INT_MAX);
6216 SkASSERT(eLink[rIndex] == INT_MAX);
6217 }
6218#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00006219}
6220
6221void simplifyx(const SkPath& path, SkPath& result) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006222 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comf839c032012-10-26 21:03:50 +00006223 result.reset();
6224 result.setFillType(SkPath::kEvenOdd_FillType);
6225 PathWrapper simple(result);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006226
6227 // turn path into list of segments
6228 SkTArray<Contour> contours;
6229 // FIXME: add self-intersecting cubics' T values to segment
6230 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00006231 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006232 SkTDArray<Contour*> contourList;
caryclark@google.com4eeda372012-12-06 21:47:48 +00006233 makeContourList(contours, contourList, false, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006234 Contour** currentPtr = contourList.begin();
6235 if (!currentPtr) {
6236 return;
6237 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00006238 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006239 // find all intersections between segments
6240 do {
6241 Contour** nextPtr = currentPtr;
6242 Contour* current = *currentPtr++;
6243 Contour* next;
6244 do {
6245 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00006246 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00006247 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00006248 // eat through coincident edges
caryclark@google.com4eeda372012-12-06 21:47:48 +00006249 coincidenceCheck(contourList, 0);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00006250 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00006251 sortSegments(contourList);
caryclark@google.com0b7da432012-10-31 19:00:20 +00006252#if DEBUG_ACTIVE_SPANS
6253 debugShowActiveSpans(contourList);
6254#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006255 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00006256 if (builder.xorMask() == kWinding_Mask
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00006257 ? gUseOldBridgeWinding ? !bridgeWinding(contourList, simple)
6258 : bridgeWindingX(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00006259 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00006260 { // if some edges could not be resolved, assemble remaining fragments
caryclark@google.comf839c032012-10-26 21:03:50 +00006261 SkPath temp;
6262 temp.setFillType(SkPath::kEvenOdd_FillType);
6263 PathWrapper assembled(temp);
6264 assemble(simple, assembled);
6265 result = *assembled.nativePath();
caryclark@google.com24bec792012-08-20 12:43:57 +00006266 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006267}
6268