SkOpSpan.h revision 8016b264ceec2b11d2acbeb77a9fbe66e48368b9
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#ifndef SkOpSpan_DEFINED
8#define SkOpSpan_DEFINED
9
10#include "SkPathOpsDebug.h"
11#include "SkPathOpsTypes.h"
12#include "SkPoint.h"
13
14class SkChunkAlloc;
15class SkOpAngle;
16class SkOpContour;
17class SkOpGlobalState;
18class SkOpSegment;
19class SkOpSpanBase;
20class SkOpSpan;
21struct SkPathOpsBounds;
22
23// subset of op span used by terminal span (when t is equal to one)
24class SkOpPtT {
25public:
26    enum {
27        kIsAlias = 1,
28        kIsDuplicate = 1
29    };
30
31    const SkOpPtT* active() const;
32
33    // please keep in sync with debugAddOpp()
34    void addOpp(SkOpPtT* opp, SkOpPtT* oppPrev) {
35        SkOpPtT* oldNext = this->fNext;
36        SkASSERT(this != opp);
37        this->fNext = opp;
38        SkASSERT(oppPrev != oldNext);
39        oppPrev->fNext = oldNext;
40    }
41
42    bool alias() const;
43    bool coincident() const { return fCoincident; }
44    bool collapsed(const SkOpPtT* ) const;
45    bool contains(const SkOpPtT* ) const;
46    bool contains(const SkOpSegment*, const SkPoint& ) const;
47    bool contains(const SkOpSegment*, double t) const;
48    const SkOpPtT* contains(const SkOpSegment* ) const;
49    SkOpContour* contour() const;
50
51    int debugID() const {
52        return SkDEBUGRELEASE(fID, -1);
53    }
54
55    void debugAddOpp(const SkOpPtT* opp, const SkOpPtT* oppPrev) const;
56    const SkOpAngle* debugAngle(int id) const;
57    const SkOpCoincidence* debugCoincidence() const;
58    bool debugContains(const SkOpPtT* ) const;
59    const SkOpPtT* debugContains(const SkOpSegment* check) const;
60    SkOpContour* debugContour(int id) const;
61    const SkOpPtT* debugEnder(const SkOpPtT* end) const;
62    int debugLoopLimit(bool report) const;
63    bool debugMatchID(int id) const;
64    const SkOpPtT* debugOppPrev(const SkOpPtT* opp) const;
65    const SkOpPtT* debugPtT(int id) const;
66    void debugResetCoinT() const;
67    const SkOpSegment* debugSegment(int id) const;
68    void debugSetCoinT(int ) const;
69    const SkOpSpanBase* debugSpan(int id) const;
70    void debugValidate() const;
71
72    bool deleted() const {
73        return fDeleted;
74    }
75
76    bool duplicate() const {
77        return fDuplicatePt;
78    }
79
80    void dump() const;  // available to testing only
81    void dumpAll() const;
82    void dumpBase() const;
83
84    const SkOpPtT* find(const SkOpSegment* ) const;
85    SkOpGlobalState* globalState() const;
86    void init(SkOpSpanBase* , double t, const SkPoint& , bool dup);
87
88    void insert(SkOpPtT* span) {
89        SkASSERT(span != this);
90        span->fNext = fNext;
91        fNext = span;
92    }
93
94    const SkOpPtT* next() const {
95        return fNext;
96    }
97
98    SkOpPtT* next() {
99        return fNext;
100    }
101
102    bool onEnd() const;
103
104    // returns nullptr if this is already in the opp ptT loop
105    SkOpPtT* oppPrev(const SkOpPtT* opp) const {
106        // find the fOpp ptr to opp
107        SkOpPtT* oppPrev = opp->fNext;
108        if (oppPrev == this) {
109            return nullptr;
110        }
111        while (oppPrev->fNext != opp) {
112            oppPrev = oppPrev->fNext;
113            if (oppPrev == this) {
114                return nullptr;
115            }
116        }
117        return oppPrev;
118    }
119
120    static bool Overlaps(const SkOpPtT* s1, const SkOpPtT* e1, const SkOpPtT* s2,
121            const SkOpPtT* e2, const SkOpPtT** sOut, const SkOpPtT** eOut) {
122        const SkOpPtT* start1 = s1->fT < e1->fT ? s1 : e1;
123        const SkOpPtT* start2 = s2->fT < e2->fT ? s2 : e2;
124        *sOut = between(s1->fT, start2->fT, e1->fT) ? start2
125                : between(s2->fT, start1->fT, e2->fT) ? start1 : nullptr;
126        const SkOpPtT* end1 = s1->fT < e1->fT ? e1 : s1;
127        const SkOpPtT* end2 = s2->fT < e2->fT ? e2 : s2;
128        *eOut = between(s1->fT, end2->fT, e1->fT) ? end2
129                : between(s2->fT, end1->fT, e2->fT) ? end1 : nullptr;
130        if (*sOut == *eOut) {
131            SkASSERT(start1->fT >= end2->fT || start2->fT >= end1->fT);
132            return false;
133        }
134        SkASSERT(!*sOut || *sOut != *eOut);
135        return *sOut && *eOut;
136    }
137
138    bool ptAlreadySeen(const SkOpPtT* head) const;
139    SkOpPtT* prev();
140    SkOpPtT* remove(const SkOpPtT* kept);
141    void removeNext(const SkOpPtT* kept);
142
143    const SkOpSegment* segment() const;
144    SkOpSegment* segment();
145
146    void setCoincident() const {
147        SkOPASSERT(!fDeleted);
148        fCoincident = true;
149    }
150
151    void setDeleted();
152
153    void setSpan(const SkOpSpanBase* span) {
154        fSpan = const_cast<SkOpSpanBase*>(span);
155    }
156
157    const SkOpSpanBase* span() const {
158        return fSpan;
159    }
160
161    SkOpSpanBase* span() {
162        return fSpan;
163    }
164
165    const SkOpPtT* starter(const SkOpPtT* end) const {
166        return fT < end->fT ? this : end;
167    }
168
169    double fT;
170    SkPoint fPt;   // cache of point value at this t
171protected:
172    SkOpSpanBase* fSpan;  // contains winding data
173    SkOpPtT* fNext;  // intersection on opposite curve or alias on this curve
174    bool fDeleted;  // set if removed from span list
175    bool fDuplicatePt;  // set if identical pt is somewhere in the next loop
176    // below mutable since referrer is otherwise always const
177    mutable bool fCoincident;  // set if at some point a coincident span pointed here
178    SkDEBUGCODE(int fID);
179};
180
181class SkOpSpanBase {
182public:
183    SkOpSpanBase* active();
184    void addOpp(SkOpSpanBase* opp);
185
186    void bumpSpanAdds() {
187        ++fSpanAdds;
188    }
189
190    bool chased() const {
191        return fChased;
192    }
193
194    void checkForCollapsedCoincidence();
195
196    const SkOpSpanBase* coinEnd() const {
197        return fCoinEnd;
198    }
199
200    bool collapsed(double s, double e) const;
201    bool contains(const SkOpSpanBase* ) const;
202    const SkOpPtT* contains(const SkOpSegment* ) const;
203
204    bool containsCoinEnd(const SkOpSpanBase* coin) const {
205        SkASSERT(this != coin);
206        const SkOpSpanBase* next = this;
207        while ((next = next->fCoinEnd) != this) {
208            if (next == coin) {
209                return true;
210            }
211        }
212        return false;
213    }
214
215    bool containsCoinEnd(const SkOpSegment* ) const;
216    SkOpContour* contour() const;
217
218    int debugBumpCount() {
219        return SkDEBUGRELEASE(++fCount, -1);
220    }
221
222    int debugID() const {
223        return SkDEBUGRELEASE(fID, -1);
224    }
225
226#if DEBUG_COINCIDENCE_VERBOSE
227    void debugAddOpp(const char* id, SkPathOpsDebug::GlitchLog* , const SkOpSpanBase* opp) const;
228#endif
229    bool debugAlignedEnd(double t, const SkPoint& pt) const;
230    bool debugAlignedInner() const;
231    const SkOpAngle* debugAngle(int id) const;
232#if DEBUG_COINCIDENCE_VERBOSE
233    void debugCheckForCollapsedCoincidence(const char* id, SkPathOpsDebug::GlitchLog* ) const;
234#endif
235    const SkOpCoincidence* debugCoincidence() const;
236    bool debugCoinEndLoopCheck() const;
237    SkOpContour* debugContour(int id) const;
238#ifdef SK_DEBUG
239    bool debugDeleted() const { return fDebugDeleted; }
240#endif
241#if DEBUG_COINCIDENCE_VERBOSE
242    void debugInsertCoinEnd(const char* id, SkPathOpsDebug::GlitchLog* ,
243                            const SkOpSpanBase* ) const;
244    void debugMergeContained(const char* id, SkPathOpsDebug::GlitchLog* ,
245                             const SkPathOpsBounds& bounds, bool* deleted) const;
246    void debugMergeMatches(const char* id, SkPathOpsDebug::GlitchLog* log,
247                           const SkOpSpanBase* opp) const;
248#endif
249    const SkOpPtT* debugPtT(int id) const;
250    void debugResetCoinT() const;
251    const SkOpSegment* debugSegment(int id) const;
252    void debugSetCoinT(int ) const;
253#ifdef SK_DEBUG
254    void debugSetDeleted() { fDebugDeleted = true; }
255#endif
256    const SkOpSpanBase* debugSpan(int id) const;
257    const SkOpSpan* debugStarter(SkOpSpanBase const** endPtr) const;
258    SkOpGlobalState* globalState() const;
259    void debugValidate() const;
260
261    bool deleted() const {
262        return fPtT.deleted();
263    }
264
265    void dump() const;  // available to testing only
266    void dumpCoin() const;
267    void dumpAll() const;
268    void dumpBase() const;
269    void dumpHead() const;
270
271    bool final() const {
272        return fPtT.fT == 1;
273    }
274
275    SkOpAngle* fromAngle() const {
276        return fFromAngle;
277    }
278
279    void initBase(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
280
281    // Please keep this in sync with debugInsertCoinEnd()
282    void insertCoinEnd(SkOpSpanBase* coin) {
283        if (containsCoinEnd(coin)) {
284            SkASSERT(coin->containsCoinEnd(this));
285            return;
286        }
287        debugValidate();
288        SkASSERT(this != coin);
289        SkOpSpanBase* coinNext = coin->fCoinEnd;
290        coin->fCoinEnd = this->fCoinEnd;
291        this->fCoinEnd = coinNext;
292        debugValidate();
293    }
294
295    void merge(SkOpSpan* span);
296    void mergeContained(const SkPathOpsBounds& bounds);
297    void mergeMatches(SkOpSpanBase* opp);
298
299    const SkOpSpan* prev() const {
300        return fPrev;
301    }
302
303    SkOpSpan* prev() {
304        return fPrev;
305    }
306
307    const SkPoint& pt() const {
308        return fPtT.fPt;
309    }
310
311    const SkOpPtT* ptT() const {
312        return &fPtT;
313    }
314
315    SkOpPtT* ptT() {
316        return &fPtT;
317    }
318
319    SkOpSegment* segment() const {
320        return fSegment;
321    }
322
323    void setAligned() {
324        fAligned = true;
325    }
326
327    void setChased(bool chased) {
328        fChased = chased;
329    }
330
331    void setFromAngle(SkOpAngle* angle) {
332        fFromAngle = angle;
333    }
334
335    void setPrev(SkOpSpan* prev) {
336        fPrev = prev;
337    }
338
339    bool simple() const {
340        fPtT.debugValidate();
341        return fPtT.next()->next() == &fPtT;
342    }
343
344    int spanAddsCount() const {
345        return fSpanAdds;
346    }
347
348    const SkOpSpan* starter(const SkOpSpanBase* end) const {
349        const SkOpSpanBase* result = t() < end->t() ? this : end;
350        return result->upCast();
351    }
352
353    SkOpSpan* starter(SkOpSpanBase* end) {
354        SkASSERT(this->segment() == end->segment());
355        SkOpSpanBase* result = t() < end->t() ? this : end;
356        return result->upCast();
357    }
358
359    SkOpSpan* starter(SkOpSpanBase** endPtr) {
360        SkOpSpanBase* end = *endPtr;
361        SkASSERT(this->segment() == end->segment());
362        SkOpSpanBase* result;
363        if (t() < end->t()) {
364            result = this;
365        } else {
366            result = end;
367            *endPtr = this;
368        }
369        return result->upCast();
370    }
371
372    int step(const SkOpSpanBase* end) const {
373        return t() < end->t() ? 1 : -1;
374    }
375
376    double t() const {
377        return fPtT.fT;
378    }
379
380    void unaligned() {
381        fAligned = false;
382    }
383
384    SkOpSpan* upCast() {
385        SkASSERT(!final());
386        return (SkOpSpan*) this;
387    }
388
389    const SkOpSpan* upCast() const {
390        SkOPASSERT(!final());
391        return (const SkOpSpan*) this;
392    }
393
394    SkOpSpan* upCastable() {
395        return final() ? nullptr : upCast();
396    }
397
398    const SkOpSpan* upCastable() const {
399        return final() ? nullptr : upCast();
400    }
401
402private:
403    void alignInner();
404
405protected:  // no direct access to internals to avoid treating a span base as a span
406    SkOpPtT fPtT;  // list of points and t values associated with the start of this span
407    SkOpSegment* fSegment;  // segment that contains this span
408    SkOpSpanBase* fCoinEnd;  // linked list of coincident spans that end here (may point to itself)
409    SkOpAngle* fFromAngle;  // points to next angle from span start to end
410    SkOpSpan* fPrev;  // previous intersection point
411    int fSpanAdds;  // number of times intersections have been added to span
412    bool fAligned;
413    bool fChased;  // set after span has been added to chase array
414    SkDEBUGCODE(int fCount);  // number of pt/t pairs added
415    SkDEBUGCODE(int fID);
416    SkDEBUGCODE(bool fDebugDeleted);  // set when span was merged with another span
417};
418
419class SkOpSpan : public SkOpSpanBase {
420public:
421    bool alreadyAdded() const {
422        if (fAlreadyAdded) {
423            return true;
424        }
425        fAlreadyAdded = true;
426        return false;
427    }
428
429    bool clearCoincident() {
430        SkASSERT(!final());
431        if (fCoincident == this) {
432            return false;
433        }
434        fCoincident = this;
435        return true;
436    }
437
438    int computeWindSum();
439    bool containsCoincidence(const SkOpSegment* ) const;
440
441    bool containsCoincidence(const SkOpSpan* coin) const {
442        SkASSERT(this != coin);
443        const SkOpSpan* next = this;
444        while ((next = next->fCoincident) != this) {
445            if (next == coin) {
446                return true;
447            }
448        }
449        return false;
450    }
451
452    bool debugCoinLoopCheck() const;
453#if DEBUG_COINCIDENCE_VERBOSE
454    void debugInsertCoincidence(const char* , SkPathOpsDebug::GlitchLog* , const SkOpSpan* ) const;
455    void debugInsertCoincidence(const char* , SkPathOpsDebug::GlitchLog* ,
456                                const SkOpSegment* , bool flipped) const;
457#endif
458    void dumpCoin() const;
459    bool dumpSpan() const;
460
461    bool done() const {
462        SkASSERT(!final());
463        return fDone;
464    }
465
466    void init(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
467    bool insertCoincidence(const SkOpSegment* , bool flipped);
468
469    // Please keep this in sync with debugInsertCoincidence()
470    void insertCoincidence(SkOpSpan* coin) {
471        if (containsCoincidence(coin)) {
472            SkASSERT(coin->containsCoincidence(this));
473            return;
474        }
475        debugValidate();
476        SkASSERT(this != coin);
477        SkOpSpan* coinNext = coin->fCoincident;
478        coin->fCoincident = this->fCoincident;
479        this->fCoincident = coinNext;
480        debugValidate();
481    }
482
483    bool isCanceled() const {
484        SkASSERT(!final());
485        return fWindValue == 0 && fOppValue == 0;
486    }
487
488    bool isCoincident() const {
489        SkASSERT(!final());
490        return fCoincident != this;
491    }
492
493    SkOpSpanBase* next() const {
494        SkASSERT(!final());
495        return fNext;
496    }
497
498    int oppSum() const {
499        SkASSERT(!final());
500        return fOppSum;
501    }
502
503    int oppValue() const {
504        SkASSERT(!final());
505        return fOppValue;
506    }
507
508    void release(const SkOpPtT* );
509
510    SkOpPtT* setCoinStart(SkOpSpan* oldCoinStart, SkOpSegment* oppSegment);
511
512    void setDone(bool done) {
513        SkASSERT(!final());
514        fDone = done;
515    }
516
517    void setNext(SkOpSpanBase* nextT) {
518        SkASSERT(!final());
519        fNext = nextT;
520    }
521
522    void setOppSum(int oppSum);
523
524    void setOppValue(int oppValue) {
525        SkASSERT(!final());
526        SkASSERT(fOppSum == SK_MinS32);
527        SkASSERT(!oppValue || !fDone);
528        fOppValue = oppValue;
529    }
530
531    void setToAngle(SkOpAngle* angle) {
532        SkASSERT(!final());
533        fToAngle = angle;
534    }
535
536    void setWindSum(int windSum);
537
538    void setWindValue(int windValue) {
539        SkASSERT(!final());
540        SkASSERT(windValue >= 0);
541        SkASSERT(fWindSum == SK_MinS32);
542        SkOPASSERT(!windValue || !fDone);
543        fWindValue = windValue;
544    }
545
546    bool sortableTop(SkOpContour* );
547
548    SkOpAngle* toAngle() const {
549        SkASSERT(!final());
550        return fToAngle;
551    }
552
553    int windSum() const {
554        SkASSERT(!final());
555        return fWindSum;
556    }
557
558    int windValue() const {
559        SkOPASSERT(!final());
560        return fWindValue;
561    }
562
563private:  // no direct access to internals to avoid treating a span base as a span
564    SkOpSpan* fCoincident;  // linked list of spans coincident with this one (may point to itself)
565    SkOpAngle* fToAngle;  // points to next angle from span start to end
566    SkOpSpanBase* fNext;  // next intersection point
567    int fWindSum;  // accumulated from contours surrounding this one.
568    int fOppSum;  // for binary operators: the opposite winding sum
569    int fWindValue;  // 0 == canceled; 1 == normal; >1 == coincident
570    int fOppValue;  // normally 0 -- when binary coincident edges combine, opp value goes here
571    int fTopTTry; // specifies direction and t value to try next
572    bool fDone;  // if set, this span to next higher T has been processed
573    mutable bool fAlreadyAdded;
574};
575
576#endif
577