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