OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2015 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 "SkOpCoincidence.h" |
| 8 #include "SkOpSegment.h" |
| 9 #include "SkPathOpsTSect.h" |
| 10 |
| 11 void SkOpCoincidence::add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* o
ppPtTStart, |
| 12 SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator) { |
| 13 SkASSERT(coinPtTStart->fT < coinPtTEnd->fT); |
| 14 bool flipped = oppPtTStart->fT > oppPtTEnd->fT; |
| 15 SkCoincidentSpans* coinRec = SkOpTAllocator<SkCoincidentSpans>::Allocate(all
ocator); |
| 16 coinRec->fNext = this->fHead; |
| 17 coinRec->fCoinPtTStart = coinPtTStart; |
| 18 coinRec->fCoinPtTEnd = coinPtTEnd; |
| 19 coinRec->fOppPtTStart = oppPtTStart; |
| 20 coinRec->fOppPtTEnd = oppPtTEnd; |
| 21 coinRec->fFlipped = flipped; |
| 22 this->fHead = coinRec; |
| 23 } |
| 24 |
| 25 static void tRange(const SkOpPtT* overS, const SkOpPtT* overE, double tStart, do
uble tEnd, |
| 26 const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, double* coinTs,
double* coinTe) { |
| 27 double denom = overE->fT - overS->fT; |
| 28 double start = 0 < denom ? tStart : tEnd; |
| 29 double end = 0 < denom ? tEnd : tStart; |
| 30 double sRatio = (start - overS->fT) / denom; |
| 31 double eRatio = (end - overS->fT) / denom; |
| 32 *coinTs = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * sRatio; |
| 33 *coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio; |
| 34 } |
| 35 |
| 36 bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e, |
| 37 const SkOpPtT* over2s, const SkOpPtT* over2e, double tStar
t, double tEnd, |
| 38 SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, |
| 39 SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator)
{ |
| 40 double coinTs, coinTe, oppTs, oppTe; |
| 41 tRange(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coi
nTe); |
| 42 tRange(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe)
; |
| 43 SkOpSegment* coinSeg = coinPtTStart->segment(); |
| 44 SkOpSegment* oppSeg = oppPtTStart->segment(); |
| 45 SkASSERT(coinSeg != oppSeg); |
| 46 SkCoincidentSpans* check = this->fHead; |
| 47 do { |
| 48 const SkOpSegment* checkCoinSeg = check->fCoinPtTStart->segment(); |
| 49 if (checkCoinSeg != coinSeg && checkCoinSeg != oppSeg) { |
| 50 continue; |
| 51 } |
| 52 const SkOpSegment* checkOppSeg = check->fOppPtTStart->segment(); |
| 53 if (checkOppSeg != coinSeg && checkOppSeg != oppSeg) { |
| 54 continue; |
| 55 } |
| 56 int cTs = coinTs; |
| 57 int cTe = coinTe; |
| 58 int oTs = oppTs; |
| 59 int oTe = oppTe; |
| 60 if (checkCoinSeg != coinSeg) { |
| 61 SkASSERT(checkOppSeg != oppSeg); |
| 62 SkTSwap(cTs, oTs); |
| 63 SkTSwap(cTe, oTe); |
| 64 } |
| 65 int tweenCount = (int) between(check->fCoinPtTStart->fT, cTs, check->fCo
inPtTEnd->fT) |
| 66 + (int) between(check->fCoinPtTStart->fT, cTe, check->fCo
inPtTEnd->fT) |
| 67 + (int) between(check->fOppPtTStart->fT, oTs, check->fOpp
PtTEnd->fT) |
| 68 + (int) between(check->fOppPtTStart->fT, oTe, check->fOpp
PtTEnd->fT); |
| 69 // SkASSERT(tweenCount == 0 || tweenCount == 4); |
| 70 if (tweenCount) { |
| 71 return true; |
| 72 } |
| 73 } while ((check = check->fNext)); |
| 74 if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) { |
| 75 SkTSwap(oppTs, oppTe); |
| 76 } |
| 77 if (coinTs > coinTe) { |
| 78 SkTSwap(coinTs, coinTe); |
| 79 SkTSwap(oppTs, oppTe); |
| 80 } |
| 81 SkOpPtT* cs = coinSeg->addMissing(coinTs, oppSeg, allocator); |
| 82 SkOpPtT* ce = coinSeg->addMissing(coinTe, oppSeg, allocator); |
| 83 if (cs == ce) { |
| 84 return false; |
| 85 } |
| 86 SkOpPtT* os = oppSeg->addMissing(oppTs, coinSeg, allocator); |
| 87 SkOpPtT* oe = oppSeg->addMissing(oppTe, coinSeg, allocator); |
| 88 SkASSERT(os != oe); |
| 89 cs->addOpp(os); |
| 90 ce->addOpp(oe); |
| 91 this->add(cs, ce, os, oe, allocator); |
| 92 return true; |
| 93 } |
| 94 |
| 95 bool SkOpCoincidence::addMissing(SkChunkAlloc* allocator) { |
| 96 SkCoincidentSpans* outer = this->fHead; |
| 97 if (!outer) { |
| 98 return true; |
| 99 } |
| 100 do { |
| 101 SkCoincidentSpans* inner = outer; |
| 102 while ((inner = inner->fNext)) { |
| 103 double overS, overE; |
| 104 if (this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd, |
| 105 inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) { |
| 106 if (!addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd, |
| 107 inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE, |
| 108 outer->fOppPtTStart, outer->fOppPtTEnd, |
| 109 inner->fOppPtTStart, inner->fOppPtTEnd, allocator)) { |
| 110 return false; |
| 111 } |
| 112 } else if (this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd, |
| 113 inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) { |
| 114 if (!addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd, |
| 115 inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE, |
| 116 outer->fOppPtTStart, outer->fOppPtTEnd, |
| 117 inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator)) { |
| 118 return false; |
| 119 } |
| 120 } else if (this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd, |
| 121 inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) { |
| 122 if (!addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd, |
| 123 inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE, |
| 124 outer->fCoinPtTStart, outer->fCoinPtTEnd, |
| 125 inner->fOppPtTStart, inner->fOppPtTEnd, allocator)) { |
| 126 return false; |
| 127 } |
| 128 } else if (this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd, |
| 129 inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) { |
| 130 if (!addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd, |
| 131 inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE, |
| 132 outer->fCoinPtTStart, outer->fCoinPtTEnd, |
| 133 inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator)) { |
| 134 return false; |
| 135 } |
| 136 } |
| 137 } |
| 138 |
| 139 } while ((outer = outer->fNext)); |
| 140 return true; |
| 141 } |
| 142 |
| 143 |
| 144 bool SkOpCoincidence::contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpP
tT* oppPtTStart, |
| 145 SkOpPtT* oppPtTEnd, bool flipped) { |
| 146 SkCoincidentSpans* coin = fHead; |
| 147 if (!coin) { |
| 148 return false; |
| 149 } |
| 150 do { |
| 151 if (coin->fCoinPtTStart == coinPtTStart && coin->fCoinPtTEnd == coinPtT
End |
| 152 && coin->fOppPtTStart == oppPtTStart && coin->fOppPtTEnd == oppP
tTEnd |
| 153 && coin->fFlipped == flipped) { |
| 154 return true; |
| 155 } |
| 156 } while ((coin = coin->fNext)); |
| 157 return false; |
| 158 } |
| 159 |
| 160 // walk span sets in parallel, moving winding from one to the other |
| 161 bool SkOpCoincidence::apply() { |
| 162 SkCoincidentSpans* coin = fHead; |
| 163 if (!coin) { |
| 164 return true; |
| 165 } |
| 166 do { |
| 167 SkOpSpanBase* end = coin->fCoinPtTEnd->span(); |
| 168 SkOpSpan* start = coin->fCoinPtTStart->span()->upCast(); |
| 169 SkASSERT(start == start->starter(end)); |
| 170 bool flipped = coin->fFlipped; |
| 171 SkOpSpanBase* oEnd = (flipped ? coin->fOppPtTStart : coin->fOppPtTEnd)->
span(); |
| 172 SkOpSpan* oStart = (flipped ? coin->fOppPtTEnd : coin->fOppPtTStart)->sp
an()->upCast(); |
| 173 SkASSERT(oStart == oStart->starter(oEnd)); |
| 174 SkOpSegment* segment = start->segment(); |
| 175 SkOpSegment* oSegment = oStart->segment(); |
| 176 bool operandSwap = segment->operand() != oSegment->operand(); |
| 177 if (flipped) { |
| 178 do { |
| 179 SkOpSpanBase* oNext = oStart->next(); |
| 180 if (oNext == oEnd) { |
| 181 break; |
| 182 } |
| 183 oStart = oNext->upCast(); |
| 184 } while (true); |
| 185 } |
| 186 bool isXor = segment->isXor(); |
| 187 bool oppXor = oSegment->isXor(); |
| 188 do { |
| 189 int windValue = start->windValue(); |
| 190 int oWindValue = oStart->windValue(); |
| 191 int oppValue = start->oppValue(); |
| 192 int oOppValue = oStart->oppValue(); |
| 193 // winding values are added or subtracted depending on direction and
wind type |
| 194 // same or opposite values are summed depending on the operand value |
| 195 if (windValue >= oWindValue) { |
| 196 if (operandSwap) { |
| 197 SkTSwap(oWindValue, oOppValue); |
| 198 } |
| 199 if (flipped) { |
| 200 windValue -= oWindValue; |
| 201 oppValue -= oOppValue; |
| 202 } else { |
| 203 windValue += oWindValue; |
| 204 oppValue += oOppValue; |
| 205 } |
| 206 if (isXor) { |
| 207 windValue &= 1; |
| 208 } |
| 209 if (oppXor) { |
| 210 oppValue &= 1; |
| 211 } |
| 212 oWindValue = oOppValue = 0; |
| 213 } else { |
| 214 if (operandSwap) { |
| 215 SkTSwap(windValue, oppValue); |
| 216 } |
| 217 if (flipped) { |
| 218 oWindValue -= windValue; |
| 219 oOppValue -= oppValue; |
| 220 } else { |
| 221 oWindValue += windValue; |
| 222 oOppValue += oppValue; |
| 223 } |
| 224 if (isXor) { |
| 225 oOppValue &= 1; |
| 226 } |
| 227 if (oppXor) { |
| 228 oWindValue &= 1; |
| 229 } |
| 230 windValue = oppValue = 0; |
| 231 } |
| 232 start->setWindValue(windValue); |
| 233 start->setOppValue(oppValue); |
| 234 oStart->setWindValue(oWindValue); |
| 235 oStart->setOppValue(oOppValue); |
| 236 if (!windValue && !oppValue) { |
| 237 segment->markDone(start); |
| 238 } |
| 239 if (!oWindValue && !oOppValue) { |
| 240 oSegment->markDone(oStart); |
| 241 } |
| 242 SkOpSpanBase* next = start->next(); |
| 243 SkOpSpanBase* oNext = flipped ? oStart->prev() : oStart->next(); |
| 244 if (next == end) { |
| 245 break; |
| 246 } |
| 247 start = next->upCast(); |
| 248 if (!oNext) { |
| 249 return false; |
| 250 } |
| 251 if (!oNext->upCastable()) { |
| 252 return false; |
| 253 } |
| 254 oStart = oNext->upCast(); |
| 255 } while (true); |
| 256 } while ((coin = coin->fNext)); |
| 257 return true; |
| 258 } |
| 259 |
| 260 void SkOpCoincidence::detach(SkCoincidentSpans* remove) { |
| 261 SkCoincidentSpans* coin = fHead; |
| 262 SkCoincidentSpans* prev = NULL; |
| 263 SkCoincidentSpans* next; |
| 264 do { |
| 265 next = coin->fNext; |
| 266 if (coin == remove) { |
| 267 if (prev) { |
| 268 prev->fNext = next; |
| 269 } else { |
| 270 fHead = next; |
| 271 } |
| 272 break; |
| 273 } |
| 274 prev = coin; |
| 275 } while ((coin = next)); |
| 276 SkASSERT(coin); |
| 277 } |
| 278 |
| 279 void SkOpCoincidence::expand() { |
| 280 SkCoincidentSpans* coin = fHead; |
| 281 if (!coin) { |
| 282 return; |
| 283 } |
| 284 do { |
| 285 SkOpSpan* start = coin->fCoinPtTStart->span()->upCast(); |
| 286 SkOpSpanBase* end = coin->fCoinPtTEnd->span(); |
| 287 SkOpSegment* segment = coin->fCoinPtTStart->segment(); |
| 288 SkOpSegment* oppSegment = coin->fOppPtTStart->segment(); |
| 289 SkOpSpan* prev = start->prev(); |
| 290 SkOpPtT* oppPtT; |
| 291 if (prev && (oppPtT = prev->contains(oppSegment))) { |
| 292 double midT = (prev->t() + start->t()) / 2; |
| 293 if (segment->isClose(midT, oppSegment)) { |
| 294 coin->fCoinPtTStart = prev->ptT(); |
| 295 coin->fOppPtTStart = oppPtT; |
| 296 } |
| 297 } |
| 298 SkOpSpanBase* next = end->final() ? NULL : end->upCast()->next(); |
| 299 if (next && (oppPtT = next->contains(oppSegment))) { |
| 300 double midT = (end->t() + next->t()) / 2; |
| 301 if (segment->isClose(midT, oppSegment)) { |
| 302 coin->fCoinPtTEnd = next->ptT(); |
| 303 coin->fOppPtTEnd = oppPtT; |
| 304 } |
| 305 } |
| 306 } while ((coin = coin->fNext)); |
| 307 } |
| 308 |
| 309 void SkOpCoincidence::fixUp(SkOpPtT* deleted, SkOpPtT* kept) { |
| 310 SkCoincidentSpans* coin = fHead; |
| 311 if (!coin) { |
| 312 return; |
| 313 } |
| 314 do { |
| 315 if (coin->fCoinPtTStart == deleted) { |
| 316 if (coin->fCoinPtTEnd->span() == kept->span()) { |
| 317 return this->detach(coin); |
| 318 } |
| 319 coin->fCoinPtTStart = kept; |
| 320 } |
| 321 if (coin->fCoinPtTEnd == deleted) { |
| 322 if (coin->fCoinPtTStart->span() == kept->span()) { |
| 323 return this->detach(coin); |
| 324 } |
| 325 coin->fCoinPtTEnd = kept; |
| 326 } |
| 327 if (coin->fOppPtTStart == deleted) { |
| 328 if (coin->fOppPtTEnd->span() == kept->span()) { |
| 329 return this->detach(coin); |
| 330 } |
| 331 coin->fOppPtTStart = kept; |
| 332 } |
| 333 if (coin->fOppPtTEnd == deleted) { |
| 334 if (coin->fOppPtTStart->span() == kept->span()) { |
| 335 return this->detach(coin); |
| 336 } |
| 337 coin->fOppPtTEnd = kept; |
| 338 } |
| 339 } while ((coin = coin->fNext)); |
| 340 } |
| 341 |
| 342 void SkOpCoincidence::mark() { |
| 343 SkCoincidentSpans* coin = fHead; |
| 344 if (!coin) { |
| 345 return; |
| 346 } |
| 347 do { |
| 348 SkOpSpanBase* end = coin->fCoinPtTEnd->span(); |
| 349 SkOpSpanBase* oldEnd = end; |
| 350 SkOpSpan* start = coin->fCoinPtTStart->span()->starter(&end); |
| 351 SkOpSpanBase* oEnd = coin->fOppPtTEnd->span(); |
| 352 SkOpSpanBase* oOldEnd = oEnd; |
| 353 SkOpSpanBase* oStart = coin->fOppPtTStart->span()->starter(&oEnd); |
| 354 bool flipped = (end == oldEnd) != (oEnd == oOldEnd); |
| 355 if (flipped) { |
| 356 SkTSwap(oStart, oEnd); |
| 357 } |
| 358 SkOpSpanBase* next = start; |
| 359 SkOpSpanBase* oNext = oStart; |
| 360 // check to see if coincident span could be bigger |
| 361 |
| 362 do { |
| 363 next = next->upCast()->next(); |
| 364 oNext = flipped ? oNext->prev() : oNext->upCast()->next(); |
| 365 if (next == end || oNext == oEnd) { |
| 366 break; |
| 367 } |
| 368 if (!next->containsCoinEnd(oNext)) { |
| 369 next->insertCoinEnd(oNext); |
| 370 } |
| 371 SkOpSpan* nextSpan = next->upCast(); |
| 372 SkOpSpan* oNextSpan = oNext->upCast(); |
| 373 if (!nextSpan->containsCoincidence(oNextSpan)) { |
| 374 nextSpan->insertCoincidence(oNextSpan); |
| 375 } |
| 376 } while (true); |
| 377 } while ((coin = coin->fNext)); |
| 378 } |
| 379 |
| 380 bool SkOpCoincidence::overlap(const SkOpPtT* coin1s, const SkOpPtT* coin1e, |
| 381 const SkOpPtT* coin2s, const SkOpPtT* coin2e, double* overS, double* ove
rE) const { |
| 382 if (coin1s->segment() != coin2s->segment()) { |
| 383 return false; |
| 384 } |
| 385 *overS = SkTMax(SkTMin(coin1s->fT, coin1e->fT), SkTMin(coin2s->fT, coin2e->f
T)); |
| 386 *overE = SkTMin(SkTMax(coin1s->fT, coin1e->fT), SkTMax(coin2s->fT, coin2e->f
T)); |
| 387 return *overS < *overE; |
| 388 } |
OLD | NEW |