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