gfx/skia/trunk/src/pathops/SkOpSegment.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /*
michael@0 2 * Copyright 2012 Google Inc.
michael@0 3 *
michael@0 4 * Use of this source code is governed by a BSD-style license that can be
michael@0 5 * found in the LICENSE file.
michael@0 6 */
michael@0 7 #include "SkIntersections.h"
michael@0 8 #include "SkOpSegment.h"
michael@0 9 #include "SkPathWriter.h"
michael@0 10 #include "SkTSort.h"
michael@0 11
michael@0 12 #define F (false) // discard the edge
michael@0 13 #define T (true) // keep the edge
michael@0 14
michael@0 15 static const bool gUnaryActiveEdge[2][2] = {
michael@0 16 // from=0 from=1
michael@0 17 // to=0,1 to=0,1
michael@0 18 {F, T}, {T, F},
michael@0 19 };
michael@0 20
michael@0 21 static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = {
michael@0 22 // miFrom=0 miFrom=1
michael@0 23 // miTo=0 miTo=1 miTo=0 miTo=1
michael@0 24 // suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1
michael@0 25 // suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1
michael@0 26 {{{{F, F}, {F, F}}, {{T, F}, {T, F}}}, {{{T, T}, {F, F}}, {{F, T}, {T, F}}}}, // mi - su
michael@0 27 {{{{F, F}, {F, F}}, {{F, T}, {F, T}}}, {{{F, F}, {T, T}}, {{F, T}, {T, F}}}}, // mi & su
michael@0 28 {{{{F, T}, {T, F}}, {{T, T}, {F, F}}}, {{{T, F}, {T, F}}, {{F, F}, {F, F}}}}, // mi | su
michael@0 29 {{{{F, T}, {T, F}}, {{T, F}, {F, T}}}, {{{T, F}, {F, T}}, {{F, T}, {T, F}}}}, // mi ^ su
michael@0 30 };
michael@0 31
michael@0 32 #undef F
michael@0 33 #undef T
michael@0 34
michael@0 35 enum {
michael@0 36 kOutsideTrackedTCount = 16, // FIXME: determine what this should be
michael@0 37 kMissingSpanCount = 4, // FIXME: determine what this should be
michael@0 38 };
michael@0 39
michael@0 40 // note that this follows the same logic flow as build angles
michael@0 41 bool SkOpSegment::activeAngle(int index, int* done, SkTArray<SkOpAngle, true>* angles) {
michael@0 42 if (activeAngleInner(index, done, angles)) {
michael@0 43 return true;
michael@0 44 }
michael@0 45 double referenceT = fTs[index].fT;
michael@0 46 int lesser = index;
michael@0 47 while (--lesser >= 0
michael@0 48 && (precisely_negative(referenceT - fTs[lesser].fT) || fTs[lesser].fTiny)) {
michael@0 49 if (activeAngleOther(lesser, done, angles)) {
michael@0 50 return true;
michael@0 51 }
michael@0 52 }
michael@0 53 do {
michael@0 54 if (activeAngleOther(index, done, angles)) {
michael@0 55 return true;
michael@0 56 }
michael@0 57 if (++index == fTs.count()) {
michael@0 58 break;
michael@0 59 }
michael@0 60 if (fTs[index - 1].fTiny) {
michael@0 61 referenceT = fTs[index].fT;
michael@0 62 continue;
michael@0 63 }
michael@0 64 } while (precisely_negative(fTs[index].fT - referenceT));
michael@0 65 return false;
michael@0 66 }
michael@0 67
michael@0 68 bool SkOpSegment::activeAngleOther(int index, int* done, SkTArray<SkOpAngle, true>* angles) {
michael@0 69 SkOpSpan* span = &fTs[index];
michael@0 70 SkOpSegment* other = span->fOther;
michael@0 71 int oIndex = span->fOtherIndex;
michael@0 72 return other->activeAngleInner(oIndex, done, angles);
michael@0 73 }
michael@0 74
michael@0 75 bool SkOpSegment::activeAngleInner(int index, int* done, SkTArray<SkOpAngle, true>* angles) {
michael@0 76 int next = nextExactSpan(index, 1);
michael@0 77 if (next > 0) {
michael@0 78 SkOpSpan& upSpan = fTs[index];
michael@0 79 if (upSpan.fWindValue || upSpan.fOppValue) {
michael@0 80 addAngle(angles, index, next);
michael@0 81 if (upSpan.fDone || upSpan.fUnsortableEnd) {
michael@0 82 (*done)++;
michael@0 83 } else if (upSpan.fWindSum != SK_MinS32) {
michael@0 84 return true;
michael@0 85 }
michael@0 86 } else if (!upSpan.fDone) {
michael@0 87 upSpan.fDone = true;
michael@0 88 fDoneSpans++;
michael@0 89 }
michael@0 90 }
michael@0 91 int prev = nextExactSpan(index, -1);
michael@0 92 // edge leading into junction
michael@0 93 if (prev >= 0) {
michael@0 94 SkOpSpan& downSpan = fTs[prev];
michael@0 95 if (downSpan.fWindValue || downSpan.fOppValue) {
michael@0 96 addAngle(angles, index, prev);
michael@0 97 if (downSpan.fDone) {
michael@0 98 (*done)++;
michael@0 99 } else if (downSpan.fWindSum != SK_MinS32) {
michael@0 100 return true;
michael@0 101 }
michael@0 102 } else if (!downSpan.fDone) {
michael@0 103 downSpan.fDone = true;
michael@0 104 fDoneSpans++;
michael@0 105 }
michael@0 106 }
michael@0 107 return false;
michael@0 108 }
michael@0 109
michael@0 110 SkPoint SkOpSegment::activeLeftTop(bool onlySortable, int* firstT) const {
michael@0 111 SkASSERT(!done());
michael@0 112 SkPoint topPt = {SK_ScalarMax, SK_ScalarMax};
michael@0 113 int count = fTs.count();
michael@0 114 // see if either end is not done since we want smaller Y of the pair
michael@0 115 bool lastDone = true;
michael@0 116 bool lastUnsortable = false;
michael@0 117 double lastT = -1;
michael@0 118 for (int index = 0; index < count; ++index) {
michael@0 119 const SkOpSpan& span = fTs[index];
michael@0 120 if (onlySortable && (span.fUnsortableStart || lastUnsortable)) {
michael@0 121 goto next;
michael@0 122 }
michael@0 123 if (span.fDone && lastDone) {
michael@0 124 goto next;
michael@0 125 }
michael@0 126 if (approximately_negative(span.fT - lastT)) {
michael@0 127 goto next;
michael@0 128 }
michael@0 129 {
michael@0 130 const SkPoint& xy = xyAtT(&span);
michael@0 131 if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
michael@0 132 topPt = xy;
michael@0 133 if (firstT) {
michael@0 134 *firstT = index;
michael@0 135 }
michael@0 136 }
michael@0 137 if (fVerb != SkPath::kLine_Verb && !lastDone) {
michael@0 138 SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT, span.fT);
michael@0 139 if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
michael@0 140 && topPt.fX > curveTop.fX)) {
michael@0 141 topPt = curveTop;
michael@0 142 if (firstT) {
michael@0 143 *firstT = index;
michael@0 144 }
michael@0 145 }
michael@0 146 }
michael@0 147 lastT = span.fT;
michael@0 148 }
michael@0 149 next:
michael@0 150 lastDone = span.fDone;
michael@0 151 lastUnsortable = span.fUnsortableEnd;
michael@0 152 }
michael@0 153 return topPt;
michael@0 154 }
michael@0 155
michael@0 156 bool SkOpSegment::activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op) {
michael@0 157 int sumMiWinding = updateWinding(endIndex, index);
michael@0 158 int sumSuWinding = updateOppWinding(endIndex, index);
michael@0 159 if (fOperand) {
michael@0 160 SkTSwap<int>(sumMiWinding, sumSuWinding);
michael@0 161 }
michael@0 162 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
michael@0 163 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding,
michael@0 164 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
michael@0 165 }
michael@0 166
michael@0 167 bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
michael@0 168 int* sumMiWinding, int* sumSuWinding,
michael@0 169 int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) {
michael@0 170 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
michael@0 171 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
michael@0 172 bool miFrom;
michael@0 173 bool miTo;
michael@0 174 bool suFrom;
michael@0 175 bool suTo;
michael@0 176 if (operand()) {
michael@0 177 miFrom = (*oppMaxWinding & xorMiMask) != 0;
michael@0 178 miTo = (*oppSumWinding & xorMiMask) != 0;
michael@0 179 suFrom = (*maxWinding & xorSuMask) != 0;
michael@0 180 suTo = (*sumWinding & xorSuMask) != 0;
michael@0 181 } else {
michael@0 182 miFrom = (*maxWinding & xorMiMask) != 0;
michael@0 183 miTo = (*sumWinding & xorMiMask) != 0;
michael@0 184 suFrom = (*oppMaxWinding & xorSuMask) != 0;
michael@0 185 suTo = (*oppSumWinding & xorSuMask) != 0;
michael@0 186 }
michael@0 187 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
michael@0 188 #if DEBUG_ACTIVE_OP
michael@0 189 SkDebugf("%s op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n", __FUNCTION__,
michael@0 190 SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
michael@0 191 #endif
michael@0 192 return result;
michael@0 193 }
michael@0 194
michael@0 195 bool SkOpSegment::activeWinding(int index, int endIndex) {
michael@0 196 int sumWinding = updateWinding(endIndex, index);
michael@0 197 int maxWinding;
michael@0 198 return activeWinding(index, endIndex, &maxWinding, &sumWinding);
michael@0 199 }
michael@0 200
michael@0 201 bool SkOpSegment::activeWinding(int index, int endIndex, int* maxWinding, int* sumWinding) {
michael@0 202 setUpWinding(index, endIndex, maxWinding, sumWinding);
michael@0 203 bool from = *maxWinding != 0;
michael@0 204 bool to = *sumWinding != 0;
michael@0 205 bool result = gUnaryActiveEdge[from][to];
michael@0 206 return result;
michael@0 207 }
michael@0 208
michael@0 209 void SkOpSegment::addAngle(SkTArray<SkOpAngle, true>* anglesPtr, int start, int end) const {
michael@0 210 SkASSERT(start != end);
michael@0 211 SkOpAngle& angle = anglesPtr->push_back();
michael@0 212 angle.set(this, start, end);
michael@0 213 }
michael@0 214
michael@0 215 void SkOpSegment::addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt,
michael@0 216 SkOpSegment* other) {
michael@0 217 int tIndex = -1;
michael@0 218 int tCount = fTs.count();
michael@0 219 int oIndex = -1;
michael@0 220 int oCount = other->fTs.count();
michael@0 221 do {
michael@0 222 ++tIndex;
michael@0 223 } while (startPt != fTs[tIndex].fPt && tIndex < tCount);
michael@0 224 int tIndexStart = tIndex;
michael@0 225 do {
michael@0 226 ++oIndex;
michael@0 227 } while (endPt != other->fTs[oIndex].fPt && oIndex < oCount);
michael@0 228 int oIndexStart = oIndex;
michael@0 229 const SkPoint* nextPt;
michael@0 230 do {
michael@0 231 nextPt = &fTs[++tIndex].fPt;
michael@0 232 SkASSERT(fTs[tIndex].fT < 1 || startPt != *nextPt);
michael@0 233 } while (startPt == *nextPt);
michael@0 234 double nextT = fTs[tIndex].fT;
michael@0 235 const SkPoint* oNextPt;
michael@0 236 do {
michael@0 237 oNextPt = &other->fTs[++oIndex].fPt;
michael@0 238 SkASSERT(other->fTs[oIndex].fT < 1 || endPt != *oNextPt);
michael@0 239 } while (endPt == *oNextPt);
michael@0 240 double oNextT = other->fTs[oIndex].fT;
michael@0 241 // at this point, spans before and after are at:
michael@0 242 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
michael@0 243 // if tIndexStart == 0, no prior span
michael@0 244 // if nextT == 1, no following span
michael@0 245
michael@0 246 // advance the span with zero winding
michael@0 247 // if the following span exists (not past the end, non-zero winding)
michael@0 248 // connect the two edges
michael@0 249 if (!fTs[tIndexStart].fWindValue) {
michael@0 250 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
michael@0 251 #if DEBUG_CONCIDENT
michael@0 252 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
michael@0 253 __FUNCTION__, fID, other->fID, tIndexStart - 1,
michael@0 254 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
michael@0 255 xyAtT(tIndexStart).fY);
michael@0 256 #endif
michael@0 257 addTPair(fTs[tIndexStart].fT, other, other->fTs[oIndex].fT, false,
michael@0 258 fTs[tIndexStart].fPt);
michael@0 259 }
michael@0 260 if (nextT < 1 && fTs[tIndex].fWindValue) {
michael@0 261 #if DEBUG_CONCIDENT
michael@0 262 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
michael@0 263 __FUNCTION__, fID, other->fID, tIndex,
michael@0 264 fTs[tIndex].fT, xyAtT(tIndex).fX,
michael@0 265 xyAtT(tIndex).fY);
michael@0 266 #endif
michael@0 267 addTPair(fTs[tIndex].fT, other, other->fTs[oIndexStart].fT, false, fTs[tIndex].fPt);
michael@0 268 }
michael@0 269 } else {
michael@0 270 SkASSERT(!other->fTs[oIndexStart].fWindValue);
michael@0 271 if (oIndexStart > 0 && other->fTs[oIndexStart - 1].fWindValue) {
michael@0 272 #if DEBUG_CONCIDENT
michael@0 273 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
michael@0 274 __FUNCTION__, fID, other->fID, oIndexStart - 1,
michael@0 275 other->fTs[oIndexStart].fT, other->xyAtT(oIndexStart).fX,
michael@0 276 other->xyAtT(oIndexStart).fY);
michael@0 277 other->debugAddTPair(other->fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
michael@0 278 #endif
michael@0 279 }
michael@0 280 if (oNextT < 1 && other->fTs[oIndex].fWindValue) {
michael@0 281 #if DEBUG_CONCIDENT
michael@0 282 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
michael@0 283 __FUNCTION__, fID, other->fID, oIndex,
michael@0 284 other->fTs[oIndex].fT, other->xyAtT(oIndex).fX,
michael@0 285 other->xyAtT(oIndex).fY);
michael@0 286 other->debugAddTPair(other->fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
michael@0 287 #endif
michael@0 288 }
michael@0 289 }
michael@0 290 }
michael@0 291
michael@0 292 void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt,
michael@0 293 SkOpSegment* other) {
michael@0 294 // walk this to startPt
michael@0 295 // walk other to startPt
michael@0 296 // if either is > 0, add a pointer to the other, copying adjacent winding
michael@0 297 int tIndex = -1;
michael@0 298 int oIndex = -1;
michael@0 299 do {
michael@0 300 ++tIndex;
michael@0 301 } while (startPt != fTs[tIndex].fPt);
michael@0 302 do {
michael@0 303 ++oIndex;
michael@0 304 } while (startPt != other->fTs[oIndex].fPt);
michael@0 305 if (tIndex > 0 || oIndex > 0 || fOperand != other->fOperand) {
michael@0 306 addTPair(fTs[tIndex].fT, other, other->fTs[oIndex].fT, false, startPt);
michael@0 307 }
michael@0 308 SkPoint nextPt = startPt;
michael@0 309 do {
michael@0 310 const SkPoint* workPt;
michael@0 311 do {
michael@0 312 workPt = &fTs[++tIndex].fPt;
michael@0 313 } while (nextPt == *workPt);
michael@0 314 do {
michael@0 315 workPt = &other->fTs[++oIndex].fPt;
michael@0 316 } while (nextPt == *workPt);
michael@0 317 nextPt = *workPt;
michael@0 318 double tStart = fTs[tIndex].fT;
michael@0 319 double oStart = other->fTs[oIndex].fT;
michael@0 320 if (tStart == 1 && oStart == 1 && fOperand == other->fOperand) {
michael@0 321 break;
michael@0 322 }
michael@0 323 addTPair(tStart, other, oStart, false, nextPt);
michael@0 324 } while (endPt != nextPt);
michael@0 325 }
michael@0 326
michael@0 327 void SkOpSegment::addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
michael@0 328 init(pts, SkPath::kCubic_Verb, operand, evenOdd);
michael@0 329 fBounds.setCubicBounds(pts);
michael@0 330 }
michael@0 331
michael@0 332 void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active) const {
michael@0 333 SkPoint edge[4];
michael@0 334 const SkPoint* ePtr;
michael@0 335 int lastT = fTs.count() - 1;
michael@0 336 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
michael@0 337 ePtr = fPts;
michael@0 338 } else {
michael@0 339 // OPTIMIZE? if not active, skip remainder and return xyAtT(end)
michael@0 340 subDivide(start, end, edge);
michael@0 341 ePtr = edge;
michael@0 342 }
michael@0 343 if (active) {
michael@0 344 bool reverse = ePtr == fPts && start != 0;
michael@0 345 if (reverse) {
michael@0 346 path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
michael@0 347 switch (fVerb) {
michael@0 348 case SkPath::kLine_Verb:
michael@0 349 path->deferredLine(ePtr[0]);
michael@0 350 break;
michael@0 351 case SkPath::kQuad_Verb:
michael@0 352 path->quadTo(ePtr[1], ePtr[0]);
michael@0 353 break;
michael@0 354 case SkPath::kCubic_Verb:
michael@0 355 path->cubicTo(ePtr[2], ePtr[1], ePtr[0]);
michael@0 356 break;
michael@0 357 default:
michael@0 358 SkASSERT(0);
michael@0 359 }
michael@0 360 // return ePtr[0];
michael@0 361 } else {
michael@0 362 path->deferredMoveLine(ePtr[0]);
michael@0 363 switch (fVerb) {
michael@0 364 case SkPath::kLine_Verb:
michael@0 365 path->deferredLine(ePtr[1]);
michael@0 366 break;
michael@0 367 case SkPath::kQuad_Verb:
michael@0 368 path->quadTo(ePtr[1], ePtr[2]);
michael@0 369 break;
michael@0 370 case SkPath::kCubic_Verb:
michael@0 371 path->cubicTo(ePtr[1], ePtr[2], ePtr[3]);
michael@0 372 break;
michael@0 373 default:
michael@0 374 SkASSERT(0);
michael@0 375 }
michael@0 376 }
michael@0 377 }
michael@0 378 // return ePtr[SkPathOpsVerbToPoints(fVerb)];
michael@0 379 }
michael@0 380
michael@0 381 void SkOpSegment::addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
michael@0 382 init(pts, SkPath::kLine_Verb, operand, evenOdd);
michael@0 383 fBounds.set(pts, 2);
michael@0 384 }
michael@0 385
michael@0 386 // add 2 to edge or out of range values to get T extremes
michael@0 387 void SkOpSegment::addOtherT(int index, double otherT, int otherIndex) {
michael@0 388 SkOpSpan& span = fTs[index];
michael@0 389 if (precisely_zero(otherT)) {
michael@0 390 otherT = 0;
michael@0 391 } else if (precisely_equal(otherT, 1)) {
michael@0 392 otherT = 1;
michael@0 393 }
michael@0 394 span.fOtherT = otherT;
michael@0 395 span.fOtherIndex = otherIndex;
michael@0 396 }
michael@0 397
michael@0 398 void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
michael@0 399 init(pts, SkPath::kQuad_Verb, operand, evenOdd);
michael@0 400 fBounds.setQuadBounds(pts);
michael@0 401 }
michael@0 402
michael@0 403 // Defer all coincident edge processing until
michael@0 404 // after normal intersections have been computed
michael@0 405
michael@0 406 // no need to be tricky; insert in normal T order
michael@0 407 // resolve overlapping ts when considering coincidence later
michael@0 408
michael@0 409 // add non-coincident intersection. Resulting edges are sorted in T.
michael@0 410 int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
michael@0 411 if (precisely_zero(newT)) {
michael@0 412 newT = 0;
michael@0 413 } else if (precisely_equal(newT, 1)) {
michael@0 414 newT = 1;
michael@0 415 }
michael@0 416 // FIXME: in the pathological case where there is a ton of intercepts,
michael@0 417 // binary search?
michael@0 418 int insertedAt = -1;
michael@0 419 size_t tCount = fTs.count();
michael@0 420 const SkPoint& firstPt = fPts[0];
michael@0 421 const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
michael@0 422 for (size_t index = 0; index < tCount; ++index) {
michael@0 423 // OPTIMIZATION: if there are three or more identical Ts, then
michael@0 424 // the fourth and following could be further insertion-sorted so
michael@0 425 // that all the edges are clockwise or counterclockwise.
michael@0 426 // This could later limit segment tests to the two adjacent
michael@0 427 // neighbors, although it doesn't help with determining which
michael@0 428 // circular direction to go in.
michael@0 429 const SkOpSpan& span = fTs[index];
michael@0 430 if (newT < span.fT) {
michael@0 431 insertedAt = index;
michael@0 432 break;
michael@0 433 }
michael@0 434 if (newT == span.fT) {
michael@0 435 if (pt == span.fPt) {
michael@0 436 insertedAt = index;
michael@0 437 break;
michael@0 438 }
michael@0 439 if ((pt == firstPt && newT == 0) || (span.fPt == lastPt && newT == 1)) {
michael@0 440 insertedAt = index;
michael@0 441 break;
michael@0 442 }
michael@0 443 }
michael@0 444 }
michael@0 445 SkOpSpan* span;
michael@0 446 if (insertedAt >= 0) {
michael@0 447 span = fTs.insert(insertedAt);
michael@0 448 } else {
michael@0 449 insertedAt = tCount;
michael@0 450 span = fTs.append();
michael@0 451 }
michael@0 452 span->fT = newT;
michael@0 453 span->fOther = other;
michael@0 454 span->fPt = pt;
michael@0 455 #if 0
michael@0 456 // cubics, for instance, may not be exact enough to satisfy this check (e.g., cubicOp69d)
michael@0 457 SkASSERT(approximately_equal(xyAtT(newT).fX, pt.fX)
michael@0 458 && approximately_equal(xyAtT(newT).fY, pt.fY));
michael@0 459 #endif
michael@0 460 span->fWindSum = SK_MinS32;
michael@0 461 span->fOppSum = SK_MinS32;
michael@0 462 span->fWindValue = 1;
michael@0 463 span->fOppValue = 0;
michael@0 464 span->fSmall = false;
michael@0 465 span->fTiny = false;
michael@0 466 span->fLoop = false;
michael@0 467 if ((span->fDone = newT == 1)) {
michael@0 468 ++fDoneSpans;
michael@0 469 }
michael@0 470 span->fUnsortableStart = false;
michael@0 471 span->fUnsortableEnd = false;
michael@0 472 int less = -1;
michael@0 473 while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, span->fPt)) {
michael@0 474 if (span[less].fDone) {
michael@0 475 break;
michael@0 476 }
michael@0 477 double tInterval = newT - span[less].fT;
michael@0 478 if (precisely_negative(tInterval)) {
michael@0 479 break;
michael@0 480 }
michael@0 481 if (fVerb == SkPath::kCubic_Verb) {
michael@0 482 double tMid = newT - tInterval / 2;
michael@0 483 SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
michael@0 484 if (!midPt.approximatelyEqual(xyAtT(span))) {
michael@0 485 break;
michael@0 486 }
michael@0 487 }
michael@0 488 span[less].fSmall = true;
michael@0 489 bool tiny = span[less].fPt == span->fPt;
michael@0 490 span[less].fTiny = tiny;
michael@0 491 span[less].fDone = true;
michael@0 492 if (approximately_negative(newT - span[less].fT) && tiny) {
michael@0 493 if (approximately_greater_than_one(newT)) {
michael@0 494 SkASSERT(&span[less] - fTs.begin() < fTs.count());
michael@0 495 span[less].fUnsortableStart = true;
michael@0 496 if (&span[less - 1] - fTs.begin() >= 0) {
michael@0 497 span[less - 1].fUnsortableEnd = true;
michael@0 498 }
michael@0 499 }
michael@0 500 if (approximately_less_than_zero(span[less].fT)) {
michael@0 501 SkASSERT(&span[less + 1] - fTs.begin() < fTs.count());
michael@0 502 SkASSERT(&span[less] - fTs.begin() >= 0);
michael@0 503 span[less + 1].fUnsortableStart = true;
michael@0 504 span[less].fUnsortableEnd = true;
michael@0 505 }
michael@0 506 }
michael@0 507 ++fDoneSpans;
michael@0 508 --less;
michael@0 509 }
michael@0 510 int more = 1;
michael@0 511 while (fTs.end() - &span[more - 1] > 1 && AlmostEqualUlps(span[more].fPt, span->fPt)) {
michael@0 512 if (span[more - 1].fDone) {
michael@0 513 break;
michael@0 514 }
michael@0 515 double tEndInterval = span[more].fT - newT;
michael@0 516 if (precisely_negative(tEndInterval)) {
michael@0 517 if ((span->fTiny = span[more].fTiny)) {
michael@0 518 span->fDone = true;
michael@0 519 ++fDoneSpans;
michael@0 520 }
michael@0 521 break;
michael@0 522 }
michael@0 523 if (fVerb == SkPath::kCubic_Verb) {
michael@0 524 double tMid = newT - tEndInterval / 2;
michael@0 525 SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
michael@0 526 if (!midEndPt.approximatelyEqual(xyAtT(span))) {
michael@0 527 break;
michael@0 528 }
michael@0 529 }
michael@0 530 span[more - 1].fSmall = true;
michael@0 531 bool tiny = span[more].fPt == span->fPt;
michael@0 532 span[more - 1].fTiny = tiny;
michael@0 533 span[more - 1].fDone = true;
michael@0 534 if (approximately_negative(span[more].fT - newT) && tiny) {
michael@0 535 if (approximately_greater_than_one(span[more].fT)) {
michael@0 536 span[more + 1].fUnsortableStart = true;
michael@0 537 span[more].fUnsortableEnd = true;
michael@0 538 }
michael@0 539 if (approximately_less_than_zero(newT)) {
michael@0 540 span[more].fUnsortableStart = true;
michael@0 541 span[more - 1].fUnsortableEnd = true;
michael@0 542 }
michael@0 543 }
michael@0 544 ++fDoneSpans;
michael@0 545 ++more;
michael@0 546 }
michael@0 547 return insertedAt;
michael@0 548 }
michael@0 549
michael@0 550 // set spans from start to end to decrement by one
michael@0 551 // note this walks other backwards
michael@0 552 // FIXME: there's probably an edge case that can be constructed where
michael@0 553 // two span in one segment are separated by float epsilon on one span but
michael@0 554 // not the other, if one segment is very small. For this
michael@0 555 // case the counts asserted below may or may not be enough to separate the
michael@0 556 // spans. Even if the counts work out, what if the spans aren't correctly
michael@0 557 // sorted? It feels better in such a case to match the span's other span
michael@0 558 // pointer since both coincident segments must contain the same spans.
michael@0 559 // FIXME? It seems that decrementing by one will fail for complex paths that
michael@0 560 // have three or more coincident edges. Shouldn't this subtract the difference
michael@0 561 // between the winding values?
michael@0 562 /* |--> |-->
michael@0 563 this 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4 0>>>>1>>>>2>>>>3>>>4
michael@0 564 other 2<<<<1<<<<0 2<<<<1<<<<0 2<<<<1<<<<0
michael@0 565 ^ ^ <--| <--|
michael@0 566 startPt endPt test/oTest first pos test/oTest final pos
michael@0 567 */
michael@0 568 void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other) {
michael@0 569 bool binary = fOperand != other->fOperand;
michael@0 570 int index = 0;
michael@0 571 while (startPt != fTs[index].fPt) {
michael@0 572 SkASSERT(index < fTs.count());
michael@0 573 ++index;
michael@0 574 }
michael@0 575 while (index > 0 && fTs[index].fT == fTs[index - 1].fT) {
michael@0 576 --index;
michael@0 577 }
michael@0 578 int oIndex = other->fTs.count();
michael@0 579 while (startPt != other->fTs[--oIndex].fPt) { // look for startPt match
michael@0 580 SkASSERT(oIndex > 0);
michael@0 581 }
michael@0 582 double oStartT = other->fTs[oIndex].fT;
michael@0 583 // look for first point beyond match
michael@0 584 while (startPt == other->fTs[--oIndex].fPt || oStartT == other->fTs[oIndex].fT) {
michael@0 585 SkASSERT(oIndex > 0);
michael@0 586 }
michael@0 587 SkOpSpan* test = &fTs[index];
michael@0 588 SkOpSpan* oTest = &other->fTs[oIndex];
michael@0 589 SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
michael@0 590 SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
michael@0 591 do {
michael@0 592 SkASSERT(test->fT < 1);
michael@0 593 SkASSERT(oTest->fT < 1);
michael@0 594 bool decrement = test->fWindValue && oTest->fWindValue;
michael@0 595 bool track = test->fWindValue || oTest->fWindValue;
michael@0 596 bool bigger = test->fWindValue >= oTest->fWindValue;
michael@0 597 const SkPoint& testPt = test->fPt;
michael@0 598 double testT = test->fT;
michael@0 599 const SkPoint& oTestPt = oTest->fPt;
michael@0 600 double oTestT = oTest->fT;
michael@0 601 do {
michael@0 602 if (decrement) {
michael@0 603 if (binary && bigger) {
michael@0 604 test->fOppValue--;
michael@0 605 } else {
michael@0 606 decrementSpan(test);
michael@0 607 }
michael@0 608 } else if (track) {
michael@0 609 TrackOutsidePair(&outsidePts, testPt, oTestPt);
michael@0 610 }
michael@0 611 SkASSERT(index < fTs.count() - 1);
michael@0 612 test = &fTs[++index];
michael@0 613 } while (testPt == test->fPt || testT == test->fT);
michael@0 614 SkDEBUGCODE(int originalWindValue = oTest->fWindValue);
michael@0 615 do {
michael@0 616 SkASSERT(oTest->fT < 1);
michael@0 617 SkASSERT(originalWindValue == oTest->fWindValue);
michael@0 618 if (decrement) {
michael@0 619 if (binary && !bigger) {
michael@0 620 oTest->fOppValue--;
michael@0 621 } else {
michael@0 622 other->decrementSpan(oTest);
michael@0 623 }
michael@0 624 } else if (track) {
michael@0 625 TrackOutsidePair(&oOutsidePts, oTestPt, testPt);
michael@0 626 }
michael@0 627 if (!oIndex) {
michael@0 628 break;
michael@0 629 }
michael@0 630 oTest = &other->fTs[--oIndex];
michael@0 631 } while (oTestPt == oTest->fPt || oTestT == oTest->fT);
michael@0 632 } while (endPt != test->fPt && test->fT < 1);
michael@0 633 // FIXME: determine if canceled edges need outside ts added
michael@0 634 int outCount = outsidePts.count();
michael@0 635 if (!done() && outCount) {
michael@0 636 addCancelOutsides(outsidePts[0], outsidePts[1], other);
michael@0 637 if (outCount > 2) {
michael@0 638 addCancelOutsides(outsidePts[outCount - 2], outsidePts[outCount - 1], other);
michael@0 639 }
michael@0 640 }
michael@0 641 if (!other->done() && oOutsidePts.count()) {
michael@0 642 other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
michael@0 643 }
michael@0 644 }
michael@0 645
michael@0 646 int SkOpSegment::addSelfT(SkOpSegment* other, const SkPoint& pt, double newT) {
michael@0 647 // if the tail nearly intersects itself but not quite, the caller records this separately
michael@0 648 int result = addT(other, pt, newT);
michael@0 649 SkOpSpan* span = &fTs[result];
michael@0 650 span->fLoop = true;
michael@0 651 return result;
michael@0 652 }
michael@0 653
michael@0 654 void SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr,
michael@0 655 SkTArray<SkPoint, true>* outsideTs) {
michael@0 656 int index = *indexPtr;
michael@0 657 int oWindValue = oTest.fWindValue;
michael@0 658 int oOppValue = oTest.fOppValue;
michael@0 659 if (binary) {
michael@0 660 SkTSwap<int>(oWindValue, oOppValue);
michael@0 661 }
michael@0 662 SkOpSpan* const test = &fTs[index];
michael@0 663 SkOpSpan* end = test;
michael@0 664 const SkPoint& oStartPt = oTest.fPt;
michael@0 665 do {
michael@0 666 if (bumpSpan(end, oWindValue, oOppValue)) {
michael@0 667 TrackOutside(outsideTs, oStartPt);
michael@0 668 }
michael@0 669 end = &fTs[++index];
michael@0 670 } while ((end->fPt == test->fPt || end->fT == test->fT) && end->fT < 1);
michael@0 671 *indexPtr = index;
michael@0 672 }
michael@0 673
michael@0 674 // because of the order in which coincidences are resolved, this and other
michael@0 675 // may not have the same intermediate points. Compute the corresponding
michael@0 676 // intermediate T values (using this as the master, other as the follower)
michael@0 677 // and walk other conditionally -- hoping that it catches up in the end
michael@0 678 void SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
michael@0 679 SkTArray<SkPoint, true>* oOutsidePts) {
michael@0 680 int oIndex = *oIndexPtr;
michael@0 681 SkOpSpan* const oTest = &fTs[oIndex];
michael@0 682 SkOpSpan* oEnd = oTest;
michael@0 683 const SkPoint& startPt = test.fPt;
michael@0 684 const SkPoint& oStartPt = oTest->fPt;
michael@0 685 double oStartT = oTest->fT;
michael@0 686 if (oStartPt == oEnd->fPt || oStartT == oEnd->fT) {
michael@0 687 TrackOutside(oOutsidePts, startPt);
michael@0 688 }
michael@0 689 while (oStartPt == oEnd->fPt || oStartT == oEnd->fT) {
michael@0 690 zeroSpan(oEnd);
michael@0 691 oEnd = &fTs[++oIndex];
michael@0 692 }
michael@0 693 *oIndexPtr = oIndex;
michael@0 694 }
michael@0 695
michael@0 696 // FIXME: need to test this case:
michael@0 697 // contourA has two segments that are coincident
michael@0 698 // contourB has two segments that are coincident in the same place
michael@0 699 // each ends up with +2/0 pairs for winding count
michael@0 700 // since logic below doesn't transfer count (only increments/decrements) can this be
michael@0 701 // resolved to +4/0 ?
michael@0 702
michael@0 703 // set spans from start to end to increment the greater by one and decrement
michael@0 704 // the lesser
michael@0 705 void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
michael@0 706 SkOpSegment* other) {
michael@0 707 bool binary = fOperand != other->fOperand;
michael@0 708 int index = 0;
michael@0 709 while (startPt != fTs[index].fPt) {
michael@0 710 SkASSERT(index < fTs.count());
michael@0 711 ++index;
michael@0 712 }
michael@0 713 double startT = fTs[index].fT;
michael@0 714 while (index > 0 && fTs[index - 1].fT == startT) {
michael@0 715 --index;
michael@0 716 }
michael@0 717 int oIndex = 0;
michael@0 718 while (startPt != other->fTs[oIndex].fPt) {
michael@0 719 SkASSERT(oIndex < other->fTs.count());
michael@0 720 ++oIndex;
michael@0 721 }
michael@0 722 double oStartT = other->fTs[oIndex].fT;
michael@0 723 while (oIndex > 0 && other->fTs[oIndex - 1].fT == oStartT) {
michael@0 724 --oIndex;
michael@0 725 }
michael@0 726 SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
michael@0 727 SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
michael@0 728 SkOpSpan* test = &fTs[index];
michael@0 729 const SkPoint* testPt = &test->fPt;
michael@0 730 double testT = test->fT;
michael@0 731 SkOpSpan* oTest = &other->fTs[oIndex];
michael@0 732 const SkPoint* oTestPt = &oTest->fPt;
michael@0 733 SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
michael@0 734 do {
michael@0 735 SkASSERT(test->fT < 1);
michael@0 736 SkASSERT(oTest->fT < 1);
michael@0 737 #if 0
michael@0 738 if (test->fDone || oTest->fDone) {
michael@0 739 #else // consolidate the winding count even if done
michael@0 740 if ((test->fWindValue == 0 && test->fOppValue == 0)
michael@0 741 || (oTest->fWindValue == 0 && oTest->fOppValue == 0)) {
michael@0 742 #endif
michael@0 743 SkDEBUGCODE(int firstWind = test->fWindValue);
michael@0 744 SkDEBUGCODE(int firstOpp = test->fOppValue);
michael@0 745 do {
michael@0 746 SkASSERT(firstWind == fTs[index].fWindValue);
michael@0 747 SkASSERT(firstOpp == fTs[index].fOppValue);
michael@0 748 ++index;
michael@0 749 SkASSERT(index < fTs.count());
michael@0 750 } while (*testPt == fTs[index].fPt);
michael@0 751 SkDEBUGCODE(firstWind = oTest->fWindValue);
michael@0 752 SkDEBUGCODE(firstOpp = oTest->fOppValue);
michael@0 753 do {
michael@0 754 SkASSERT(firstWind == other->fTs[oIndex].fWindValue);
michael@0 755 SkASSERT(firstOpp == other->fTs[oIndex].fOppValue);
michael@0 756 ++oIndex;
michael@0 757 SkASSERT(oIndex < other->fTs.count());
michael@0 758 } while (*oTestPt == other->fTs[oIndex].fPt);
michael@0 759 } else {
michael@0 760 if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
michael@0 761 bumpCoincidentThis(*oTest, binary, &index, &outsidePts);
michael@0 762 other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts);
michael@0 763 } else {
michael@0 764 other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts);
michael@0 765 bumpCoincidentOther(*oTest, &index, &outsidePts);
michael@0 766 }
michael@0 767 }
michael@0 768 test = &fTs[index];
michael@0 769 testPt = &test->fPt;
michael@0 770 testT = test->fT;
michael@0 771 if (endPt == *testPt || endT == testT) {
michael@0 772 break;
michael@0 773 }
michael@0 774 oTest = &other->fTs[oIndex];
michael@0 775 oTestPt = &oTest->fPt;
michael@0 776 SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
michael@0 777 } while (endPt != *oTestPt);
michael@0 778 if (endPt != *testPt && endT != testT) { // in rare cases, one may have ended before the other
michael@0 779 int lastWind = test[-1].fWindValue;
michael@0 780 int lastOpp = test[-1].fOppValue;
michael@0 781 bool zero = lastWind == 0 && lastOpp == 0;
michael@0 782 do {
michael@0 783 if (test->fWindValue || test->fOppValue) {
michael@0 784 test->fWindValue = lastWind;
michael@0 785 test->fOppValue = lastOpp;
michael@0 786 if (zero) {
michael@0 787 test->fDone = true;
michael@0 788 ++fDoneSpans;
michael@0 789 }
michael@0 790 }
michael@0 791 test = &fTs[++index];
michael@0 792 testPt = &test->fPt;
michael@0 793 } while (endPt != *testPt);
michael@0 794 }
michael@0 795 int outCount = outsidePts.count();
michael@0 796 if (!done() && outCount) {
michael@0 797 addCoinOutsides(outsidePts[0], endPt, other);
michael@0 798 }
michael@0 799 if (!other->done() && oOutsidePts.count()) {
michael@0 800 other->addCoinOutsides(oOutsidePts[0], endPt, this);
michael@0 801 }
michael@0 802 }
michael@0 803
michael@0 804 // FIXME: this doesn't prevent the same span from being added twice
michael@0 805 // fix in caller, SkASSERT here?
michael@0 806 void SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
michael@0 807 const SkPoint& pt) {
michael@0 808 int tCount = fTs.count();
michael@0 809 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
michael@0 810 const SkOpSpan& span = fTs[tIndex];
michael@0 811 if (!approximately_negative(span.fT - t)) {
michael@0 812 break;
michael@0 813 }
michael@0 814 if (approximately_negative(span.fT - t) && span.fOther == other
michael@0 815 && approximately_equal(span.fOtherT, otherT)) {
michael@0 816 #if DEBUG_ADD_T_PAIR
michael@0 817 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
michael@0 818 __FUNCTION__, fID, t, other->fID, otherT);
michael@0 819 #endif
michael@0 820 return;
michael@0 821 }
michael@0 822 }
michael@0 823 #if DEBUG_ADD_T_PAIR
michael@0 824 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
michael@0 825 __FUNCTION__, fID, t, other->fID, otherT);
michael@0 826 #endif
michael@0 827 int insertedAt = addT(other, pt, t);
michael@0 828 int otherInsertedAt = other->addT(this, pt, otherT);
michael@0 829 addOtherT(insertedAt, otherT, otherInsertedAt);
michael@0 830 other->addOtherT(otherInsertedAt, t, insertedAt);
michael@0 831 matchWindingValue(insertedAt, t, borrowWind);
michael@0 832 other->matchWindingValue(otherInsertedAt, otherT, borrowWind);
michael@0 833 }
michael@0 834
michael@0 835 void SkOpSegment::addTwoAngles(int start, int end, SkTArray<SkOpAngle, true>* angles) const {
michael@0 836 // add edge leading into junction
michael@0 837 int min = SkMin32(end, start);
michael@0 838 if (fTs[min].fWindValue > 0 || fTs[min].fOppValue != 0) {
michael@0 839 addAngle(angles, end, start);
michael@0 840 }
michael@0 841 // add edge leading away from junction
michael@0 842 int step = SkSign32(end - start);
michael@0 843 int tIndex = nextExactSpan(end, step);
michael@0 844 min = SkMin32(end, tIndex);
michael@0 845 if (tIndex >= 0 && (fTs[min].fWindValue > 0 || fTs[min].fOppValue != 0)) {
michael@0 846 addAngle(angles, end, tIndex);
michael@0 847 }
michael@0 848 }
michael@0 849
michael@0 850 bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const {
michael@0 851 const SkPoint midPt = ptAtT(midT);
michael@0 852 SkPathOpsBounds bounds;
michael@0 853 bounds.set(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
michael@0 854 bounds.sort();
michael@0 855 return bounds.almostContains(midPt);
michael@0 856 }
michael@0 857
michael@0 858 bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const {
michael@0 859 if (lesser > greater) {
michael@0 860 SkTSwap<int>(lesser, greater);
michael@0 861 }
michael@0 862 return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
michael@0 863 }
michael@0 864
michael@0 865 // note that this follows the same logic flow as active angle
michael@0 866 bool SkOpSegment::buildAngles(int index, SkTArray<SkOpAngle, true>* angles, bool allowOpp) const {
michael@0 867 double referenceT = fTs[index].fT;
michael@0 868 const SkPoint& referencePt = fTs[index].fPt;
michael@0 869 int lesser = index;
michael@0 870 while (--lesser >= 0 && (allowOpp || fTs[lesser].fOther->fOperand == fOperand)
michael@0 871 && (precisely_negative(referenceT - fTs[lesser].fT) || fTs[lesser].fTiny)) {
michael@0 872 buildAnglesInner(lesser, angles);
michael@0 873 }
michael@0 874 do {
michael@0 875 buildAnglesInner(index, angles);
michael@0 876 if (++index == fTs.count()) {
michael@0 877 break;
michael@0 878 }
michael@0 879 if (!allowOpp && fTs[index].fOther->fOperand != fOperand) {
michael@0 880 break;
michael@0 881 }
michael@0 882 if (fTs[index - 1].fTiny) {
michael@0 883 referenceT = fTs[index].fT;
michael@0 884 continue;
michael@0 885 }
michael@0 886 if (!precisely_negative(fTs[index].fT - referenceT) && fTs[index].fPt == referencePt) {
michael@0 887 // FIXME
michael@0 888 // testQuad8 generates the wrong output unless false is returned here. Other tests will
michael@0 889 // take this path although they aren't required to. This means that many go much slower
michael@0 890 // because of this sort fail.
michael@0 891 // SkDebugf("!!!\n");
michael@0 892 return false;
michael@0 893 }
michael@0 894 } while (precisely_negative(fTs[index].fT - referenceT));
michael@0 895 return true;
michael@0 896 }
michael@0 897
michael@0 898 void SkOpSegment::buildAnglesInner(int index, SkTArray<SkOpAngle, true>* angles) const {
michael@0 899 const SkOpSpan* span = &fTs[index];
michael@0 900 SkOpSegment* other = span->fOther;
michael@0 901 // if there is only one live crossing, and no coincidence, continue
michael@0 902 // in the same direction
michael@0 903 // if there is coincidence, the only choice may be to reverse direction
michael@0 904 // find edge on either side of intersection
michael@0 905 int oIndex = span->fOtherIndex;
michael@0 906 // if done == -1, prior span has already been processed
michael@0 907 int step = 1;
michael@0 908 int next = other->nextExactSpan(oIndex, step);
michael@0 909 if (next < 0) {
michael@0 910 step = -step;
michael@0 911 next = other->nextExactSpan(oIndex, step);
michael@0 912 }
michael@0 913 // add candidate into and away from junction
michael@0 914 other->addTwoAngles(next, oIndex, angles);
michael@0 915 }
michael@0 916
michael@0 917 int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType,
michael@0 918 SkTArray<SkOpAngle, true>* angles, SkTArray<SkOpAngle*, true>* sorted) {
michael@0 919 addTwoAngles(startIndex, endIndex, angles);
michael@0 920 if (!buildAngles(endIndex, angles, includeType == SkOpAngle::kBinaryOpp)) {
michael@0 921 return SK_NaN32;
michael@0 922 }
michael@0 923 int angleCount = angles->count();
michael@0 924 // abort early before sorting if an unsortable (not an unorderable) angle is found
michael@0 925 if (includeType != SkOpAngle::kUnaryXor) {
michael@0 926 int firstIndex = -1;
michael@0 927 while (++firstIndex < angleCount) {
michael@0 928 const SkOpAngle& angle = (*angles)[firstIndex];
michael@0 929 if (angle.segment()->windSum(&angle) != SK_MinS32) {
michael@0 930 break;
michael@0 931 }
michael@0 932 }
michael@0 933 if (firstIndex == angleCount) {
michael@0 934 return SK_MinS32;
michael@0 935 }
michael@0 936 }
michael@0 937 bool sortable = SortAngles2(*angles, sorted);
michael@0 938 #if DEBUG_SORT_RAW
michael@0 939 if (sorted->count() > 0) {
michael@0 940 (*sorted)[0]->segment()->debugShowSort(__FUNCTION__, *sorted, 0, 0, 0, sortable);
michael@0 941 }
michael@0 942 #endif
michael@0 943 if (!sortable) {
michael@0 944 return SK_NaN32;
michael@0 945 }
michael@0 946 if (includeType == SkOpAngle::kUnaryXor) {
michael@0 947 return SK_MinS32;
michael@0 948 }
michael@0 949 // if all angles have a computed winding,
michael@0 950 // or if no adjacent angles are orderable,
michael@0 951 // or if adjacent orderable angles have no computed winding,
michael@0 952 // there's nothing to do
michael@0 953 // if two orderable angles are adjacent, and one has winding computed, transfer to the other
michael@0 954 const SkOpAngle* baseAngle = NULL;
michael@0 955 int last = angleCount;
michael@0 956 int finalInitialOrderable = -1;
michael@0 957 bool tryReverse = false;
michael@0 958 // look for counterclockwise transfers
michael@0 959 do {
michael@0 960 int index = 0;
michael@0 961 do {
michael@0 962 SkOpAngle* testAngle = (*sorted)[index];
michael@0 963 int testWinding = testAngle->segment()->windSum(testAngle);
michael@0 964 if (SK_MinS32 != testWinding && !testAngle->unorderable()) {
michael@0 965 baseAngle = testAngle;
michael@0 966 continue;
michael@0 967 }
michael@0 968 if (testAngle->unorderable()) {
michael@0 969 baseAngle = NULL;
michael@0 970 tryReverse = true;
michael@0 971 continue;
michael@0 972 }
michael@0 973 if (baseAngle) {
michael@0 974 ComputeOneSum(baseAngle, testAngle, includeType);
michael@0 975 baseAngle = SK_MinS32 != testAngle->segment()->windSum(testAngle) ? testAngle
michael@0 976 : NULL;
michael@0 977 tryReverse |= !baseAngle;
michael@0 978 continue;
michael@0 979 }
michael@0 980 if (finalInitialOrderable + 1 == index) {
michael@0 981 finalInitialOrderable = index;
michael@0 982 }
michael@0 983 } while (++index != last);
michael@0 984 if (finalInitialOrderable < 0) {
michael@0 985 break;
michael@0 986 }
michael@0 987 last = finalInitialOrderable + 1;
michael@0 988 finalInitialOrderable = -2; // make this always negative the second time through
michael@0 989 } while (baseAngle);
michael@0 990 if (tryReverse) {
michael@0 991 baseAngle = NULL;
michael@0 992 last = 0;
michael@0 993 finalInitialOrderable = angleCount;
michael@0 994 do {
michael@0 995 int index = angleCount;
michael@0 996 while (--index >= last) {
michael@0 997 SkOpAngle* testAngle = (*sorted)[index];
michael@0 998 int testWinding = testAngle->segment()->windSum(testAngle);
michael@0 999 if (SK_MinS32 != testWinding) {
michael@0 1000 baseAngle = testAngle;
michael@0 1001 continue;
michael@0 1002 }
michael@0 1003 if (testAngle->unorderable()) {
michael@0 1004 baseAngle = NULL;
michael@0 1005 continue;
michael@0 1006 }
michael@0 1007 if (baseAngle) {
michael@0 1008 ComputeOneSumReverse(baseAngle, testAngle, includeType);
michael@0 1009 baseAngle = SK_MinS32 != testAngle->segment()->windSum(testAngle) ? testAngle
michael@0 1010 : NULL;
michael@0 1011 continue;
michael@0 1012 }
michael@0 1013 if (finalInitialOrderable - 1 == index) {
michael@0 1014 finalInitialOrderable = index;
michael@0 1015 }
michael@0 1016 }
michael@0 1017 if (finalInitialOrderable >= angleCount) {
michael@0 1018 break;
michael@0 1019 }
michael@0 1020 last = finalInitialOrderable;
michael@0 1021 finalInitialOrderable = angleCount + 1; // make this inactive 2nd time through
michael@0 1022 } while (baseAngle);
michael@0 1023 }
michael@0 1024 int minIndex = SkMin32(startIndex, endIndex);
michael@0 1025 return windSum(minIndex);
michael@0 1026 }
michael@0 1027
michael@0 1028 void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
michael@0 1029 SkOpAngle::IncludeType includeType) {
michael@0 1030 const SkOpSegment* baseSegment = baseAngle->segment();
michael@0 1031 int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
michael@0 1032 int sumSuWinding;
michael@0 1033 bool binary = includeType >= SkOpAngle::kBinarySingle;
michael@0 1034 if (binary) {
michael@0 1035 sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
michael@0 1036 if (baseSegment->operand()) {
michael@0 1037 SkTSwap<int>(sumMiWinding, sumSuWinding);
michael@0 1038 }
michael@0 1039 }
michael@0 1040 SkOpSegment* nextSegment = nextAngle->segment();
michael@0 1041 int maxWinding, sumWinding;
michael@0 1042 SkOpSpan* last;
michael@0 1043 if (binary) {
michael@0 1044 int oppMaxWinding, oppSumWinding;
michael@0 1045 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
michael@0 1046 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
michael@0 1047 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
michael@0 1048 nextAngle);
michael@0 1049 } else {
michael@0 1050 nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
michael@0 1051 &maxWinding, &sumWinding);
michael@0 1052 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
michael@0 1053 }
michael@0 1054 nextAngle->setLastMarked(last);
michael@0 1055 }
michael@0 1056
michael@0 1057 void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
michael@0 1058 SkOpAngle::IncludeType includeType) {
michael@0 1059 const SkOpSegment* baseSegment = baseAngle->segment();
michael@0 1060 int sumMiWinding = baseSegment->updateWinding(baseAngle);
michael@0 1061 int sumSuWinding;
michael@0 1062 bool binary = includeType >= SkOpAngle::kBinarySingle;
michael@0 1063 if (binary) {
michael@0 1064 sumSuWinding = baseSegment->updateOppWinding(baseAngle);
michael@0 1065 if (baseSegment->operand()) {
michael@0 1066 SkTSwap<int>(sumMiWinding, sumSuWinding);
michael@0 1067 }
michael@0 1068 }
michael@0 1069 SkOpSegment* nextSegment = nextAngle->segment();
michael@0 1070 int maxWinding, sumWinding;
michael@0 1071 SkOpSpan* last;
michael@0 1072 if (binary) {
michael@0 1073 int oppMaxWinding, oppSumWinding;
michael@0 1074 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
michael@0 1075 &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
michael@0 1076 last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
michael@0 1077 nextAngle);
michael@0 1078 } else {
michael@0 1079 nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
michael@0 1080 &maxWinding, &sumWinding);
michael@0 1081 last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
michael@0 1082 }
michael@0 1083 nextAngle->setLastMarked(last);
michael@0 1084 }
michael@0 1085
michael@0 1086 int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT,
michael@0 1087 bool* hitSomething, double mid, bool opp, bool current) const {
michael@0 1088 SkScalar bottom = fBounds.fBottom;
michael@0 1089 int bestTIndex = -1;
michael@0 1090 if (bottom <= *bestY) {
michael@0 1091 return bestTIndex;
michael@0 1092 }
michael@0 1093 SkScalar top = fBounds.fTop;
michael@0 1094 if (top >= basePt.fY) {
michael@0 1095 return bestTIndex;
michael@0 1096 }
michael@0 1097 if (fBounds.fLeft > basePt.fX) {
michael@0 1098 return bestTIndex;
michael@0 1099 }
michael@0 1100 if (fBounds.fRight < basePt.fX) {
michael@0 1101 return bestTIndex;
michael@0 1102 }
michael@0 1103 if (fBounds.fLeft == fBounds.fRight) {
michael@0 1104 // if vertical, and directly above test point, wait for another one
michael@0 1105 return AlmostEqualUlps(basePt.fX, fBounds.fLeft) ? SK_MinS32 : bestTIndex;
michael@0 1106 }
michael@0 1107 // intersect ray starting at basePt with edge
michael@0 1108 SkIntersections intersections;
michael@0 1109 // OPTIMIZE: use specialty function that intersects ray with curve,
michael@0 1110 // returning t values only for curve (we don't care about t on ray)
michael@0 1111 intersections.allowNear(false);
michael@0 1112 int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
michael@0 1113 (fPts, top, bottom, basePt.fX, false);
michael@0 1114 if (pts == 0 || (current && pts == 1)) {
michael@0 1115 return bestTIndex;
michael@0 1116 }
michael@0 1117 if (current) {
michael@0 1118 SkASSERT(pts > 1);
michael@0 1119 int closestIdx = 0;
michael@0 1120 double closest = fabs(intersections[0][0] - mid);
michael@0 1121 for (int idx = 1; idx < pts; ++idx) {
michael@0 1122 double test = fabs(intersections[0][idx] - mid);
michael@0 1123 if (closest > test) {
michael@0 1124 closestIdx = idx;
michael@0 1125 closest = test;
michael@0 1126 }
michael@0 1127 }
michael@0 1128 intersections.quickRemoveOne(closestIdx, --pts);
michael@0 1129 }
michael@0 1130 double bestT = -1;
michael@0 1131 for (int index = 0; index < pts; ++index) {
michael@0 1132 double foundT = intersections[0][index];
michael@0 1133 if (approximately_less_than_zero(foundT)
michael@0 1134 || approximately_greater_than_one(foundT)) {
michael@0 1135 continue;
michael@0 1136 }
michael@0 1137 SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
michael@0 1138 if (approximately_negative(testY - *bestY)
michael@0 1139 || approximately_negative(basePt.fY - testY)) {
michael@0 1140 continue;
michael@0 1141 }
michael@0 1142 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
michael@0 1143 return SK_MinS32; // if the intersection is edge on, wait for another one
michael@0 1144 }
michael@0 1145 if (fVerb > SkPath::kLine_Verb) {
michael@0 1146 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
michael@0 1147 if (approximately_zero(dx)) {
michael@0 1148 return SK_MinS32; // hit vertical, wait for another one
michael@0 1149 }
michael@0 1150 }
michael@0 1151 *bestY = testY;
michael@0 1152 bestT = foundT;
michael@0 1153 }
michael@0 1154 if (bestT < 0) {
michael@0 1155 return bestTIndex;
michael@0 1156 }
michael@0 1157 SkASSERT(bestT >= 0);
michael@0 1158 SkASSERT(bestT <= 1);
michael@0 1159 int start;
michael@0 1160 int end = 0;
michael@0 1161 do {
michael@0 1162 start = end;
michael@0 1163 end = nextSpan(start, 1);
michael@0 1164 } while (fTs[end].fT < bestT);
michael@0 1165 // FIXME: see next candidate for a better pattern to find the next start/end pair
michael@0 1166 while (start + 1 < end && fTs[start].fDone) {
michael@0 1167 ++start;
michael@0 1168 }
michael@0 1169 if (!isCanceled(start)) {
michael@0 1170 *hitT = bestT;
michael@0 1171 bestTIndex = start;
michael@0 1172 *hitSomething = true;
michael@0 1173 }
michael@0 1174 return bestTIndex;
michael@0 1175 }
michael@0 1176
michael@0 1177 bool SkOpSegment::decrementSpan(SkOpSpan* span) {
michael@0 1178 SkASSERT(span->fWindValue > 0);
michael@0 1179 if (--(span->fWindValue) == 0) {
michael@0 1180 if (!span->fOppValue && !span->fDone) {
michael@0 1181 span->fDone = true;
michael@0 1182 ++fDoneSpans;
michael@0 1183 return true;
michael@0 1184 }
michael@0 1185 }
michael@0 1186 return false;
michael@0 1187 }
michael@0 1188
michael@0 1189 bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
michael@0 1190 SkASSERT(!span->fDone || span->fTiny || span->fSmall);
michael@0 1191 span->fWindValue += windDelta;
michael@0 1192 SkASSERT(span->fWindValue >= 0);
michael@0 1193 span->fOppValue += oppDelta;
michael@0 1194 SkASSERT(span->fOppValue >= 0);
michael@0 1195 if (fXor) {
michael@0 1196 span->fWindValue &= 1;
michael@0 1197 }
michael@0 1198 if (fOppXor) {
michael@0 1199 span->fOppValue &= 1;
michael@0 1200 }
michael@0 1201 if (!span->fWindValue && !span->fOppValue) {
michael@0 1202 span->fDone = true;
michael@0 1203 ++fDoneSpans;
michael@0 1204 return true;
michael@0 1205 }
michael@0 1206 return false;
michael@0 1207 }
michael@0 1208
michael@0 1209 // look to see if the curve end intersects an intermediary that intersects the other
michael@0 1210 void SkOpSegment::checkEnds() {
michael@0 1211 debugValidate();
michael@0 1212 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
michael@0 1213 int count = fTs.count();
michael@0 1214 for (int index = 0; index < count; ++index) {
michael@0 1215 const SkOpSpan& span = fTs[index];
michael@0 1216 double otherT = span.fOtherT;
michael@0 1217 if (otherT != 0 && otherT != 1) { // only check ends
michael@0 1218 continue;
michael@0 1219 }
michael@0 1220 const SkOpSegment* other = span.fOther;
michael@0 1221 // peek start/last describe the range of spans that match the other t of this span
michael@0 1222 int peekStart = span.fOtherIndex;
michael@0 1223 while (--peekStart >= 0 && other->fTs[peekStart].fT == otherT)
michael@0 1224 ;
michael@0 1225 int otherCount = other->fTs.count();
michael@0 1226 int peekLast = span.fOtherIndex;
michael@0 1227 while (++peekLast < otherCount && other->fTs[peekLast].fT == otherT)
michael@0 1228 ;
michael@0 1229 if (++peekStart == --peekLast) { // if there isn't a range, there's nothing to do
michael@0 1230 continue;
michael@0 1231 }
michael@0 1232 // t start/last describe the range of spans that match the t of this span
michael@0 1233 double t = span.fT;
michael@0 1234 double tBottom = -1;
michael@0 1235 int tStart = -1;
michael@0 1236 int tLast = count;
michael@0 1237 bool lastSmall = false;
michael@0 1238 double afterT = t;
michael@0 1239 for (int inner = 0; inner < count; ++inner) {
michael@0 1240 double innerT = fTs[inner].fT;
michael@0 1241 if (innerT <= t && innerT > tBottom) {
michael@0 1242 if (innerT < t || !lastSmall) {
michael@0 1243 tStart = inner - 1;
michael@0 1244 }
michael@0 1245 tBottom = innerT;
michael@0 1246 }
michael@0 1247 if (innerT > afterT) {
michael@0 1248 if (t == afterT && lastSmall) {
michael@0 1249 afterT = innerT;
michael@0 1250 } else {
michael@0 1251 tLast = inner;
michael@0 1252 break;
michael@0 1253 }
michael@0 1254 }
michael@0 1255 lastSmall = innerT <= t ? fTs[inner].fSmall : false;
michael@0 1256 }
michael@0 1257 for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) {
michael@0 1258 if (peekIndex == span.fOtherIndex) { // skip the other span pointed to by this span
michael@0 1259 continue;
michael@0 1260 }
michael@0 1261 const SkOpSpan& peekSpan = other->fTs[peekIndex];
michael@0 1262 SkOpSegment* match = peekSpan.fOther;
michael@0 1263 if (match->done()) {
michael@0 1264 continue; // if the edge has already been eaten (likely coincidence), ignore it
michael@0 1265 }
michael@0 1266 const double matchT = peekSpan.fOtherT;
michael@0 1267 // see if any of the spans match the other spans
michael@0 1268 for (int tIndex = tStart + 1; tIndex < tLast; ++tIndex) {
michael@0 1269 const SkOpSpan& tSpan = fTs[tIndex];
michael@0 1270 if (tSpan.fOther == match) {
michael@0 1271 if (tSpan.fOtherT == matchT) {
michael@0 1272 goto nextPeekIndex;
michael@0 1273 }
michael@0 1274 double midT = (tSpan.fOtherT + matchT) / 2;
michael@0 1275 if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) {
michael@0 1276 goto nextPeekIndex;
michael@0 1277 }
michael@0 1278 }
michael@0 1279 }
michael@0 1280 if (missingSpans.count() > 0) {
michael@0 1281 const MissingSpan& lastMissing = missingSpans.back();
michael@0 1282 if (lastMissing.fT == t
michael@0 1283 && lastMissing.fOther == match
michael@0 1284 && lastMissing.fOtherT == matchT) {
michael@0 1285 SkASSERT(lastMissing.fPt == peekSpan.fPt);
michael@0 1286 continue;
michael@0 1287 }
michael@0 1288 }
michael@0 1289 #if DEBUG_CHECK_ENDS
michael@0 1290 SkDebugf("%s id=%d missing t=%1.9g other=%d otherT=%1.9g pt=(%1.9g,%1.9g)\n",
michael@0 1291 __FUNCTION__, fID, t, match->fID, matchT, peekSpan.fPt.fX, peekSpan.fPt.fY);
michael@0 1292 #endif
michael@0 1293 // this segment is missing a entry that the other contains
michael@0 1294 // remember so we can add the missing one and recompute the indices
michael@0 1295 {
michael@0 1296 MissingSpan& missing = missingSpans.push_back();
michael@0 1297 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
michael@0 1298 missing.fT = t;
michael@0 1299 missing.fOther = match;
michael@0 1300 missing.fOtherT = matchT;
michael@0 1301 missing.fPt = peekSpan.fPt;
michael@0 1302 }
michael@0 1303 break;
michael@0 1304 nextPeekIndex:
michael@0 1305 ;
michael@0 1306 }
michael@0 1307 }
michael@0 1308 if (missingSpans.count() == 0) {
michael@0 1309 debugValidate();
michael@0 1310 return;
michael@0 1311 }
michael@0 1312 debugValidate();
michael@0 1313 int missingCount = missingSpans.count();
michael@0 1314 for (int index = 0; index < missingCount; ++index) {
michael@0 1315 MissingSpan& missing = missingSpans[index];
michael@0 1316 addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt);
michael@0 1317 }
michael@0 1318 fixOtherTIndex();
michael@0 1319 // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
michael@0 1320 // avoid this
michael@0 1321 for (int index = 0; index < missingCount; ++index) {
michael@0 1322 missingSpans[index].fOther->fixOtherTIndex();
michael@0 1323 }
michael@0 1324 debugValidate();
michael@0 1325 }
michael@0 1326
michael@0 1327 bool SkOpSegment::checkSmall(int index) const {
michael@0 1328 if (fTs[index].fSmall) {
michael@0 1329 return true;
michael@0 1330 }
michael@0 1331 double tBase = fTs[index].fT;
michael@0 1332 while (index > 0 && precisely_negative(tBase - fTs[--index].fT))
michael@0 1333 ;
michael@0 1334 return fTs[index].fSmall;
michael@0 1335 }
michael@0 1336
michael@0 1337 // if pair of spans on either side of tiny have the same end point and mid point, mark
michael@0 1338 // them as parallel
michael@0 1339 // OPTIMIZATION : mark the segment to note that some span is tiny
michael@0 1340 void SkOpSegment::checkTiny() {
michael@0 1341 SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
michael@0 1342 SkOpSpan* thisSpan = fTs.begin() - 1;
michael@0 1343 const SkOpSpan* endSpan = fTs.end() - 1; // last can't be tiny
michael@0 1344 while (++thisSpan < endSpan) {
michael@0 1345 if (!thisSpan->fTiny) {
michael@0 1346 continue;
michael@0 1347 }
michael@0 1348 SkOpSpan* nextSpan = thisSpan + 1;
michael@0 1349 double thisT = thisSpan->fT;
michael@0 1350 double nextT = nextSpan->fT;
michael@0 1351 if (thisT == nextT) {
michael@0 1352 continue;
michael@0 1353 }
michael@0 1354 SkASSERT(thisT < nextT);
michael@0 1355 SkASSERT(thisSpan->fPt == nextSpan->fPt);
michael@0 1356 SkOpSegment* thisOther = thisSpan->fOther;
michael@0 1357 SkOpSegment* nextOther = nextSpan->fOther;
michael@0 1358 int oIndex = thisSpan->fOtherIndex;
michael@0 1359 for (int oStep = -1; oStep <= 1; oStep += 2) {
michael@0 1360 int oEnd = thisOther->nextExactSpan(oIndex, oStep);
michael@0 1361 if (oEnd < 0) {
michael@0 1362 continue;
michael@0 1363 }
michael@0 1364 const SkOpSpan& oSpan = thisOther->span(oEnd);
michael@0 1365 int nIndex = nextSpan->fOtherIndex;
michael@0 1366 for (int nStep = -1; nStep <= 1; nStep += 2) {
michael@0 1367 int nEnd = nextOther->nextExactSpan(nIndex, nStep);
michael@0 1368 if (nEnd < 0) {
michael@0 1369 continue;
michael@0 1370 }
michael@0 1371 const SkOpSpan& nSpan = nextOther->span(nEnd);
michael@0 1372 if (oSpan.fPt != nSpan.fPt) {
michael@0 1373 continue;
michael@0 1374 }
michael@0 1375 double oMidT = (thisSpan->fOtherT + oSpan.fT) / 2;
michael@0 1376 const SkPoint& oPt = thisOther->ptAtT(oMidT);
michael@0 1377 double nMidT = (nextSpan->fOtherT + nSpan.fT) / 2;
michael@0 1378 const SkPoint& nPt = nextOther->ptAtT(nMidT);
michael@0 1379 if (!AlmostEqualUlps(oPt, nPt)) {
michael@0 1380 continue;
michael@0 1381 }
michael@0 1382 #if DEBUG_CHECK_TINY
michael@0 1383 SkDebugf("%s [%d] add coincidence [%d] [%d]\n", __FUNCTION__, fID,
michael@0 1384 thisOther->fID, nextOther->fID);
michael@0 1385 #endif
michael@0 1386 // this segment is missing a entry that the other contains
michael@0 1387 // remember so we can add the missing one and recompute the indices
michael@0 1388 MissingSpan& missing = missingSpans.push_back();
michael@0 1389 SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
michael@0 1390 missing.fSegment = thisOther;
michael@0 1391 missing.fT = thisSpan->fOtherT;
michael@0 1392 missing.fOther = nextOther;
michael@0 1393 missing.fOtherT = nextSpan->fOtherT;
michael@0 1394 missing.fPt = thisSpan->fPt;
michael@0 1395 }
michael@0 1396 }
michael@0 1397 }
michael@0 1398 int missingCount = missingSpans.count();
michael@0 1399 if (!missingCount) {
michael@0 1400 return;
michael@0 1401 }
michael@0 1402 for (int index = 0; index < missingCount; ++index) {
michael@0 1403 MissingSpan& missing = missingSpans[index];
michael@0 1404 missing.fSegment->addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt);
michael@0 1405 }
michael@0 1406 for (int index = 0; index < missingCount; ++index) {
michael@0 1407 MissingSpan& missing = missingSpans[index];
michael@0 1408 missing.fSegment->fixOtherTIndex();
michael@0 1409 missing.fOther->fixOtherTIndex();
michael@0 1410 }
michael@0 1411 }
michael@0 1412
michael@0 1413 bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart,
michael@0 1414 int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const {
michael@0 1415 SkASSERT(span->fT == 0 || span->fT == 1);
michael@0 1416 SkASSERT(span->fOtherT == 0 || span->fOtherT == 1);
michael@0 1417 const SkOpSpan* otherSpan = &other->span(oEnd);
michael@0 1418 double refT = otherSpan->fT;
michael@0 1419 const SkPoint& refPt = otherSpan->fPt;
michael@0 1420 const SkOpSpan* lastSpan = &other->span(step > 0 ? other->count() - 1 : 0);
michael@0 1421 do {
michael@0 1422 const SkOpSegment* match = span->fOther;
michael@0 1423 if (match == otherSpan->fOther) {
michael@0 1424 // find start of respective spans and see if both have winding
michael@0 1425 int startIndex, endIndex;
michael@0 1426 if (span->fOtherT == 1) {
michael@0 1427 endIndex = span->fOtherIndex;
michael@0 1428 startIndex = match->nextExactSpan(endIndex, -1);
michael@0 1429 } else {
michael@0 1430 startIndex = span->fOtherIndex;
michael@0 1431 endIndex = match->nextExactSpan(startIndex, 1);
michael@0 1432 }
michael@0 1433 const SkOpSpan& startSpan = match->span(startIndex);
michael@0 1434 if (startSpan.fWindValue != 0) {
michael@0 1435 // draw ray from endSpan.fPt perpendicular to end tangent and measure distance
michael@0 1436 // to other segment.
michael@0 1437 const SkOpSpan& endSpan = match->span(endIndex);
michael@0 1438 SkDLine ray;
michael@0 1439 SkVector dxdy;
michael@0 1440 if (span->fOtherT == 1) {
michael@0 1441 ray.fPts[0].set(startSpan.fPt);
michael@0 1442 dxdy = match->dxdy(startIndex);
michael@0 1443 } else {
michael@0 1444 ray.fPts[0].set(endSpan.fPt);
michael@0 1445 dxdy = match->dxdy(endIndex);
michael@0 1446 }
michael@0 1447 ray.fPts[1].fX = ray.fPts[0].fX + dxdy.fY;
michael@0 1448 ray.fPts[1].fY = ray.fPts[0].fY - dxdy.fX;
michael@0 1449 SkIntersections i;
michael@0 1450 int roots = (i.*CurveRay[SkPathOpsVerbToPoints(other->verb())])(other->pts(), ray);
michael@0 1451 for (int index = 0; index < roots; ++index) {
michael@0 1452 if (ray.fPts[0].approximatelyEqual(i.pt(index))) {
michael@0 1453 double matchMidT = (match->span(startIndex).fT
michael@0 1454 + match->span(endIndex).fT) / 2;
michael@0 1455 SkPoint matchMidPt = match->ptAtT(matchMidT);
michael@0 1456 double otherMidT = (i[0][index] + other->span(oStart).fT) / 2;
michael@0 1457 SkPoint otherMidPt = other->ptAtT(otherMidT);
michael@0 1458 if (SkDPoint::ApproximatelyEqual(matchMidPt, otherMidPt)) {
michael@0 1459 *startPt = startSpan.fPt;
michael@0 1460 *endPt = endSpan.fPt;
michael@0 1461 *endT = endSpan.fT;
michael@0 1462 return true;
michael@0 1463 }
michael@0 1464 }
michael@0 1465 }
michael@0 1466 }
michael@0 1467 return false;
michael@0 1468 }
michael@0 1469 if (otherSpan == lastSpan) {
michael@0 1470 break;
michael@0 1471 }
michael@0 1472 otherSpan += step;
michael@0 1473 } while (otherSpan->fT == refT || otherSpan->fPt == refPt);
michael@0 1474 return false;
michael@0 1475 }
michael@0 1476
michael@0 1477 /*
michael@0 1478 The M and S variable name parts stand for the operators.
michael@0 1479 Mi stands for Minuend (see wiki subtraction, analogous to difference)
michael@0 1480 Su stands for Subtrahend
michael@0 1481 The Opp variable name part designates that the value is for the Opposite operator.
michael@0 1482 Opposite values result from combining coincident spans.
michael@0 1483 */
michael@0 1484 SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
michael@0 1485 bool* unsortable, SkPathOp op, const int xorMiMask,
michael@0 1486 const int xorSuMask) {
michael@0 1487 const int startIndex = *nextStart;
michael@0 1488 const int endIndex = *nextEnd;
michael@0 1489 SkASSERT(startIndex != endIndex);
michael@0 1490 SkDEBUGCODE(const int count = fTs.count());
michael@0 1491 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
michael@0 1492 const int step = SkSign32(endIndex - startIndex);
michael@0 1493 const int end = nextExactSpan(startIndex, step);
michael@0 1494 SkASSERT(end >= 0);
michael@0 1495 SkOpSpan* endSpan = &fTs[end];
michael@0 1496 SkOpSegment* other;
michael@0 1497 if (isSimple(end)) {
michael@0 1498 // mark the smaller of startIndex, endIndex done, and all adjacent
michael@0 1499 // spans with the same T value (but not 'other' spans)
michael@0 1500 #if DEBUG_WINDING
michael@0 1501 SkDebugf("%s simple\n", __FUNCTION__);
michael@0 1502 #endif
michael@0 1503 int min = SkMin32(startIndex, endIndex);
michael@0 1504 if (fTs[min].fDone) {
michael@0 1505 return NULL;
michael@0 1506 }
michael@0 1507 markDoneBinary(min);
michael@0 1508 other = endSpan->fOther;
michael@0 1509 *nextStart = endSpan->fOtherIndex;
michael@0 1510 double startT = other->fTs[*nextStart].fT;
michael@0 1511 *nextEnd = *nextStart;
michael@0 1512 do {
michael@0 1513 *nextEnd += step;
michael@0 1514 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
michael@0 1515 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
michael@0 1516 if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
michael@0 1517 *unsortable = true;
michael@0 1518 return NULL;
michael@0 1519 }
michael@0 1520 return other;
michael@0 1521 }
michael@0 1522 // more than one viable candidate -- measure angles to find best
michael@0 1523 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
michael@0 1524 SkASSERT(startIndex - endIndex != 0);
michael@0 1525 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
michael@0 1526 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
michael@0 1527 int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp, &angles, &sorted);
michael@0 1528 bool sortable = calcWinding != SK_NaN32;
michael@0 1529 if (sortable && sorted.count() == 0) {
michael@0 1530 // if no edge has a computed winding sum, we can go no further
michael@0 1531 *unsortable = true;
michael@0 1532 return NULL;
michael@0 1533 }
michael@0 1534 int angleCount = angles.count();
michael@0 1535 int firstIndex = findStartingEdge(sorted, startIndex, end);
michael@0 1536 SkASSERT(!sortable || firstIndex >= 0);
michael@0 1537 #if DEBUG_SORT
michael@0 1538 debugShowSort(__FUNCTION__, sorted, firstIndex, sortable);
michael@0 1539 #endif
michael@0 1540 if (!sortable) {
michael@0 1541 *unsortable = true;
michael@0 1542 return NULL;
michael@0 1543 }
michael@0 1544 SkASSERT(sorted[firstIndex]->segment() == this);
michael@0 1545 #if DEBUG_WINDING
michael@0 1546 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
michael@0 1547 sorted[firstIndex]->sign());
michael@0 1548 #endif
michael@0 1549 int sumMiWinding = updateWinding(endIndex, startIndex);
michael@0 1550 int sumSuWinding = updateOppWinding(endIndex, startIndex);
michael@0 1551 if (operand()) {
michael@0 1552 SkTSwap<int>(sumMiWinding, sumSuWinding);
michael@0 1553 }
michael@0 1554 int nextIndex = firstIndex + 1;
michael@0 1555 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
michael@0 1556 const SkOpAngle* foundAngle = NULL;
michael@0 1557 bool foundDone = false;
michael@0 1558 // iterate through the angle, and compute everyone's winding
michael@0 1559 SkOpSegment* nextSegment;
michael@0 1560 int activeCount = 0;
michael@0 1561 do {
michael@0 1562 SkASSERT(nextIndex != firstIndex);
michael@0 1563 if (nextIndex == angleCount) {
michael@0 1564 nextIndex = 0;
michael@0 1565 }
michael@0 1566 const SkOpAngle* nextAngle = sorted[nextIndex];
michael@0 1567 nextSegment = nextAngle->segment();
michael@0 1568 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
michael@0 1569 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
michael@0 1570 nextAngle->end(), op, &sumMiWinding, &sumSuWinding,
michael@0 1571 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
michael@0 1572 if (activeAngle) {
michael@0 1573 ++activeCount;
michael@0 1574 if (!foundAngle || (foundDone && activeCount & 1)) {
michael@0 1575 if (nextSegment->isTiny(nextAngle)) {
michael@0 1576 *unsortable = true;
michael@0 1577 return NULL;
michael@0 1578 }
michael@0 1579 foundAngle = nextAngle;
michael@0 1580 foundDone = nextSegment->done(nextAngle);
michael@0 1581 }
michael@0 1582 }
michael@0 1583 if (nextSegment->done()) {
michael@0 1584 continue;
michael@0 1585 }
michael@0 1586 if (nextSegment->isTiny(nextAngle)) {
michael@0 1587 continue;
michael@0 1588 }
michael@0 1589 if (!activeAngle) {
michael@0 1590 nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
michael@0 1591 }
michael@0 1592 SkOpSpan* last = nextAngle->lastMarked();
michael@0 1593 if (last) {
michael@0 1594 *chase->append() = last;
michael@0 1595 #if DEBUG_WINDING
michael@0 1596 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
michael@0 1597 last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
michael@0 1598 last->fSmall);
michael@0 1599 #endif
michael@0 1600 }
michael@0 1601 } while (++nextIndex != lastIndex);
michael@0 1602 markDoneBinary(SkMin32(startIndex, endIndex));
michael@0 1603 if (!foundAngle) {
michael@0 1604 return NULL;
michael@0 1605 }
michael@0 1606 *nextStart = foundAngle->start();
michael@0 1607 *nextEnd = foundAngle->end();
michael@0 1608 nextSegment = foundAngle->segment();
michael@0 1609 #if DEBUG_WINDING
michael@0 1610 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
michael@0 1611 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
michael@0 1612 #endif
michael@0 1613 return nextSegment;
michael@0 1614 }
michael@0 1615
michael@0 1616 SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart,
michael@0 1617 int* nextEnd, bool* unsortable) {
michael@0 1618 const int startIndex = *nextStart;
michael@0 1619 const int endIndex = *nextEnd;
michael@0 1620 SkASSERT(startIndex != endIndex);
michael@0 1621 SkDEBUGCODE(const int count = fTs.count());
michael@0 1622 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
michael@0 1623 const int step = SkSign32(endIndex - startIndex);
michael@0 1624 const int end = nextExactSpan(startIndex, step);
michael@0 1625 SkASSERT(end >= 0);
michael@0 1626 SkOpSpan* endSpan = &fTs[end];
michael@0 1627 SkOpSegment* other;
michael@0 1628 if (isSimple(end)) {
michael@0 1629 // mark the smaller of startIndex, endIndex done, and all adjacent
michael@0 1630 // spans with the same T value (but not 'other' spans)
michael@0 1631 #if DEBUG_WINDING
michael@0 1632 SkDebugf("%s simple\n", __FUNCTION__);
michael@0 1633 #endif
michael@0 1634 int min = SkMin32(startIndex, endIndex);
michael@0 1635 if (fTs[min].fDone) {
michael@0 1636 return NULL;
michael@0 1637 }
michael@0 1638 markDoneUnary(min);
michael@0 1639 other = endSpan->fOther;
michael@0 1640 *nextStart = endSpan->fOtherIndex;
michael@0 1641 double startT = other->fTs[*nextStart].fT;
michael@0 1642 *nextEnd = *nextStart;
michael@0 1643 do {
michael@0 1644 *nextEnd += step;
michael@0 1645 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
michael@0 1646 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
michael@0 1647 if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
michael@0 1648 *unsortable = true;
michael@0 1649 return NULL;
michael@0 1650 }
michael@0 1651 return other;
michael@0 1652 }
michael@0 1653 // more than one viable candidate -- measure angles to find best
michael@0 1654 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
michael@0 1655 SkASSERT(startIndex - endIndex != 0);
michael@0 1656 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
michael@0 1657 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
michael@0 1658 int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding, &angles, &sorted);
michael@0 1659 bool sortable = calcWinding != SK_NaN32;
michael@0 1660 int angleCount = angles.count();
michael@0 1661 int firstIndex = findStartingEdge(sorted, startIndex, end);
michael@0 1662 SkASSERT(!sortable || firstIndex >= 0);
michael@0 1663 #if DEBUG_SORT
michael@0 1664 debugShowSort(__FUNCTION__, sorted, firstIndex, sortable);
michael@0 1665 #endif
michael@0 1666 if (!sortable) {
michael@0 1667 *unsortable = true;
michael@0 1668 return NULL;
michael@0 1669 }
michael@0 1670 SkASSERT(sorted[firstIndex]->segment() == this);
michael@0 1671 #if DEBUG_WINDING
michael@0 1672 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
michael@0 1673 sorted[firstIndex]->sign());
michael@0 1674 #endif
michael@0 1675 int sumWinding = updateWinding(endIndex, startIndex);
michael@0 1676 int nextIndex = firstIndex + 1;
michael@0 1677 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
michael@0 1678 const SkOpAngle* foundAngle = NULL;
michael@0 1679 bool foundDone = false;
michael@0 1680 // iterate through the angle, and compute everyone's winding
michael@0 1681 SkOpSegment* nextSegment;
michael@0 1682 int activeCount = 0;
michael@0 1683 do {
michael@0 1684 SkASSERT(nextIndex != firstIndex);
michael@0 1685 if (nextIndex == angleCount) {
michael@0 1686 nextIndex = 0;
michael@0 1687 }
michael@0 1688 const SkOpAngle* nextAngle = sorted[nextIndex];
michael@0 1689 nextSegment = nextAngle->segment();
michael@0 1690 int maxWinding;
michael@0 1691 bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
michael@0 1692 &maxWinding, &sumWinding);
michael@0 1693 if (activeAngle) {
michael@0 1694 ++activeCount;
michael@0 1695 if (!foundAngle || (foundDone && activeCount & 1)) {
michael@0 1696 if (nextSegment->isTiny(nextAngle)) {
michael@0 1697 *unsortable = true;
michael@0 1698 return NULL;
michael@0 1699 }
michael@0 1700 foundAngle = nextAngle;
michael@0 1701 foundDone = nextSegment->done(nextAngle);
michael@0 1702 }
michael@0 1703 }
michael@0 1704 if (nextSegment->done()) {
michael@0 1705 continue;
michael@0 1706 }
michael@0 1707 if (nextSegment->isTiny(nextAngle)) {
michael@0 1708 continue;
michael@0 1709 }
michael@0 1710 if (!activeAngle) {
michael@0 1711 nextSegment->markAndChaseDoneUnary(nextAngle->start(), nextAngle->end());
michael@0 1712 }
michael@0 1713 SkOpSpan* last = nextAngle->lastMarked();
michael@0 1714 if (last) {
michael@0 1715 *chase->append() = last;
michael@0 1716 #if DEBUG_WINDING
michael@0 1717 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
michael@0 1718 last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
michael@0 1719 last->fSmall);
michael@0 1720 #endif
michael@0 1721 }
michael@0 1722 } while (++nextIndex != lastIndex);
michael@0 1723 markDoneUnary(SkMin32(startIndex, endIndex));
michael@0 1724 if (!foundAngle) {
michael@0 1725 return NULL;
michael@0 1726 }
michael@0 1727 *nextStart = foundAngle->start();
michael@0 1728 *nextEnd = foundAngle->end();
michael@0 1729 nextSegment = foundAngle->segment();
michael@0 1730 #if DEBUG_WINDING
michael@0 1731 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
michael@0 1732 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
michael@0 1733 #endif
michael@0 1734 return nextSegment;
michael@0 1735 }
michael@0 1736
michael@0 1737 SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsortable) {
michael@0 1738 const int startIndex = *nextStart;
michael@0 1739 const int endIndex = *nextEnd;
michael@0 1740 SkASSERT(startIndex != endIndex);
michael@0 1741 SkDEBUGCODE(int count = fTs.count());
michael@0 1742 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
michael@0 1743 int step = SkSign32(endIndex - startIndex);
michael@0 1744 int end = nextExactSpan(startIndex, step);
michael@0 1745 SkASSERT(end >= 0);
michael@0 1746 SkOpSpan* endSpan = &fTs[end];
michael@0 1747 SkOpSegment* other;
michael@0 1748 if (isSimple(end)) {
michael@0 1749 #if DEBUG_WINDING
michael@0 1750 SkDebugf("%s simple\n", __FUNCTION__);
michael@0 1751 #endif
michael@0 1752 int min = SkMin32(startIndex, endIndex);
michael@0 1753 if (fTs[min].fDone) {
michael@0 1754 return NULL;
michael@0 1755 }
michael@0 1756 markDone(min, 1);
michael@0 1757 other = endSpan->fOther;
michael@0 1758 *nextStart = endSpan->fOtherIndex;
michael@0 1759 double startT = other->fTs[*nextStart].fT;
michael@0 1760 // FIXME: I don't know why the logic here is difference from the winding case
michael@0 1761 SkDEBUGCODE(bool firstLoop = true;)
michael@0 1762 if ((approximately_less_than_zero(startT) && step < 0)
michael@0 1763 || (approximately_greater_than_one(startT) && step > 0)) {
michael@0 1764 step = -step;
michael@0 1765 SkDEBUGCODE(firstLoop = false;)
michael@0 1766 }
michael@0 1767 do {
michael@0 1768 *nextEnd = *nextStart;
michael@0 1769 do {
michael@0 1770 *nextEnd += step;
michael@0 1771 } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
michael@0 1772 if (other->fTs[SkMin32(*nextStart, *nextEnd)].fWindValue) {
michael@0 1773 break;
michael@0 1774 }
michael@0 1775 SkASSERT(firstLoop);
michael@0 1776 SkDEBUGCODE(firstLoop = false;)
michael@0 1777 step = -step;
michael@0 1778 } while (true);
michael@0 1779 SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
michael@0 1780 return other;
michael@0 1781 }
michael@0 1782 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
michael@0 1783 SkASSERT(startIndex - endIndex != 0);
michael@0 1784 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
michael@0 1785 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
michael@0 1786 int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryXor, &angles, &sorted);
michael@0 1787 bool sortable = calcWinding != SK_NaN32;
michael@0 1788 int angleCount = angles.count();
michael@0 1789 int firstIndex = findStartingEdge(sorted, startIndex, end);
michael@0 1790 SkASSERT(!sortable || firstIndex >= 0);
michael@0 1791 #if DEBUG_SORT
michael@0 1792 debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0, sortable);
michael@0 1793 #endif
michael@0 1794 if (!sortable) {
michael@0 1795 *unsortable = true;
michael@0 1796 return NULL;
michael@0 1797 }
michael@0 1798 SkASSERT(sorted[firstIndex]->segment() == this);
michael@0 1799 #if DEBUG_WINDING
michael@0 1800 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
michael@0 1801 sorted[firstIndex]->sign());
michael@0 1802 #endif
michael@0 1803 int nextIndex = firstIndex + 1;
michael@0 1804 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
michael@0 1805 const SkOpAngle* foundAngle = NULL;
michael@0 1806 bool foundDone = false;
michael@0 1807 SkOpSegment* nextSegment;
michael@0 1808 int activeCount = 0;
michael@0 1809 do {
michael@0 1810 SkASSERT(nextIndex != firstIndex);
michael@0 1811 if (nextIndex == angleCount) {
michael@0 1812 nextIndex = 0;
michael@0 1813 }
michael@0 1814 const SkOpAngle* nextAngle = sorted[nextIndex];
michael@0 1815 nextSegment = nextAngle->segment();
michael@0 1816 ++activeCount;
michael@0 1817 if (!foundAngle || (foundDone && activeCount & 1)) {
michael@0 1818 if (nextSegment->isTiny(nextAngle)) {
michael@0 1819 *unsortable = true;
michael@0 1820 return NULL;
michael@0 1821 }
michael@0 1822 foundAngle = nextAngle;
michael@0 1823 foundDone = nextSegment->done(nextAngle);
michael@0 1824 }
michael@0 1825 if (nextSegment->done()) {
michael@0 1826 continue;
michael@0 1827 }
michael@0 1828 } while (++nextIndex != lastIndex);
michael@0 1829 markDone(SkMin32(startIndex, endIndex), 1);
michael@0 1830 if (!foundAngle) {
michael@0 1831 return NULL;
michael@0 1832 }
michael@0 1833 *nextStart = foundAngle->start();
michael@0 1834 *nextEnd = foundAngle->end();
michael@0 1835 nextSegment = foundAngle->segment();
michael@0 1836 #if DEBUG_WINDING
michael@0 1837 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
michael@0 1838 __FUNCTION__, debugID(), nextSegment->debugID(), *nextStart, *nextEnd);
michael@0 1839 #endif
michael@0 1840 return nextSegment;
michael@0 1841 }
michael@0 1842
michael@0 1843 int SkOpSegment::findStartingEdge(const SkTArray<SkOpAngle*, true>& sorted, int start, int end) {
michael@0 1844 int angleCount = sorted.count();
michael@0 1845 int firstIndex = -1;
michael@0 1846 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
michael@0 1847 const SkOpAngle* angle = sorted[angleIndex];
michael@0 1848 if (angle->segment() == this && angle->start() == end &&
michael@0 1849 angle->end() == start) {
michael@0 1850 firstIndex = angleIndex;
michael@0 1851 break;
michael@0 1852 }
michael@0 1853 }
michael@0 1854 return firstIndex;
michael@0 1855 }
michael@0 1856
michael@0 1857 int SkOpSegment::findT(double t, const SkOpSegment* match) const {
michael@0 1858 int count = this->count();
michael@0 1859 for (int index = 0; index < count; ++index) {
michael@0 1860 const SkOpSpan& span = fTs[index];
michael@0 1861 if (span.fT == t && span.fOther == match) {
michael@0 1862 return index;
michael@0 1863 }
michael@0 1864 }
michael@0 1865 SkASSERT(0);
michael@0 1866 return -1;
michael@0 1867 }
michael@0 1868
michael@0 1869 // FIXME: either:
michael@0 1870 // a) mark spans with either end unsortable as done, or
michael@0 1871 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
michael@0 1872 // when encountering an unsortable span
michael@0 1873
michael@0 1874 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
michael@0 1875 // and use more concise logic like the old edge walker code?
michael@0 1876 // FIXME: this needs to deal with coincident edges
michael@0 1877 SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable,
michael@0 1878 bool onlySortable) {
michael@0 1879 // iterate through T intersections and return topmost
michael@0 1880 // topmost tangent from y-min to first pt is closer to horizontal
michael@0 1881 SkASSERT(!done());
michael@0 1882 int firstT = -1;
michael@0 1883 /* SkPoint topPt = */ activeLeftTop(onlySortable, &firstT);
michael@0 1884 if (firstT < 0) {
michael@0 1885 *unsortable = true;
michael@0 1886 firstT = 0;
michael@0 1887 while (fTs[firstT].fDone) {
michael@0 1888 SkASSERT(firstT < fTs.count());
michael@0 1889 ++firstT;
michael@0 1890 }
michael@0 1891 *tIndexPtr = firstT;
michael@0 1892 *endIndexPtr = nextExactSpan(firstT, 1);
michael@0 1893 return this;
michael@0 1894 }
michael@0 1895 // sort the edges to find the leftmost
michael@0 1896 int step = 1;
michael@0 1897 int end = nextSpan(firstT, step);
michael@0 1898 if (end == -1) {
michael@0 1899 step = -1;
michael@0 1900 end = nextSpan(firstT, step);
michael@0 1901 SkASSERT(end != -1);
michael@0 1902 }
michael@0 1903 // if the topmost T is not on end, or is three-way or more, find left
michael@0 1904 // look for left-ness from tLeft to firstT (matching y of other)
michael@0 1905 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles;
michael@0 1906 SkASSERT(firstT - end != 0);
michael@0 1907 addTwoAngles(end, firstT, &angles);
michael@0 1908 if (!buildAngles(firstT, &angles, true) && onlySortable) {
michael@0 1909 // *unsortable = true;
michael@0 1910 // return NULL;
michael@0 1911 }
michael@0 1912 SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
michael@0 1913 bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMayBeUnordered_SortAngleKind);
michael@0 1914 if (onlySortable && !sortable) {
michael@0 1915 *unsortable = true;
michael@0 1916 return NULL;
michael@0 1917 }
michael@0 1918 int first = SK_MaxS32;
michael@0 1919 SkScalar top = SK_ScalarMax;
michael@0 1920 int count = sorted.count();
michael@0 1921 for (int index = 0; index < count; ++index) {
michael@0 1922 const SkOpAngle* angle = sorted[index];
michael@0 1923 if (onlySortable && angle->unorderable()) {
michael@0 1924 continue;
michael@0 1925 }
michael@0 1926 SkOpSegment* next = angle->segment();
michael@0 1927 SkPathOpsBounds bounds;
michael@0 1928 next->subDivideBounds(angle->end(), angle->start(), &bounds);
michael@0 1929 if (approximately_greater(top, bounds.fTop)) {
michael@0 1930 top = bounds.fTop;
michael@0 1931 first = index;
michael@0 1932 }
michael@0 1933 }
michael@0 1934 SkASSERT(first < SK_MaxS32);
michael@0 1935 #if DEBUG_SORT // || DEBUG_SWAP_TOP
michael@0 1936 sorted[first]->segment()->debugShowSort(__FUNCTION__, sorted, first, 0, 0, sortable);
michael@0 1937 #endif
michael@0 1938 // skip edges that have already been processed
michael@0 1939 firstT = first - 1;
michael@0 1940 SkOpSegment* leftSegment;
michael@0 1941 do {
michael@0 1942 if (++firstT == count) {
michael@0 1943 firstT = 0;
michael@0 1944 }
michael@0 1945 const SkOpAngle* angle = sorted[firstT];
michael@0 1946 SkASSERT(!onlySortable || !angle->unsortable());
michael@0 1947 leftSegment = angle->segment();
michael@0 1948 *tIndexPtr = angle->end();
michael@0 1949 *endIndexPtr = angle->start();
michael@0 1950 } while (leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone);
michael@0 1951 if (leftSegment->verb() >= SkPath::kQuad_Verb) {
michael@0 1952 const int tIndex = *tIndexPtr;
michael@0 1953 const int endIndex = *endIndexPtr;
michael@0 1954 if (!leftSegment->clockwise(tIndex, endIndex)) {
michael@0 1955 bool swap = !leftSegment->monotonicInY(tIndex, endIndex)
michael@0 1956 && !leftSegment->serpentine(tIndex, endIndex);
michael@0 1957 #if DEBUG_SWAP_TOP
michael@0 1958 SkDebugf("%s swap=%d serpentine=%d containedByEnds=%d monotonic=%d\n", __FUNCTION__,
michael@0 1959 swap,
michael@0 1960 leftSegment->serpentine(tIndex, endIndex),
michael@0 1961 leftSegment->controlsContainedByEnds(tIndex, endIndex),
michael@0 1962 leftSegment->monotonicInY(tIndex, endIndex));
michael@0 1963 #endif
michael@0 1964 if (swap) {
michael@0 1965 // FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
michael@0 1966 // sorted but merely the first not already processed (i.e., not done)
michael@0 1967 SkTSwap(*tIndexPtr, *endIndexPtr);
michael@0 1968 }
michael@0 1969 }
michael@0 1970 }
michael@0 1971 SkASSERT(!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fTiny);
michael@0 1972 return leftSegment;
michael@0 1973 }
michael@0 1974
michael@0 1975 // FIXME: not crazy about this
michael@0 1976 // when the intersections are performed, the other index is into an
michael@0 1977 // incomplete array. As the array grows, the indices become incorrect
michael@0 1978 // while the following fixes the indices up again, it isn't smart about
michael@0 1979 // skipping segments whose indices are already correct
michael@0 1980 // assuming we leave the code that wrote the index in the first place
michael@0 1981 // FIXME: if called after remove, this needs to correct tiny
michael@0 1982 void SkOpSegment::fixOtherTIndex() {
michael@0 1983 int iCount = fTs.count();
michael@0 1984 for (int i = 0; i < iCount; ++i) {
michael@0 1985 SkOpSpan& iSpan = fTs[i];
michael@0 1986 double oT = iSpan.fOtherT;
michael@0 1987 SkOpSegment* other = iSpan.fOther;
michael@0 1988 int oCount = other->fTs.count();
michael@0 1989 SkDEBUGCODE(iSpan.fOtherIndex = -1);
michael@0 1990 for (int o = 0; o < oCount; ++o) {
michael@0 1991 SkOpSpan& oSpan = other->fTs[o];
michael@0 1992 if (oT == oSpan.fT && this == oSpan.fOther && oSpan.fOtherT == iSpan.fT) {
michael@0 1993 iSpan.fOtherIndex = o;
michael@0 1994 oSpan.fOtherIndex = i;
michael@0 1995 break;
michael@0 1996 }
michael@0 1997 }
michael@0 1998 SkASSERT(iSpan.fOtherIndex >= 0);
michael@0 1999 }
michael@0 2000 }
michael@0 2001
michael@0 2002 void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
michael@0 2003 fDoneSpans = 0;
michael@0 2004 fOperand = operand;
michael@0 2005 fXor = evenOdd;
michael@0 2006 fPts = pts;
michael@0 2007 fVerb = verb;
michael@0 2008 }
michael@0 2009
michael@0 2010 void SkOpSegment::initWinding(int start, int end) {
michael@0 2011 int local = spanSign(start, end);
michael@0 2012 int oppLocal = oppSign(start, end);
michael@0 2013 (void) markAndChaseWinding(start, end, local, oppLocal);
michael@0 2014 // OPTIMIZATION: the reverse mark and chase could skip the first marking
michael@0 2015 (void) markAndChaseWinding(end, start, local, oppLocal);
michael@0 2016 }
michael@0 2017
michael@0 2018 /*
michael@0 2019 when we start with a vertical intersect, we try to use the dx to determine if the edge is to
michael@0 2020 the left or the right of vertical. This determines if we need to add the span's
michael@0 2021 sign or not. However, this isn't enough.
michael@0 2022 If the supplied sign (winding) is zero, then we didn't hit another vertical span, so dx is needed.
michael@0 2023 If there was a winding, then it may or may not need adjusting. If the span the winding was borrowed
michael@0 2024 from has the same x direction as this span, the winding should change. If the dx is opposite, then
michael@0 2025 the same winding is shared by both.
michael@0 2026 */
michael@0 2027 void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkScalar hitDx,
michael@0 2028 int oppWind, SkScalar hitOppDx) {
michael@0 2029 SkASSERT(hitDx || !winding);
michael@0 2030 SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
michael@0 2031 SkASSERT(dx);
michael@0 2032 int windVal = windValue(SkMin32(start, end));
michael@0 2033 #if DEBUG_WINDING_AT_T
michael@0 2034 SkDebugf("%s oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, winding,
michael@0 2035 hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
michael@0 2036 #endif
michael@0 2037 if (!winding) {
michael@0 2038 winding = dx < 0 ? windVal : -windVal;
michael@0 2039 } else if (winding * dx < 0) {
michael@0 2040 int sideWind = winding + (dx < 0 ? windVal : -windVal);
michael@0 2041 if (abs(winding) < abs(sideWind)) {
michael@0 2042 winding = sideWind;
michael@0 2043 }
michael@0 2044 }
michael@0 2045 #if DEBUG_WINDING_AT_T
michael@0 2046 SkDebugf(" winding=%d\n", winding);
michael@0 2047 #endif
michael@0 2048 SkDEBUGCODE(int oppLocal = oppSign(start, end));
michael@0 2049 SkASSERT(hitOppDx || !oppWind || !oppLocal);
michael@0 2050 int oppWindVal = oppValue(SkMin32(start, end));
michael@0 2051 if (!oppWind) {
michael@0 2052 oppWind = dx < 0 ? oppWindVal : -oppWindVal;
michael@0 2053 } else if (hitOppDx * dx >= 0) {
michael@0 2054 int oppSideWind = oppWind + (dx < 0 ? oppWindVal : -oppWindVal);
michael@0 2055 if (abs(oppWind) < abs(oppSideWind)) {
michael@0 2056 oppWind = oppSideWind;
michael@0 2057 }
michael@0 2058 }
michael@0 2059 (void) markAndChaseWinding(start, end, winding, oppWind);
michael@0 2060 // OPTIMIZATION: the reverse mark and chase could skip the first marking
michael@0 2061 (void) markAndChaseWinding(end, start, winding, oppWind);
michael@0 2062 }
michael@0 2063
michael@0 2064 // OPTIMIZE: successive calls could start were the last leaves off
michael@0 2065 // or calls could specialize to walk forwards or backwards
michael@0 2066 bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const {
michael@0 2067 size_t tCount = fTs.count();
michael@0 2068 for (size_t index = 0; index < tCount; ++index) {
michael@0 2069 const SkOpSpan& span = fTs[index];
michael@0 2070 if (approximately_zero(startT - span.fT) && pt == span.fPt) {
michael@0 2071 return false;
michael@0 2072 }
michael@0 2073 }
michael@0 2074 return true;
michael@0 2075 }
michael@0 2076
michael@0 2077 bool SkOpSegment::isSimple(int end) const {
michael@0 2078 int count = fTs.count();
michael@0 2079 if (count == 2) {
michael@0 2080 return true;
michael@0 2081 }
michael@0 2082 double t = fTs[end].fT;
michael@0 2083 if (approximately_less_than_zero(t)) {
michael@0 2084 return !approximately_less_than_zero(fTs[1].fT);
michael@0 2085 }
michael@0 2086 if (approximately_greater_than_one(t)) {
michael@0 2087 return !approximately_greater_than_one(fTs[count - 2].fT);
michael@0 2088 }
michael@0 2089 return false;
michael@0 2090 }
michael@0 2091
michael@0 2092 bool SkOpSegment::isTiny(const SkOpAngle* angle) const {
michael@0 2093 int start = angle->start();
michael@0 2094 int end = angle->end();
michael@0 2095 const SkOpSpan& mSpan = fTs[SkMin32(start, end)];
michael@0 2096 return mSpan.fTiny;
michael@0 2097 }
michael@0 2098
michael@0 2099 bool SkOpSegment::isTiny(int index) const {
michael@0 2100 return fTs[index].fTiny;
michael@0 2101 }
michael@0 2102
michael@0 2103 // look pair of active edges going away from coincident edge
michael@0 2104 // one of them should be the continuation of other
michael@0 2105 // if both are active, look to see if they both the connect to another coincident pair
michael@0 2106 // if one at least one is a line, then make the pair coincident
michael@0 2107 // if neither is a line, test for coincidence
michael@0 2108 bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, int step,
michael@0 2109 bool cancel) {
michael@0 2110 int otherTIndex = other->findT(otherT, this);
michael@0 2111 int next = other->nextExactSpan(otherTIndex, step);
michael@0 2112 int otherMin = SkTMin(otherTIndex, next);
michael@0 2113 int otherWind = other->span(otherMin).fWindValue;
michael@0 2114 if (otherWind == 0) {
michael@0 2115 return false;
michael@0 2116 }
michael@0 2117 SkASSERT(next >= 0);
michael@0 2118 int tIndex = 0;
michael@0 2119 do {
michael@0 2120 SkOpSpan* test = &fTs[tIndex];
michael@0 2121 SkASSERT(test->fT == 0);
michael@0 2122 if (test->fOther == other || test->fOtherT != 1) {
michael@0 2123 continue;
michael@0 2124 }
michael@0 2125 SkPoint startPt, endPt;
michael@0 2126 double endT;
michael@0 2127 if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) {
michael@0 2128 SkOpSegment* match = test->fOther;
michael@0 2129 if (cancel) {
michael@0 2130 match->addTCancel(startPt, endPt, other);
michael@0 2131 } else {
michael@0 2132 match->addTCoincident(startPt, endPt, endT, other);
michael@0 2133 }
michael@0 2134 return true;
michael@0 2135 }
michael@0 2136 } while (fTs[++tIndex].fT == 0);
michael@0 2137 return false;
michael@0 2138 }
michael@0 2139
michael@0 2140 // this span is excluded by the winding rule -- chase the ends
michael@0 2141 // as long as they are unambiguous to mark connections as done
michael@0 2142 // and give them the same winding value
michael@0 2143
michael@0 2144 SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
michael@0 2145 int step = SkSign32(endIndex - index);
michael@0 2146 int min = SkMin32(index, endIndex);
michael@0 2147 markDoneBinary(min);
michael@0 2148 SkOpSpan* last;
michael@0 2149 SkOpSegment* other = this;
michael@0 2150 while ((other = other->nextChase(&index, step, &min, &last))) {
michael@0 2151 if (other->done()) {
michael@0 2152 return NULL;
michael@0 2153 }
michael@0 2154 other->markDoneBinary(min);
michael@0 2155 }
michael@0 2156 return last;
michael@0 2157 }
michael@0 2158
michael@0 2159 SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
michael@0 2160 int step = SkSign32(endIndex - index);
michael@0 2161 int min = SkMin32(index, endIndex);
michael@0 2162 markDoneUnary(min);
michael@0 2163 SkOpSpan* last;
michael@0 2164 SkOpSegment* other = this;
michael@0 2165 while ((other = other->nextChase(&index, step, &min, &last))) {
michael@0 2166 if (other->done()) {
michael@0 2167 return NULL;
michael@0 2168 }
michael@0 2169 other->markDoneUnary(min);
michael@0 2170 }
michael@0 2171 return last;
michael@0 2172 }
michael@0 2173
michael@0 2174 SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, const int winding) {
michael@0 2175 int index = angle->start();
michael@0 2176 int endIndex = angle->end();
michael@0 2177 int step = SkSign32(endIndex - index);
michael@0 2178 int min = SkMin32(index, endIndex);
michael@0 2179 markWinding(min, winding);
michael@0 2180 SkOpSpan* last;
michael@0 2181 SkOpSegment* other = this;
michael@0 2182 while ((other = other->nextChase(&index, step, &min, &last))) {
michael@0 2183 if (other->fTs[min].fWindSum != SK_MinS32) {
michael@0 2184 SkASSERT(other->fTs[min].fWindSum == winding);
michael@0 2185 return NULL;
michael@0 2186 }
michael@0 2187 other->markWinding(min, winding);
michael@0 2188 }
michael@0 2189 return last;
michael@0 2190 }
michael@0 2191
michael@0 2192 SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) {
michael@0 2193 int min = SkMin32(index, endIndex);
michael@0 2194 int step = SkSign32(endIndex - index);
michael@0 2195 markWinding(min, winding, oppWinding);
michael@0 2196 SkOpSpan* last;
michael@0 2197 SkOpSegment* other = this;
michael@0 2198 while ((other = other->nextChase(&index, step, &min, &last))) {
michael@0 2199 if (other->fTs[min].fWindSum != SK_MinS32) {
michael@0 2200 SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop);
michael@0 2201 return NULL;
michael@0 2202 }
michael@0 2203 other->markWinding(min, winding, oppWinding);
michael@0 2204 }
michael@0 2205 return last;
michael@0 2206 }
michael@0 2207
michael@0 2208 SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding) {
michael@0 2209 int start = angle->start();
michael@0 2210 int end = angle->end();
michael@0 2211 return markAndChaseWinding(start, end, winding, oppWinding);
michael@0 2212 }
michael@0 2213
michael@0 2214 SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
michael@0 2215 SkASSERT(angle->segment() == this);
michael@0 2216 if (UseInnerWinding(maxWinding, sumWinding)) {
michael@0 2217 maxWinding = sumWinding;
michael@0 2218 }
michael@0 2219 SkOpSpan* last = markAndChaseWinding(angle, maxWinding);
michael@0 2220 #if DEBUG_WINDING
michael@0 2221 if (last) {
michael@0 2222 SkDebugf("%s last id=%d windSum=", __FUNCTION__,
michael@0 2223 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
michael@0 2224 SkPathOpsDebug::WindingPrintf(last->fWindSum);
michael@0 2225 SkDebugf(" small=%d\n", last->fSmall);
michael@0 2226 }
michael@0 2227 #endif
michael@0 2228 return last;
michael@0 2229 }
michael@0 2230
michael@0 2231 SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
michael@0 2232 int oppSumWinding, const SkOpAngle* angle) {
michael@0 2233 SkASSERT(angle->segment() == this);
michael@0 2234 if (UseInnerWinding(maxWinding, sumWinding)) {
michael@0 2235 maxWinding = sumWinding;
michael@0 2236 }
michael@0 2237 if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
michael@0 2238 oppMaxWinding = oppSumWinding;
michael@0 2239 }
michael@0 2240 SkOpSpan* last = markAndChaseWinding(angle, maxWinding, oppMaxWinding);
michael@0 2241 #if DEBUG_WINDING
michael@0 2242 if (last) {
michael@0 2243 SkDebugf("%s last id=%d windSum=", __FUNCTION__,
michael@0 2244 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
michael@0 2245 SkPathOpsDebug::WindingPrintf(last->fWindSum);
michael@0 2246 SkDebugf(" small=%d\n", last->fSmall);
michael@0 2247 }
michael@0 2248 #endif
michael@0 2249 return last;
michael@0 2250 }
michael@0 2251
michael@0 2252 // FIXME: this should also mark spans with equal (x,y)
michael@0 2253 // This may be called when the segment is already marked done. While this
michael@0 2254 // wastes time, it shouldn't do any more than spin through the T spans.
michael@0 2255 // OPTIMIZATION: abort on first done found (assuming that this code is
michael@0 2256 // always called to mark segments done).
michael@0 2257 void SkOpSegment::markDone(int index, int winding) {
michael@0 2258 // SkASSERT(!done());
michael@0 2259 SkASSERT(winding);
michael@0 2260 double referenceT = fTs[index].fT;
michael@0 2261 int lesser = index;
michael@0 2262 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
michael@0 2263 markOneDone(__FUNCTION__, lesser, winding);
michael@0 2264 }
michael@0 2265 do {
michael@0 2266 markOneDone(__FUNCTION__, index, winding);
michael@0 2267 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
michael@0 2268 }
michael@0 2269
michael@0 2270 void SkOpSegment::markDoneBinary(int index) {
michael@0 2271 double referenceT = fTs[index].fT;
michael@0 2272 int lesser = index;
michael@0 2273 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
michael@0 2274 markOneDoneBinary(__FUNCTION__, lesser);
michael@0 2275 }
michael@0 2276 do {
michael@0 2277 markOneDoneBinary(__FUNCTION__, index);
michael@0 2278 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
michael@0 2279 }
michael@0 2280
michael@0 2281 void SkOpSegment::markDoneUnary(int index) {
michael@0 2282 double referenceT = fTs[index].fT;
michael@0 2283 int lesser = index;
michael@0 2284 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
michael@0 2285 markOneDoneUnary(__FUNCTION__, lesser);
michael@0 2286 }
michael@0 2287 do {
michael@0 2288 markOneDoneUnary(__FUNCTION__, index);
michael@0 2289 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
michael@0 2290 }
michael@0 2291
michael@0 2292 void SkOpSegment::markOneDone(const char* funName, int tIndex, int winding) {
michael@0 2293 SkOpSpan* span = markOneWinding(funName, tIndex, winding);
michael@0 2294 if (!span) {
michael@0 2295 return;
michael@0 2296 }
michael@0 2297 span->fDone = true;
michael@0 2298 fDoneSpans++;
michael@0 2299 }
michael@0 2300
michael@0 2301 void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex) {
michael@0 2302 SkOpSpan* span = verifyOneWinding(funName, tIndex);
michael@0 2303 if (!span) {
michael@0 2304 return;
michael@0 2305 }
michael@0 2306 span->fDone = true;
michael@0 2307 fDoneSpans++;
michael@0 2308 }
michael@0 2309
michael@0 2310 void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) {
michael@0 2311 SkOpSpan* span = verifyOneWindingU(funName, tIndex);
michael@0 2312 if (!span) {
michael@0 2313 return;
michael@0 2314 }
michael@0 2315 span->fDone = true;
michael@0 2316 fDoneSpans++;
michael@0 2317 }
michael@0 2318
michael@0 2319 SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding) {
michael@0 2320 SkOpSpan& span = fTs[tIndex];
michael@0 2321 if (span.fDone) {
michael@0 2322 return NULL;
michael@0 2323 }
michael@0 2324 #if DEBUG_MARK_DONE
michael@0 2325 debugShowNewWinding(funName, span, winding);
michael@0 2326 #endif
michael@0 2327 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
michael@0 2328 SkASSERT(abs(winding) <= SkPathOpsDebug::gMaxWindSum);
michael@0 2329 span.fWindSum = winding;
michael@0 2330 return &span;
michael@0 2331 }
michael@0 2332
michael@0 2333 SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
michael@0 2334 int oppWinding) {
michael@0 2335 SkOpSpan& span = fTs[tIndex];
michael@0 2336 if (span.fDone && !span.fSmall) {
michael@0 2337 return NULL;
michael@0 2338 }
michael@0 2339 #if DEBUG_MARK_DONE
michael@0 2340 debugShowNewWinding(funName, span, winding, oppWinding);
michael@0 2341 #endif
michael@0 2342 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
michael@0 2343 SkASSERT(abs(winding) <= SkPathOpsDebug::gMaxWindSum);
michael@0 2344 span.fWindSum = winding;
michael@0 2345 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
michael@0 2346 SkASSERT(abs(oppWinding) <= SkPathOpsDebug::gMaxWindSum);
michael@0 2347 span.fOppSum = oppWinding;
michael@0 2348 return &span;
michael@0 2349 }
michael@0 2350
michael@0 2351 // from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
michael@0 2352 bool SkOpSegment::clockwise(int tStart, int tEnd) const {
michael@0 2353 SkASSERT(fVerb != SkPath::kLine_Verb);
michael@0 2354 SkPoint edge[4];
michael@0 2355 subDivide(tStart, tEnd, edge);
michael@0 2356 int points = SkPathOpsVerbToPoints(fVerb);
michael@0 2357 double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
michael@0 2358 if (fVerb == SkPath::kCubic_Verb) {
michael@0 2359 SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY);
michael@0 2360 if (edge[1].fY < lesser && edge[2].fY < lesser) {
michael@0 2361 SkDLine tangent1 = {{ {edge[0].fX, edge[0].fY}, {edge[1].fX, edge[1].fY} }};
michael@0 2362 SkDLine tangent2 = {{ {edge[2].fX, edge[2].fY}, {edge[3].fX, edge[3].fY} }};
michael@0 2363 if (SkIntersections::Test(tangent1, tangent2)) {
michael@0 2364 SkPoint topPt = cubic_top(fPts, fTs[tStart].fT, fTs[tEnd].fT);
michael@0 2365 sum += (topPt.fX - edge[0].fX) * (topPt.fY + edge[0].fY);
michael@0 2366 sum += (edge[3].fX - topPt.fX) * (edge[3].fY + topPt.fY);
michael@0 2367 return sum <= 0;
michael@0 2368 }
michael@0 2369 }
michael@0 2370 }
michael@0 2371 for (int idx = 0; idx < points; ++idx){
michael@0 2372 sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
michael@0 2373 }
michael@0 2374 return sum <= 0;
michael@0 2375 }
michael@0 2376
michael@0 2377 bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
michael@0 2378 if (fVerb == SkPath::kLine_Verb) {
michael@0 2379 return false;
michael@0 2380 }
michael@0 2381 if (fVerb == SkPath::kQuad_Verb) {
michael@0 2382 SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
michael@0 2383 return dst.monotonicInY();
michael@0 2384 }
michael@0 2385 SkASSERT(fVerb == SkPath::kCubic_Verb);
michael@0 2386 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
michael@0 2387 return dst.monotonicInY();
michael@0 2388 }
michael@0 2389
michael@0 2390 bool SkOpSegment::serpentine(int tStart, int tEnd) const {
michael@0 2391 if (fVerb != SkPath::kCubic_Verb) {
michael@0 2392 return false;
michael@0 2393 }
michael@0 2394 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
michael@0 2395 return dst.serpentine();
michael@0 2396 }
michael@0 2397
michael@0 2398 SkOpSpan* SkOpSegment::verifyOneWinding(const char* funName, int tIndex) {
michael@0 2399 SkOpSpan& span = fTs[tIndex];
michael@0 2400 if (span.fDone) {
michael@0 2401 return NULL;
michael@0 2402 }
michael@0 2403 #if DEBUG_MARK_DONE
michael@0 2404 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
michael@0 2405 #endif
michael@0 2406 // If the prior angle in the sort is unorderable, the winding sum may not be computable.
michael@0 2407 // To enable the assert, the 'prior is unorderable' state could be
michael@0 2408 // piped down to this test, but not sure it's worth it.
michael@0 2409 // (Once the sort order is stored in the span, this test may be feasible.)
michael@0 2410 // SkASSERT(span.fWindSum != SK_MinS32);
michael@0 2411 // SkASSERT(span.fOppSum != SK_MinS32);
michael@0 2412 return &span;
michael@0 2413 }
michael@0 2414
michael@0 2415 SkOpSpan* SkOpSegment::verifyOneWindingU(const char* funName, int tIndex) {
michael@0 2416 SkOpSpan& span = fTs[tIndex];
michael@0 2417 if (span.fDone) {
michael@0 2418 return NULL;
michael@0 2419 }
michael@0 2420 #if DEBUG_MARK_DONE
michael@0 2421 debugShowNewWinding(funName, span, span.fWindSum);
michael@0 2422 #endif
michael@0 2423 // If the prior angle in the sort is unorderable, the winding sum may not be computable.
michael@0 2424 // To enable the assert, the 'prior is unorderable' state could be
michael@0 2425 // piped down to this test, but not sure it's worth it.
michael@0 2426 // (Once the sort order is stored in the span, this test may be feasible.)
michael@0 2427 // SkASSERT(span.fWindSum != SK_MinS32);
michael@0 2428 return &span;
michael@0 2429 }
michael@0 2430
michael@0 2431 // note that just because a span has one end that is unsortable, that's
michael@0 2432 // not enough to mark it done. The other end may be sortable, allowing the
michael@0 2433 // span to be added.
michael@0 2434 // FIXME: if abs(start - end) > 1, mark intermediates as unsortable on both ends
michael@0 2435 void SkOpSegment::markUnsortable(int start, int end) {
michael@0 2436 SkOpSpan* span = &fTs[start];
michael@0 2437 if (start < end) {
michael@0 2438 #if DEBUG_UNSORTABLE
michael@0 2439 debugShowNewWinding(__FUNCTION__, *span, 0);
michael@0 2440 #endif
michael@0 2441 span->fUnsortableStart = true;
michael@0 2442 } else {
michael@0 2443 --span;
michael@0 2444 #if DEBUG_UNSORTABLE
michael@0 2445 debugShowNewWinding(__FUNCTION__, *span, 0);
michael@0 2446 #endif
michael@0 2447 span->fUnsortableEnd = true;
michael@0 2448 }
michael@0 2449 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
michael@0 2450 return;
michael@0 2451 }
michael@0 2452 span->fDone = true;
michael@0 2453 fDoneSpans++;
michael@0 2454 }
michael@0 2455
michael@0 2456 void SkOpSegment::markWinding(int index, int winding) {
michael@0 2457 // SkASSERT(!done());
michael@0 2458 SkASSERT(winding);
michael@0 2459 double referenceT = fTs[index].fT;
michael@0 2460 int lesser = index;
michael@0 2461 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
michael@0 2462 markOneWinding(__FUNCTION__, lesser, winding);
michael@0 2463 }
michael@0 2464 do {
michael@0 2465 markOneWinding(__FUNCTION__, index, winding);
michael@0 2466 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
michael@0 2467 }
michael@0 2468
michael@0 2469 void SkOpSegment::markWinding(int index, int winding, int oppWinding) {
michael@0 2470 // SkASSERT(!done());
michael@0 2471 SkASSERT(winding || oppWinding);
michael@0 2472 double referenceT = fTs[index].fT;
michael@0 2473 int lesser = index;
michael@0 2474 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
michael@0 2475 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
michael@0 2476 }
michael@0 2477 do {
michael@0 2478 markOneWinding(__FUNCTION__, index, winding, oppWinding);
michael@0 2479 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
michael@0 2480 }
michael@0 2481
michael@0 2482 void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) {
michael@0 2483 int nextDoorWind = SK_MaxS32;
michael@0 2484 int nextOppWind = SK_MaxS32;
michael@0 2485 if (tIndex > 0) {
michael@0 2486 const SkOpSpan& below = fTs[tIndex - 1];
michael@0 2487 if (approximately_negative(t - below.fT)) {
michael@0 2488 nextDoorWind = below.fWindValue;
michael@0 2489 nextOppWind = below.fOppValue;
michael@0 2490 }
michael@0 2491 }
michael@0 2492 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
michael@0 2493 const SkOpSpan& above = fTs[tIndex + 1];
michael@0 2494 if (approximately_negative(above.fT - t)) {
michael@0 2495 nextDoorWind = above.fWindValue;
michael@0 2496 nextOppWind = above.fOppValue;
michael@0 2497 }
michael@0 2498 }
michael@0 2499 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
michael@0 2500 const SkOpSpan& below = fTs[tIndex - 1];
michael@0 2501 nextDoorWind = below.fWindValue;
michael@0 2502 nextOppWind = below.fOppValue;
michael@0 2503 }
michael@0 2504 if (nextDoorWind != SK_MaxS32) {
michael@0 2505 SkOpSpan& newSpan = fTs[tIndex];
michael@0 2506 newSpan.fWindValue = nextDoorWind;
michael@0 2507 newSpan.fOppValue = nextOppWind;
michael@0 2508 if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
michael@0 2509 newSpan.fDone = true;
michael@0 2510 ++fDoneSpans;
michael@0 2511 }
michael@0 2512 }
michael@0 2513 }
michael@0 2514
michael@0 2515 // return span if when chasing, two or more radiating spans are not done
michael@0 2516 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
michael@0 2517 // candidate and the remaining spans have windValue == 0 (canceled by
michael@0 2518 // coincidence). The coincident edges could either be removed altogether,
michael@0 2519 // or this code could be more complicated in detecting this case. Worth it?
michael@0 2520 bool SkOpSegment::multipleSpans(int end) const {
michael@0 2521 return end > 0 && end < fTs.count() - 1;
michael@0 2522 }
michael@0 2523
michael@0 2524 bool SkOpSegment::nextCandidate(int* start, int* end) const {
michael@0 2525 while (fTs[*end].fDone) {
michael@0 2526 if (fTs[*end].fT == 1) {
michael@0 2527 return false;
michael@0 2528 }
michael@0 2529 ++(*end);
michael@0 2530 }
michael@0 2531 *start = *end;
michael@0 2532 *end = nextExactSpan(*start, 1);
michael@0 2533 return true;
michael@0 2534 }
michael@0 2535
michael@0 2536 SkOpSegment* SkOpSegment::nextChase(int* index, const int step, int* min, SkOpSpan** last) {
michael@0 2537 int end = nextExactSpan(*index, step);
michael@0 2538 SkASSERT(end >= 0);
michael@0 2539 if (fTs[end].fSmall) {
michael@0 2540 *last = NULL;
michael@0 2541 return NULL;
michael@0 2542 }
michael@0 2543 if (multipleSpans(end)) {
michael@0 2544 *last = &fTs[end];
michael@0 2545 return NULL;
michael@0 2546 }
michael@0 2547 const SkOpSpan& endSpan = fTs[end];
michael@0 2548 SkOpSegment* other = endSpan.fOther;
michael@0 2549 *index = endSpan.fOtherIndex;
michael@0 2550 SkASSERT(*index >= 0);
michael@0 2551 int otherEnd = other->nextExactSpan(*index, step);
michael@0 2552 SkASSERT(otherEnd >= 0);
michael@0 2553 *min = SkMin32(*index, otherEnd);
michael@0 2554 if (other->fTs[*min].fSmall) {
michael@0 2555 *last = NULL;
michael@0 2556 return NULL;
michael@0 2557 }
michael@0 2558 return other;
michael@0 2559 }
michael@0 2560
michael@0 2561 // This has callers for two different situations: one establishes the end
michael@0 2562 // of the current span, and one establishes the beginning of the next span
michael@0 2563 // (thus the name). When this is looking for the end of the current span,
michael@0 2564 // coincidence is found when the beginning Ts contain -step and the end
michael@0 2565 // contains step. When it is looking for the beginning of the next, the
michael@0 2566 // first Ts found can be ignored and the last Ts should contain -step.
michael@0 2567 // OPTIMIZATION: probably should split into two functions
michael@0 2568 int SkOpSegment::nextSpan(int from, int step) const {
michael@0 2569 const SkOpSpan& fromSpan = fTs[from];
michael@0 2570 int count = fTs.count();
michael@0 2571 int to = from;
michael@0 2572 while (step > 0 ? ++to < count : --to >= 0) {
michael@0 2573 const SkOpSpan& span = fTs[to];
michael@0 2574 if (approximately_zero(span.fT - fromSpan.fT)) {
michael@0 2575 continue;
michael@0 2576 }
michael@0 2577 return to;
michael@0 2578 }
michael@0 2579 return -1;
michael@0 2580 }
michael@0 2581
michael@0 2582 // FIXME
michael@0 2583 // this returns at any difference in T, vs. a preset minimum. It may be
michael@0 2584 // that all callers to nextSpan should use this instead.
michael@0 2585 int SkOpSegment::nextExactSpan(int from, int step) const {
michael@0 2586 int to = from;
michael@0 2587 if (step < 0) {
michael@0 2588 const SkOpSpan& fromSpan = fTs[from];
michael@0 2589 while (--to >= 0) {
michael@0 2590 const SkOpSpan& span = fTs[to];
michael@0 2591 if (precisely_negative(fromSpan.fT - span.fT) || span.fTiny) {
michael@0 2592 continue;
michael@0 2593 }
michael@0 2594 return to;
michael@0 2595 }
michael@0 2596 } else {
michael@0 2597 while (fTs[from].fTiny) {
michael@0 2598 from++;
michael@0 2599 }
michael@0 2600 const SkOpSpan& fromSpan = fTs[from];
michael@0 2601 int count = fTs.count();
michael@0 2602 while (++to < count) {
michael@0 2603 const SkOpSpan& span = fTs[to];
michael@0 2604 if (precisely_negative(span.fT - fromSpan.fT)) {
michael@0 2605 continue;
michael@0 2606 }
michael@0 2607 return to;
michael@0 2608 }
michael@0 2609 }
michael@0 2610 return -1;
michael@0 2611 }
michael@0 2612
michael@0 2613 void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
michael@0 2614 int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) {
michael@0 2615 int deltaSum = spanSign(index, endIndex);
michael@0 2616 int oppDeltaSum = oppSign(index, endIndex);
michael@0 2617 if (operand()) {
michael@0 2618 *maxWinding = *sumSuWinding;
michael@0 2619 *sumWinding = *sumSuWinding -= deltaSum;
michael@0 2620 *oppMaxWinding = *sumMiWinding;
michael@0 2621 *oppSumWinding = *sumMiWinding -= oppDeltaSum;
michael@0 2622 } else {
michael@0 2623 *maxWinding = *sumMiWinding;
michael@0 2624 *sumWinding = *sumMiWinding -= deltaSum;
michael@0 2625 *oppMaxWinding = *sumSuWinding;
michael@0 2626 *oppSumWinding = *sumSuWinding -= oppDeltaSum;
michael@0 2627 }
michael@0 2628 SkASSERT(abs(*sumWinding) <= SkPathOpsDebug::gMaxWindSum);
michael@0 2629 SkASSERT(abs(*oppSumWinding) <= SkPathOpsDebug::gMaxWindSum);
michael@0 2630 }
michael@0 2631
michael@0 2632 void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding,
michael@0 2633 int* maxWinding, int* sumWinding) {
michael@0 2634 int deltaSum = spanSign(index, endIndex);
michael@0 2635 *maxWinding = *sumMiWinding;
michael@0 2636 *sumWinding = *sumMiWinding -= deltaSum;
michael@0 2637 SkASSERT(abs(*sumWinding) <= SkPathOpsDebug::gMaxWindSum);
michael@0 2638 }
michael@0 2639
michael@0 2640 // This marks all spans unsortable so that this info is available for early
michael@0 2641 // exclusion in find top and others. This could be optimized to only mark
michael@0 2642 // adjacent spans that unsortable. However, this makes it difficult to later
michael@0 2643 // determine starting points for edge detection in find top and the like.
michael@0 2644 bool SkOpSegment::SortAngles(const SkTArray<SkOpAngle, true>& angles,
michael@0 2645 SkTArray<SkOpAngle*, true>* angleList,
michael@0 2646 SortAngleKind orderKind) {
michael@0 2647 bool sortable = true;
michael@0 2648 int angleCount = angles.count();
michael@0 2649 int angleIndex;
michael@0 2650 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
michael@0 2651 const SkOpAngle& angle = angles[angleIndex];
michael@0 2652 angleList->push_back(const_cast<SkOpAngle*>(&angle));
michael@0 2653 #if DEBUG_ANGLE
michael@0 2654 (*(angleList->end() - 1))->setID(angleIndex);
michael@0 2655 #endif
michael@0 2656 sortable &= !(angle.unsortable() || (orderKind == kMustBeOrdered_SortAngleKind
michael@0 2657 && angle.unorderable()));
michael@0 2658 }
michael@0 2659 if (sortable) {
michael@0 2660 SkTQSort<SkOpAngle>(angleList->begin(), angleList->end() - 1);
michael@0 2661 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
michael@0 2662 if (angles[angleIndex].unsortable() || (orderKind == kMustBeOrdered_SortAngleKind
michael@0 2663 && angles[angleIndex].unorderable())) {
michael@0 2664 sortable = false;
michael@0 2665 break;
michael@0 2666 }
michael@0 2667 }
michael@0 2668 }
michael@0 2669 if (!sortable) {
michael@0 2670 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
michael@0 2671 const SkOpAngle& angle = angles[angleIndex];
michael@0 2672 angle.segment()->markUnsortable(angle.start(), angle.end());
michael@0 2673 }
michael@0 2674 }
michael@0 2675 return sortable;
michael@0 2676 }
michael@0 2677
michael@0 2678 // set segments to unsortable if angle is unsortable, but do not set all angles
michael@0 2679 // note that for a simple 4 way crossing, two of the edges may be orderable even though
michael@0 2680 // two edges are too short to be orderable.
michael@0 2681 // perhaps some classes of unsortable angles should make all shared angles unsortable, but
michael@0 2682 // simple lines that have tiny crossings are always sortable on the large ends
michael@0 2683 // OPTIMIZATION: check earlier when angles are added to input if any are unsortable
michael@0 2684 // may make sense then to mark all segments in angle sweep as unsortableStart/unsortableEnd
michael@0 2685 // solely for the purpose of short-circuiting future angle building around this center
michael@0 2686 bool SkOpSegment::SortAngles2(const SkTArray<SkOpAngle, true>& angles,
michael@0 2687 SkTArray<SkOpAngle*, true>* angleList) {
michael@0 2688 int angleCount = angles.count();
michael@0 2689 int angleIndex;
michael@0 2690 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
michael@0 2691 const SkOpAngle& angle = angles[angleIndex];
michael@0 2692 if (angle.unsortable()) {
michael@0 2693 return false;
michael@0 2694 }
michael@0 2695 angleList->push_back(const_cast<SkOpAngle*>(&angle));
michael@0 2696 #if DEBUG_ANGLE
michael@0 2697 (*(angleList->end() - 1))->setID(angleIndex);
michael@0 2698 #endif
michael@0 2699 }
michael@0 2700 SkTQSort<SkOpAngle>(angleList->begin(), angleList->end() - 1);
michael@0 2701 // at this point angles are sorted but individually may not be orderable
michael@0 2702 // this means that only adjcent orderable segments may transfer winding
michael@0 2703 return true;
michael@0 2704 }
michael@0 2705
michael@0 2706 // return true if midpoints were computed
michael@0 2707 bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
michael@0 2708 SkASSERT(start != end);
michael@0 2709 edge[0] = fTs[start].fPt;
michael@0 2710 int points = SkPathOpsVerbToPoints(fVerb);
michael@0 2711 edge[points] = fTs[end].fPt;
michael@0 2712 if (fVerb == SkPath::kLine_Verb) {
michael@0 2713 return false;
michael@0 2714 }
michael@0 2715 double startT = fTs[start].fT;
michael@0 2716 double endT = fTs[end].fT;
michael@0 2717 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
michael@0 2718 // don't compute midpoints if we already have them
michael@0 2719 if (fVerb == SkPath::kQuad_Verb) {
michael@0 2720 edge[1] = fPts[1];
michael@0 2721 return false;
michael@0 2722 }
michael@0 2723 SkASSERT(fVerb == SkPath::kCubic_Verb);
michael@0 2724 if (start < end) {
michael@0 2725 edge[1] = fPts[1];
michael@0 2726 edge[2] = fPts[2];
michael@0 2727 return false;
michael@0 2728 }
michael@0 2729 edge[1] = fPts[2];
michael@0 2730 edge[2] = fPts[1];
michael@0 2731 return false;
michael@0 2732 }
michael@0 2733 const SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[points].fX, edge[points].fY }};
michael@0 2734 if (fVerb == SkPath::kQuad_Verb) {
michael@0 2735 edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], startT, endT).asSkPoint();
michael@0 2736 } else {
michael@0 2737 SkASSERT(fVerb == SkPath::kCubic_Verb);
michael@0 2738 SkDPoint ctrl[2];
michael@0 2739 SkDCubic::SubDivide(fPts, sub[0], sub[1], startT, endT, ctrl);
michael@0 2740 edge[1] = ctrl[0].asSkPoint();
michael@0 2741 edge[2] = ctrl[1].asSkPoint();
michael@0 2742 }
michael@0 2743 return true;
michael@0 2744 }
michael@0 2745
michael@0 2746 // return true if midpoints were computed
michael@0 2747 bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
michael@0 2748 SkASSERT(start != end);
michael@0 2749 (*result)[0].set(fTs[start].fPt);
michael@0 2750 int points = SkPathOpsVerbToPoints(fVerb);
michael@0 2751 (*result)[points].set(fTs[end].fPt);
michael@0 2752 if (fVerb == SkPath::kLine_Verb) {
michael@0 2753 return false;
michael@0 2754 }
michael@0 2755 double startT = fTs[start].fT;
michael@0 2756 double endT = fTs[end].fT;
michael@0 2757 if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
michael@0 2758 // don't compute midpoints if we already have them
michael@0 2759 if (fVerb == SkPath::kQuad_Verb) {
michael@0 2760 (*result)[1].set(fPts[1]);
michael@0 2761 return false;
michael@0 2762 }
michael@0 2763 SkASSERT(fVerb == SkPath::kCubic_Verb);
michael@0 2764 if (start < end) {
michael@0 2765 (*result)[1].set(fPts[1]);
michael@0 2766 (*result)[2].set(fPts[2]);
michael@0 2767 return false;
michael@0 2768 }
michael@0 2769 (*result)[1].set(fPts[2]);
michael@0 2770 (*result)[2].set(fPts[1]);
michael@0 2771 return false;
michael@0 2772 }
michael@0 2773 if (fVerb == SkPath::kQuad_Verb) {
michael@0 2774 (*result)[1] = SkDQuad::SubDivide(fPts, (*result)[0], (*result)[2], startT, endT);
michael@0 2775 } else {
michael@0 2776 SkASSERT(fVerb == SkPath::kCubic_Verb);
michael@0 2777 SkDCubic::SubDivide(fPts, (*result)[0], (*result)[3], startT, endT, &(*result)[1]);
michael@0 2778 }
michael@0 2779 return true;
michael@0 2780 }
michael@0 2781
michael@0 2782 void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
michael@0 2783 SkPoint edge[4];
michael@0 2784 subDivide(start, end, edge);
michael@0 2785 (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
michael@0 2786 }
michael@0 2787
michael@0 2788 void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt,
michael@0 2789 const SkPoint& startPt) {
michael@0 2790 int outCount = outsidePts->count();
michael@0 2791 if (outCount == 0 || endPt != (*outsidePts)[outCount - 2]) {
michael@0 2792 outsidePts->push_back(endPt);
michael@0 2793 outsidePts->push_back(startPt);
michael@0 2794 }
michael@0 2795 }
michael@0 2796
michael@0 2797 void SkOpSegment::TrackOutside(SkTArray<SkPoint, true>* outsidePts, const SkPoint& startPt) {
michael@0 2798 int outCount = outsidePts->count();
michael@0 2799 if (outCount == 0 || startPt != (*outsidePts)[outCount - 1]) {
michael@0 2800 outsidePts->push_back(startPt);
michael@0 2801 }
michael@0 2802 }
michael@0 2803
michael@0 2804 void SkOpSegment::undoneSpan(int* start, int* end) {
michael@0 2805 size_t tCount = fTs.count();
michael@0 2806 size_t index;
michael@0 2807 for (index = 0; index < tCount; ++index) {
michael@0 2808 if (!fTs[index].fDone) {
michael@0 2809 break;
michael@0 2810 }
michael@0 2811 }
michael@0 2812 SkASSERT(index < tCount - 1);
michael@0 2813 *start = index;
michael@0 2814 double startT = fTs[index].fT;
michael@0 2815 while (approximately_negative(fTs[++index].fT - startT))
michael@0 2816 SkASSERT(index < tCount);
michael@0 2817 SkASSERT(index < tCount);
michael@0 2818 *end = index;
michael@0 2819 }
michael@0 2820
michael@0 2821 int SkOpSegment::updateOppWinding(int index, int endIndex) const {
michael@0 2822 int lesser = SkMin32(index, endIndex);
michael@0 2823 int oppWinding = oppSum(lesser);
michael@0 2824 int oppSpanWinding = oppSign(index, endIndex);
michael@0 2825 if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
michael@0 2826 && oppWinding != SK_MaxS32) {
michael@0 2827 oppWinding -= oppSpanWinding;
michael@0 2828 }
michael@0 2829 return oppWinding;
michael@0 2830 }
michael@0 2831
michael@0 2832 int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
michael@0 2833 int startIndex = angle->start();
michael@0 2834 int endIndex = angle->end();
michael@0 2835 return updateOppWinding(endIndex, startIndex);
michael@0 2836 }
michael@0 2837
michael@0 2838 int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
michael@0 2839 int startIndex = angle->start();
michael@0 2840 int endIndex = angle->end();
michael@0 2841 return updateOppWinding(startIndex, endIndex);
michael@0 2842 }
michael@0 2843
michael@0 2844 int SkOpSegment::updateWinding(int index, int endIndex) const {
michael@0 2845 int lesser = SkMin32(index, endIndex);
michael@0 2846 int winding = windSum(lesser);
michael@0 2847 int spanWinding = spanSign(index, endIndex);
michael@0 2848 if (winding && UseInnerWinding(winding - spanWinding, winding)
michael@0 2849 && winding != SK_MaxS32) {
michael@0 2850 winding -= spanWinding;
michael@0 2851 }
michael@0 2852 return winding;
michael@0 2853 }
michael@0 2854
michael@0 2855 int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
michael@0 2856 int startIndex = angle->start();
michael@0 2857 int endIndex = angle->end();
michael@0 2858 return updateWinding(endIndex, startIndex);
michael@0 2859 }
michael@0 2860
michael@0 2861 int SkOpSegment::updateWindingReverse(int index, int endIndex) const {
michael@0 2862 int lesser = SkMin32(index, endIndex);
michael@0 2863 int winding = windSum(lesser);
michael@0 2864 int spanWinding = spanSign(endIndex, index);
michael@0 2865 if (winding && UseInnerWindingReverse(winding - spanWinding, winding)
michael@0 2866 && winding != SK_MaxS32) {
michael@0 2867 winding -= spanWinding;
michael@0 2868 }
michael@0 2869 return winding;
michael@0 2870 }
michael@0 2871
michael@0 2872 int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
michael@0 2873 int startIndex = angle->start();
michael@0 2874 int endIndex = angle->end();
michael@0 2875 return updateWindingReverse(endIndex, startIndex);
michael@0 2876 }
michael@0 2877
michael@0 2878 // OPTIMIZATION: does the following also work, and is it any faster?
michael@0 2879 // return outerWinding * innerWinding > 0
michael@0 2880 // || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
michael@0 2881 bool SkOpSegment::UseInnerWinding(int outerWinding, int innerWinding) {
michael@0 2882 SkASSERT(outerWinding != SK_MaxS32);
michael@0 2883 SkASSERT(innerWinding != SK_MaxS32);
michael@0 2884 int absOut = abs(outerWinding);
michael@0 2885 int absIn = abs(innerWinding);
michael@0 2886 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
michael@0 2887 return result;
michael@0 2888 }
michael@0 2889
michael@0 2890 bool SkOpSegment::UseInnerWindingReverse(int outerWinding, int innerWinding) {
michael@0 2891 SkASSERT(outerWinding != SK_MaxS32);
michael@0 2892 SkASSERT(innerWinding != SK_MaxS32);
michael@0 2893 int absOut = abs(outerWinding);
michael@0 2894 int absIn = abs(innerWinding);
michael@0 2895 bool result = absOut == absIn ? true : absOut < absIn;
michael@0 2896 return result;
michael@0 2897 }
michael@0 2898
michael@0 2899 int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const {
michael@0 2900 if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
michael@0 2901 return SK_MinS32;
michael@0 2902 }
michael@0 2903 int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
michael@0 2904 SkASSERT(winding != SK_MinS32);
michael@0 2905 int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
michael@0 2906 #if DEBUG_WINDING_AT_T
michael@0 2907 SkDebugf("%s oldWinding=%d windValue=%d", __FUNCTION__, winding, windVal);
michael@0 2908 #endif
michael@0 2909 // see if a + change in T results in a +/- change in X (compute x'(T))
michael@0 2910 *dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
michael@0 2911 if (fVerb > SkPath::kLine_Verb && approximately_zero(*dx)) {
michael@0 2912 *dx = fPts[2].fX - fPts[1].fX - *dx;
michael@0 2913 }
michael@0 2914 if (*dx == 0) {
michael@0 2915 #if DEBUG_WINDING_AT_T
michael@0 2916 SkDebugf(" dx=0 winding=SK_MinS32\n");
michael@0 2917 #endif
michael@0 2918 return SK_MinS32;
michael@0 2919 }
michael@0 2920 if (windVal < 0) { // reverse sign if opp contour traveled in reverse
michael@0 2921 *dx = -*dx;
michael@0 2922 }
michael@0 2923 if (winding * *dx > 0) { // if same signs, result is negative
michael@0 2924 winding += *dx > 0 ? -windVal : windVal;
michael@0 2925 }
michael@0 2926 #if DEBUG_WINDING_AT_T
michael@0 2927 SkDebugf(" dx=%c winding=%d\n", *dx > 0 ? '+' : '-', winding);
michael@0 2928 #endif
michael@0 2929 return winding;
michael@0 2930 }
michael@0 2931
michael@0 2932 int SkOpSegment::windSum(const SkOpAngle* angle) const {
michael@0 2933 int start = angle->start();
michael@0 2934 int end = angle->end();
michael@0 2935 int index = SkMin32(start, end);
michael@0 2936 return windSum(index);
michael@0 2937 }
michael@0 2938
michael@0 2939 void SkOpSegment::zeroSpan(SkOpSpan* span) {
michael@0 2940 SkASSERT(span->fWindValue > 0 || span->fOppValue != 0);
michael@0 2941 span->fWindValue = 0;
michael@0 2942 span->fOppValue = 0;
michael@0 2943 if (span->fTiny || span->fSmall) {
michael@0 2944 return;
michael@0 2945 }
michael@0 2946 SkASSERT(!span->fDone);
michael@0 2947 span->fDone = true;
michael@0 2948 ++fDoneSpans;
michael@0 2949 }
michael@0 2950
michael@0 2951 #if DEBUG_SWAP_TOP
michael@0 2952 bool SkOpSegment::controlsContainedByEnds(int tStart, int tEnd) const {
michael@0 2953 if (fVerb != SkPath::kCubic_Verb) {
michael@0 2954 return false;
michael@0 2955 }
michael@0 2956 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
michael@0 2957 return dst.controlsContainedByEnds();
michael@0 2958 }
michael@0 2959 #endif
michael@0 2960
michael@0 2961 #if DEBUG_CONCIDENT
michael@0 2962 // SkASSERT if pair has not already been added
michael@0 2963 void SkOpSegment::debugAddTPair(double t, const SkOpSegment& other, double otherT) const {
michael@0 2964 for (int i = 0; i < fTs.count(); ++i) {
michael@0 2965 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
michael@0 2966 return;
michael@0 2967 }
michael@0 2968 }
michael@0 2969 SkASSERT(0);
michael@0 2970 }
michael@0 2971 #endif
michael@0 2972
michael@0 2973 #if DEBUG_CONCIDENT
michael@0 2974 void SkOpSegment::debugShowTs(const char* prefix) const {
michael@0 2975 SkDebugf("%s %s id=%d", __FUNCTION__, prefix, fID);
michael@0 2976 int lastWind = -1;
michael@0 2977 int lastOpp = -1;
michael@0 2978 double lastT = -1;
michael@0 2979 int i;
michael@0 2980 for (i = 0; i < fTs.count(); ++i) {
michael@0 2981 bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
michael@0 2982 || lastOpp != fTs[i].fOppValue;
michael@0 2983 if (change && lastWind >= 0) {
michael@0 2984 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
michael@0 2985 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
michael@0 2986 }
michael@0 2987 if (change) {
michael@0 2988 SkDebugf(" [o=%d", fTs[i].fOther->fID);
michael@0 2989 lastWind = fTs[i].fWindValue;
michael@0 2990 lastOpp = fTs[i].fOppValue;
michael@0 2991 lastT = fTs[i].fT;
michael@0 2992 } else {
michael@0 2993 SkDebugf(",%d", fTs[i].fOther->fID);
michael@0 2994 }
michael@0 2995 }
michael@0 2996 if (i <= 0) {
michael@0 2997 return;
michael@0 2998 }
michael@0 2999 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
michael@0 3000 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
michael@0 3001 if (fOperand) {
michael@0 3002 SkDebugf(" operand");
michael@0 3003 }
michael@0 3004 if (done()) {
michael@0 3005 SkDebugf(" done");
michael@0 3006 }
michael@0 3007 SkDebugf("\n");
michael@0 3008 }
michael@0 3009 #endif
michael@0 3010
michael@0 3011 #if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
michael@0 3012 void SkOpSegment::debugShowActiveSpans() const {
michael@0 3013 debugValidate();
michael@0 3014 if (done()) {
michael@0 3015 return;
michael@0 3016 }
michael@0 3017 #if DEBUG_ACTIVE_SPANS_SHORT_FORM
michael@0 3018 int lastId = -1;
michael@0 3019 double lastT = -1;
michael@0 3020 #endif
michael@0 3021 for (int i = 0; i < fTs.count(); ++i) {
michael@0 3022 if (fTs[i].fDone) {
michael@0 3023 continue;
michael@0 3024 }
michael@0 3025 SkASSERT(i < fTs.count() - 1);
michael@0 3026 #if DEBUG_ACTIVE_SPANS_SHORT_FORM
michael@0 3027 if (lastId == fID && lastT == fTs[i].fT) {
michael@0 3028 continue;
michael@0 3029 }
michael@0 3030 lastId = fID;
michael@0 3031 lastT = fTs[i].fT;
michael@0 3032 #endif
michael@0 3033 SkDebugf("%s id=%d", __FUNCTION__, fID);
michael@0 3034 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
michael@0 3035 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
michael@0 3036 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
michael@0 3037 }
michael@0 3038 const SkOpSpan* span = &fTs[i];
michael@0 3039 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", span->fT, xAtT(span), yAtT(span));
michael@0 3040 int iEnd = i + 1;
michael@0 3041 while (fTs[iEnd].fT < 1 && approximately_equal(fTs[i].fT, fTs[iEnd].fT)) {
michael@0 3042 ++iEnd;
michael@0 3043 }
michael@0 3044 SkDebugf(" tEnd=%1.9g", fTs[iEnd].fT);
michael@0 3045 const SkOpSegment* other = fTs[i].fOther;
michael@0 3046 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
michael@0 3047 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
michael@0 3048 if (fTs[i].fWindSum == SK_MinS32) {
michael@0 3049 SkDebugf("?");
michael@0 3050 } else {
michael@0 3051 SkDebugf("%d", fTs[i].fWindSum);
michael@0 3052 }
michael@0 3053 SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
michael@0 3054 }
michael@0 3055 }
michael@0 3056 #endif
michael@0 3057
michael@0 3058
michael@0 3059 #if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
michael@0 3060 void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding) {
michael@0 3061 const SkPoint& pt = xyAtT(&span);
michael@0 3062 SkDebugf("%s id=%d", fun, fID);
michael@0 3063 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
michael@0 3064 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
michael@0 3065 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
michael@0 3066 }
michael@0 3067 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
michael@0 3068 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
michael@0 3069 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d windSum=",
michael@0 3070 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
michael@0 3071 (&span)[1].fT, winding);
michael@0 3072 if (span.fWindSum == SK_MinS32) {
michael@0 3073 SkDebugf("?");
michael@0 3074 } else {
michael@0 3075 SkDebugf("%d", span.fWindSum);
michael@0 3076 }
michael@0 3077 SkDebugf(" windValue=%d\n", span.fWindValue);
michael@0 3078 }
michael@0 3079
michael@0 3080 void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding,
michael@0 3081 int oppWinding) {
michael@0 3082 const SkPoint& pt = xyAtT(&span);
michael@0 3083 SkDebugf("%s id=%d", fun, fID);
michael@0 3084 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
michael@0 3085 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
michael@0 3086 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
michael@0 3087 }
michael@0 3088 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
michael@0 3089 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
michael@0 3090 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d newOppSum=%d oppSum=",
michael@0 3091 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
michael@0 3092 (&span)[1].fT, winding, oppWinding);
michael@0 3093 if (span.fOppSum == SK_MinS32) {
michael@0 3094 SkDebugf("?");
michael@0 3095 } else {
michael@0 3096 SkDebugf("%d", span.fOppSum);
michael@0 3097 }
michael@0 3098 SkDebugf(" windSum=");
michael@0 3099 if (span.fWindSum == SK_MinS32) {
michael@0 3100 SkDebugf("?");
michael@0 3101 } else {
michael@0 3102 SkDebugf("%d", span.fWindSum);
michael@0 3103 }
michael@0 3104 SkDebugf(" windValue=%d\n", span.fWindValue);
michael@0 3105 }
michael@0 3106 #endif
michael@0 3107
michael@0 3108 #if DEBUG_SORT || DEBUG_SWAP_TOP
michael@0 3109 void SkOpSegment::debugShowSort(const char* fun, const SkTArray<SkOpAngle*, true>& angles,
michael@0 3110 int first, const int contourWinding,
michael@0 3111 const int oppContourWinding, bool sortable) const {
michael@0 3112 if (--SkPathOpsDebug::gSortCount < 0) {
michael@0 3113 return;
michael@0 3114 }
michael@0 3115 if (!sortable) {
michael@0 3116 if (angles.count() == 0) {
michael@0 3117 return;
michael@0 3118 }
michael@0 3119 if (first < 0) {
michael@0 3120 first = 0;
michael@0 3121 }
michael@0 3122 }
michael@0 3123 SkASSERT(angles[first]->segment() == this);
michael@0 3124 SkASSERT(!sortable || angles.count() > 1);
michael@0 3125 int lastSum = contourWinding;
michael@0 3126 int oppLastSum = oppContourWinding;
michael@0 3127 const SkOpAngle* firstAngle = angles[first];
michael@0 3128 int windSum = lastSum - spanSign(firstAngle);
michael@0 3129 int oppoSign = oppSign(firstAngle);
michael@0 3130 int oppWindSum = oppLastSum - oppoSign;
michael@0 3131 #define WIND_AS_STRING(x) char x##Str[12]; \
michael@0 3132 if (!SkPathOpsDebug::ValidWind(x)) strcpy(x##Str, "?"); \
michael@0 3133 else SK_SNPRINTF(x##Str, sizeof(x##Str), "%d", x)
michael@0 3134 WIND_AS_STRING(contourWinding);
michael@0 3135 WIND_AS_STRING(oppContourWinding);
michael@0 3136 SkDebugf("%s %s contourWinding=%s oppContourWinding=%s sign=%d\n", fun, __FUNCTION__,
michael@0 3137 contourWindingStr, oppContourWindingStr, spanSign(angles[first]));
michael@0 3138 int index = first;
michael@0 3139 bool firstTime = true;
michael@0 3140 do {
michael@0 3141 const SkOpAngle& angle = *angles[index];
michael@0 3142 const SkOpSegment& segment = *angle.segment();
michael@0 3143 int start = angle.start();
michael@0 3144 int end = angle.end();
michael@0 3145 const SkOpSpan& sSpan = segment.fTs[start];
michael@0 3146 const SkOpSpan& eSpan = segment.fTs[end];
michael@0 3147 const SkOpSpan& mSpan = segment.fTs[SkMin32(start, end)];
michael@0 3148 bool opp = segment.fOperand ^ fOperand;
michael@0 3149 if (!firstTime) {
michael@0 3150 oppoSign = segment.oppSign(&angle);
michael@0 3151 if (opp) {
michael@0 3152 oppLastSum = oppWindSum;
michael@0 3153 oppWindSum -= segment.spanSign(&angle);
michael@0 3154 if (oppoSign) {
michael@0 3155 lastSum = windSum;
michael@0 3156 windSum -= oppoSign;
michael@0 3157 }
michael@0 3158 } else {
michael@0 3159 lastSum = windSum;
michael@0 3160 windSum -= segment.spanSign(&angle);
michael@0 3161 if (oppoSign) {
michael@0 3162 oppLastSum = oppWindSum;
michael@0 3163 oppWindSum -= oppoSign;
michael@0 3164 }
michael@0 3165 }
michael@0 3166 }
michael@0 3167 SkDebugf("%s [%d] %s", __FUNCTION__, index,
michael@0 3168 angle.unsortable() ? "*** UNSORTABLE *** " : "");
michael@0 3169 #if DEBUG_SORT_COMPACT
michael@0 3170 SkDebugf("id=%d %s start=%d (%1.9g,%1.9g) end=%d (%1.9g,%1.9g)",
michael@0 3171 segment.fID, kLVerbStr[SkPathOpsVerbToPoints(segment.fVerb)],
michael@0 3172 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
michael@0 3173 segment.xAtT(&eSpan), segment.yAtT(&eSpan));
michael@0 3174 #else
michael@0 3175 switch (segment.fVerb) {
michael@0 3176 case SkPath::kLine_Verb:
michael@0 3177 SkDebugf(LINE_DEBUG_STR, LINE_DEBUG_DATA(segment.fPts));
michael@0 3178 break;
michael@0 3179 case SkPath::kQuad_Verb:
michael@0 3180 SkDebugf(QUAD_DEBUG_STR, QUAD_DEBUG_DATA(segment.fPts));
michael@0 3181 break;
michael@0 3182 case SkPath::kCubic_Verb:
michael@0 3183 SkDebugf(CUBIC_DEBUG_STR, CUBIC_DEBUG_DATA(segment.fPts));
michael@0 3184 break;
michael@0 3185 default:
michael@0 3186 SkASSERT(0);
michael@0 3187 }
michael@0 3188 SkDebugf(" tStart=%1.9g tEnd=%1.9g", sSpan.fT, eSpan.fT);
michael@0 3189 #endif
michael@0 3190 SkDebugf(" sign=%d windValue=%d windSum=", angle.sign(), mSpan.fWindValue);
michael@0 3191 SkPathOpsDebug::WindingPrintf(mSpan.fWindSum);
michael@0 3192 int last, wind;
michael@0 3193 if (opp) {
michael@0 3194 last = oppLastSum;
michael@0 3195 wind = oppWindSum;
michael@0 3196 } else {
michael@0 3197 last = lastSum;
michael@0 3198 wind = windSum;
michael@0 3199 }
michael@0 3200 bool useInner = SkPathOpsDebug::ValidWind(last) && SkPathOpsDebug::ValidWind(wind)
michael@0 3201 && UseInnerWinding(last, wind);
michael@0 3202 WIND_AS_STRING(last);
michael@0 3203 WIND_AS_STRING(wind);
michael@0 3204 WIND_AS_STRING(lastSum);
michael@0 3205 WIND_AS_STRING(oppLastSum);
michael@0 3206 WIND_AS_STRING(windSum);
michael@0 3207 WIND_AS_STRING(oppWindSum);
michael@0 3208 #undef WIND_AS_STRING
michael@0 3209 if (!oppoSign) {
michael@0 3210 SkDebugf(" %s->%s (max=%s)", lastStr, windStr, useInner ? windStr : lastStr);
michael@0 3211 } else {
michael@0 3212 SkDebugf(" %s->%s (%s->%s)", lastStr, windStr, opp ? lastSumStr : oppLastSumStr,
michael@0 3213 opp ? windSumStr : oppWindSumStr);
michael@0 3214 }
michael@0 3215 SkDebugf(" done=%d unord=%d small=%d tiny=%d opp=%d\n",
michael@0 3216 mSpan.fDone, angle.unorderable(), mSpan.fSmall, mSpan.fTiny, opp);
michael@0 3217 ++index;
michael@0 3218 if (index == angles.count()) {
michael@0 3219 index = 0;
michael@0 3220 }
michael@0 3221 if (firstTime) {
michael@0 3222 firstTime = false;
michael@0 3223 }
michael@0 3224 } while (index != first);
michael@0 3225 }
michael@0 3226
michael@0 3227 void SkOpSegment::debugShowSort(const char* fun, const SkTArray<SkOpAngle*, true>& angles,
michael@0 3228 int first, bool sortable) {
michael@0 3229 if (!sortable) {
michael@0 3230 if (angles.count() == 0) {
michael@0 3231 return;
michael@0 3232 }
michael@0 3233 if (first < 0) {
michael@0 3234 first = 0;
michael@0 3235 }
michael@0 3236 }
michael@0 3237 const SkOpAngle* firstAngle = angles[first];
michael@0 3238 const SkOpSegment* segment = firstAngle->segment();
michael@0 3239 int winding = segment->updateWinding(firstAngle);
michael@0 3240 int oppWinding = segment->updateOppWinding(firstAngle);
michael@0 3241 debugShowSort(fun, angles, first, winding, oppWinding, sortable);
michael@0 3242 }
michael@0 3243
michael@0 3244 #endif
michael@0 3245
michael@0 3246 #if DEBUG_SHOW_WINDING
michael@0 3247 int SkOpSegment::debugShowWindingValues(int slotCount, int ofInterest) const {
michael@0 3248 if (!(1 << fID & ofInterest)) {
michael@0 3249 return 0;
michael@0 3250 }
michael@0 3251 int sum = 0;
michael@0 3252 SkTArray<char, true> slots(slotCount * 2);
michael@0 3253 memset(slots.begin(), ' ', slotCount * 2);
michael@0 3254 for (int i = 0; i < fTs.count(); ++i) {
michael@0 3255 // if (!(1 << fTs[i].fOther->fID & ofInterest)) {
michael@0 3256 // continue;
michael@0 3257 // }
michael@0 3258 sum += fTs[i].fWindValue;
michael@0 3259 slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
michael@0 3260 sum += fTs[i].fOppValue;
michael@0 3261 slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
michael@0 3262 }
michael@0 3263 SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
michael@0 3264 slots.begin() + slotCount);
michael@0 3265 return sum;
michael@0 3266 }
michael@0 3267 #endif
michael@0 3268
michael@0 3269 void SkOpSegment::debugValidate() const {
michael@0 3270 #if DEBUG_VALIDATE
michael@0 3271 int count = fTs.count();
michael@0 3272 SkASSERT(count >= 2);
michael@0 3273 SkASSERT(fTs[0].fT == 0);
michael@0 3274 SkASSERT(fTs[count - 1].fT == 1);
michael@0 3275 int done = 0;
michael@0 3276 double t = -1;
michael@0 3277 for (int i = 0; i < count; ++i) {
michael@0 3278 const SkOpSpan& span = fTs[i];
michael@0 3279 SkASSERT(t <= span.fT);
michael@0 3280 t = span.fT;
michael@0 3281 int otherIndex = span.fOtherIndex;
michael@0 3282 const SkOpSegment* other = span.fOther;
michael@0 3283 const SkOpSpan& otherSpan = other->fTs[otherIndex];
michael@0 3284 SkASSERT(otherSpan.fPt == span.fPt);
michael@0 3285 SkASSERT(otherSpan.fOtherT == t);
michael@0 3286 SkASSERT(&fTs[i] == &otherSpan.fOther->fTs[otherSpan.fOtherIndex]);
michael@0 3287 done += span.fDone;
michael@0 3288 }
michael@0 3289 SkASSERT(done == fDoneSpans);
michael@0 3290 #endif
michael@0 3291 }
michael@0 3292
michael@0 3293 #ifdef SK_DEBUG
michael@0 3294 void SkOpSegment::dumpPts() const {
michael@0 3295 int last = SkPathOpsVerbToPoints(fVerb);
michael@0 3296 SkDebugf("{{");
michael@0 3297 int index = 0;
michael@0 3298 do {
michael@0 3299 SkDPoint::dump(fPts[index]);
michael@0 3300 SkDebugf(", ");
michael@0 3301 } while (++index < last);
michael@0 3302 SkDPoint::dump(fPts[index]);
michael@0 3303 SkDebugf("}}\n");
michael@0 3304 }
michael@0 3305
michael@0 3306 void SkOpSegment::dumpDPts() const {
michael@0 3307 int count = SkPathOpsVerbToPoints(fVerb);
michael@0 3308 SkDebugf("{{");
michael@0 3309 int index = 0;
michael@0 3310 do {
michael@0 3311 SkDPoint dPt = {fPts[index].fX, fPts[index].fY};
michael@0 3312 dPt.dump();
michael@0 3313 if (index != count) {
michael@0 3314 SkDebugf(", ");
michael@0 3315 }
michael@0 3316 } while (++index <= count);
michael@0 3317 SkDebugf("}}\n");
michael@0 3318 }
michael@0 3319
michael@0 3320 void SkOpSegment::dumpSpans() const {
michael@0 3321 int count = this->count();
michael@0 3322 for (int index = 0; index < count; ++index) {
michael@0 3323 const SkOpSpan& span = this->span(index);
michael@0 3324 SkDebugf("[%d] ", index);
michael@0 3325 span.dump();
michael@0 3326 }
michael@0 3327 }
michael@0 3328 #endif

mercurial