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