Home | History | Annotate | Download | only in pathops
      1 /*
      2  * Copyright 2014 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 "SkOpContour.h"
      9 #include "SkOpSegment.h"
     10 #include "SkPathWriter.h"
     11 
     12 bool SkOpPtT::alias() const {
     13     return this->span()->ptT() != this;
     14 }
     15 
     16 SkOpContour* SkOpPtT::contour() const {
     17     return segment()->contour();
     18 }
     19 
     20 SkOpGlobalState* SkOpPtT::globalState() const {
     21     return contour()->globalState();
     22 }
     23 
     24 void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
     25     fT = t;
     26     fPt = pt;
     27     fSpan = span;
     28     fNext = this;
     29     fDuplicatePt = duplicate;
     30     fDeleted = false;
     31     SkDEBUGCODE(fID = span->globalState()->nextPtTID());
     32 }
     33 
     34 bool SkOpPtT::onEnd() const {
     35     const SkOpSpanBase* span = this->span();
     36     if (span->ptT() != this) {
     37         return false;
     38     }
     39     const SkOpSegment* segment = this->segment();
     40     return span == segment->head() || span == segment->tail();
     41 }
     42 
     43 SkOpPtT* SkOpPtT::prev() {
     44     SkOpPtT* result = this;
     45     SkOpPtT* next = this;
     46     while ((next = next->fNext) != this) {
     47         result = next;
     48     }
     49     SkASSERT(result->fNext == this);
     50     return result;
     51 }
     52 
     53 SkOpPtT* SkOpPtT::remove() {
     54     SkOpPtT* prev = this;
     55     do {
     56         SkOpPtT* next = prev->fNext;
     57         if (next == this) {
     58             prev->removeNext(this);
     59             SkASSERT(prev->fNext != prev);
     60             fDeleted = true;
     61             return prev;
     62         }
     63         prev = next;
     64     } while (prev != this);
     65     SkASSERT(0);
     66     return NULL;
     67 }
     68 
     69 void SkOpPtT::removeNext(SkOpPtT* kept) {
     70     SkASSERT(this->fNext);
     71     SkOpPtT* next = this->fNext;
     72     SkASSERT(this != next->fNext);
     73     this->fNext = next->fNext;
     74     SkOpSpanBase* span = next->span();
     75     next->setDeleted();
     76     if (span->ptT() == next) {
     77         span->upCast()->detach(kept);
     78     }
     79 }
     80 
     81 const SkOpSegment* SkOpPtT::segment() const {
     82     return span()->segment();
     83 }
     84 
     85 SkOpSegment* SkOpPtT::segment() {
     86     return span()->segment();
     87 }
     88 
     89 void SkOpSpanBase::align() {
     90     if (this->fAligned) {
     91         return;
     92     }
     93     SkASSERT(!zero_or_one(this->fPtT.fT));
     94     SkASSERT(this->fPtT.next());
     95     // if a linked pt/t pair has a t of zero or one, use it as the base for alignment
     96     SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
     97     while ((ptT = ptT->next()) != stopPtT) {
     98         if (zero_or_one(ptT->fT)) {
     99             SkOpSegment* segment = ptT->segment();
    100             SkASSERT(this->segment() != segment);
    101             SkASSERT(segment->head()->ptT() == ptT || segment->tail()->ptT() == ptT);
    102             if (ptT->fT) {
    103                 segment->tail()->alignEnd(1, segment->lastPt());
    104             } else {
    105                 segment->head()->alignEnd(0, segment->pts()[0]);
    106             }
    107             return;
    108         }
    109     }
    110     alignInner();
    111     this->fAligned = true;
    112 }
    113 
    114 
    115 // FIXME: delete spans that collapse
    116 // delete segments that collapse
    117 // delete contours that collapse
    118 void SkOpSpanBase::alignEnd(double t, const SkPoint& pt) {
    119     SkASSERT(zero_or_one(t));
    120     SkOpSegment* segment = this->segment();
    121     SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt);
    122     alignInner();
    123     *segment->writablePt(!!t) = pt;
    124     SkOpPtT* ptT = &this->fPtT;
    125     SkASSERT(t == ptT->fT);
    126     SkASSERT(pt == ptT->fPt);
    127     SkOpPtT* test = ptT, * stopPtT = ptT;
    128     while ((test = test->next()) != stopPtT) {
    129         SkOpSegment* other = test->segment();
    130         if (other == this->segment()) {
    131             continue;
    132         }
    133         if (!zero_or_one(test->fT)) {
    134             continue;
    135         }
    136         *other->writablePt(!!test->fT) = pt;
    137     }
    138     this->fAligned = true;
    139 }
    140 
    141 void SkOpSpanBase::alignInner() {
    142     // force the spans to share points and t values
    143     SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
    144     const SkPoint& pt = ptT->fPt;
    145     do {
    146         ptT->fPt = pt;
    147         const SkOpSpanBase* span = ptT->span();
    148         SkOpPtT* test = ptT;
    149         do {
    150             SkOpPtT* prev = test;
    151             if ((test = test->next()) == stopPtT) {
    152                 break;
    153             }
    154             if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *test)) {
    155                 // omit aliases that alignment makes redundant
    156                 if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) {
    157                     SkASSERT(test->alias());
    158                     prev->removeNext(ptT);
    159                     test = prev;
    160                 } else {
    161                     SkASSERT(ptT->alias());
    162                     stopPtT = ptT = ptT->remove();
    163                     break;
    164                 }
    165             }
    166         } while (true);
    167     } while ((ptT = ptT->next()) != stopPtT);
    168 }
    169 
    170 bool SkOpSpanBase::contains(const SkOpSpanBase* span) const {
    171     const SkOpPtT* start = &fPtT;
    172     const SkOpPtT* check = &span->fPtT;
    173     SkASSERT(start != check);
    174     const SkOpPtT* walk = start;
    175     while ((walk = walk->next()) != start) {
    176         if (walk == check) {
    177             return true;
    178         }
    179     }
    180     return false;
    181 }
    182 
    183 SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) {
    184     SkOpPtT* start = &fPtT;
    185     SkOpPtT* walk = start;
    186     while ((walk = walk->next()) != start) {
    187         if (walk->segment() == segment) {
    188             return walk;
    189         }
    190     }
    191     return NULL;
    192 }
    193 
    194 bool SkOpSpanBase::containsCoinEnd(const SkOpSegment* segment) const {
    195     SkASSERT(this->segment() != segment);
    196     const SkOpSpanBase* next = this;
    197     while ((next = next->fCoinEnd) != this) {
    198         if (next->segment() == segment) {
    199             return true;
    200         }
    201     }
    202     return false;
    203 }
    204 
    205 SkOpContour* SkOpSpanBase::contour() const {
    206     return segment()->contour();
    207 }
    208 
    209 SkOpGlobalState* SkOpSpanBase::globalState() const {
    210     return contour()->globalState();
    211 }
    212 
    213 void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
    214     fSegment = segment;
    215     fPtT.init(this, t, pt, false);
    216     fCoinEnd = this;
    217     fFromAngle = NULL;
    218     fPrev = prev;
    219     fSpanAdds = 0;
    220     fAligned = true;
    221     fChased = false;
    222     SkDEBUGCODE(fCount = 1);
    223     SkDEBUGCODE(fID = globalState()->nextSpanID());
    224 }
    225 
    226 // this pair of spans share a common t value or point; merge them and eliminate duplicates
    227 // this does not compute the best t or pt value; this merely moves all data into a single list
    228 void SkOpSpanBase::merge(SkOpSpan* span) {
    229     SkOpPtT* spanPtT = span->ptT();
    230     SkASSERT(this->t() != spanPtT->fT);
    231     SkASSERT(!zero_or_one(spanPtT->fT));
    232     span->detach(this->ptT());
    233     SkOpPtT* remainder = spanPtT->next();
    234     ptT()->insert(spanPtT);
    235     while (remainder != spanPtT) {
    236         SkOpPtT* next = remainder->next();
    237         SkOpPtT* compare = spanPtT->next();
    238         while (compare != spanPtT) {
    239             SkOpPtT* nextC = compare->next();
    240             if (nextC->span() == remainder->span() && nextC->fT == remainder->fT) {
    241                 goto tryNextRemainder;
    242             }
    243             compare = nextC;
    244         }
    245         spanPtT->insert(remainder);
    246 tryNextRemainder:
    247         remainder = next;
    248     }
    249     fSpanAdds += span->fSpanAdds;
    250 }
    251 
    252 int SkOpSpan::computeWindSum() {
    253     SkOpGlobalState* globals = this->globalState();
    254     SkOpContour* contourHead = globals->contourHead();
    255     int windTry = 0;
    256     while (!this->sortableTop(contourHead) && ++windTry < SkOpGlobalState::kMaxWindingTries) {
    257         ;
    258     }
    259     return this->windSum();
    260 }
    261 
    262 bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const {
    263     SkASSERT(this->segment() != segment);
    264     const SkOpSpan* next = fCoincident;
    265     do {
    266         if (next->segment() == segment) {
    267             return true;
    268         }
    269     } while ((next = next->fCoincident) != this);
    270     return false;
    271 }
    272 
    273 void SkOpSpan::detach(SkOpPtT* kept) {
    274     SkASSERT(!final());
    275     SkOpSpan* prev = this->prev();
    276     SkASSERT(prev);
    277     SkOpSpanBase* next = this->next();
    278     SkASSERT(next);
    279     prev->setNext(next);
    280     next->setPrev(prev);
    281     this->segment()->detach(this);
    282     this->globalState()->coincidence()->fixUp(this->ptT(), kept);
    283     this->ptT()->setDeleted();
    284 }
    285 
    286 void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
    287     SkASSERT(t != 1);
    288     initBase(segment, prev, t, pt);
    289     fCoincident = this;
    290     fToAngle = NULL;
    291     fWindSum = fOppSum = SK_MinS32;
    292     fWindValue = 1;
    293     fOppValue = 0;
    294     fTopTTry = 0;
    295     fChased = fDone = false;
    296     segment->bumpCount();
    297 }
    298 
    299 void SkOpSpan::setOppSum(int oppSum) {
    300     SkASSERT(!final());
    301     if (fOppSum != SK_MinS32 && fOppSum != oppSum) {
    302         this->globalState()->setWindingFailed();
    303         return;
    304     }
    305     SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(oppSum) <= DEBUG_LIMIT_WIND_SUM);
    306     fOppSum = oppSum;
    307 }
    308 
    309 void SkOpSpan::setWindSum(int windSum) {
    310     SkASSERT(!final());
    311     if (fWindSum != SK_MinS32 && fWindSum != windSum) {
    312         this->globalState()->setWindingFailed();
    313         return;
    314     }
    315     SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(windSum) <= DEBUG_LIMIT_WIND_SUM);
    316     fWindSum = windSum;
    317 }
    318