SkOpContour.h revision 0bcb8ca8a8fc04a4a9c69013ccc46645d2f2ae77
1/*
2 * Copyright 2013 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 SkOpContour_DEFINED
8#define SkOpContour_DEFINED
9
10#include "SkOpSegment.h"
11#include "SkTArray.h"
12
13#if defined(SK_DEBUG) || !FORCE_RELEASE
14#include "SkThread.h"
15#endif
16
17class SkIntersections;
18class SkOpContour;
19class SkPathWriter;
20
21struct SkCoincidence {
22    SkOpContour* fOther;
23    int fSegments[2];
24    double fTs[2][2];
25    SkPoint fPts[2];
26};
27
28class SkOpContour {
29public:
30    SkOpContour() {
31        reset();
32#if defined(SK_DEBUG) || !FORCE_RELEASE
33        fID = sk_atomic_inc(&SkPathOpsDebug::gContourID);
34#endif
35    }
36
37    bool operator<(const SkOpContour& rh) const {
38        return fBounds.fTop == rh.fBounds.fTop
39                ? fBounds.fLeft < rh.fBounds.fLeft
40                : fBounds.fTop < rh.fBounds.fTop;
41    }
42
43    bool addCoincident(int index, SkOpContour* other, int otherIndex,
44                       const SkIntersections& ts, bool swap);
45    void addCoincidentPoints();
46
47    void addCross(const SkOpContour* crosser) {
48#ifdef DEBUG_CROSS
49        for (int index = 0; index < fCrosses.count(); ++index) {
50            SkASSERT(fCrosses[index] != crosser);
51        }
52#endif
53        fCrosses.push_back(crosser);
54    }
55
56    void addCubic(const SkPoint pts[4]) {
57        fSegments.push_back().addCubic(pts, fOperand, fXor);
58        fContainsCurves = fContainsCubics = true;
59    }
60
61    int addLine(const SkPoint pts[2]) {
62        fSegments.push_back().addLine(pts, fOperand, fXor);
63        return fSegments.count();
64    }
65
66    void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
67        fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
68    }
69
70    bool addPartialCoincident(int index, SkOpContour* other, int otherIndex,
71                       const SkIntersections& ts, int ptIndex, bool swap);
72
73    int addQuad(const SkPoint pts[3]) {
74        fSegments.push_back().addQuad(pts, fOperand, fXor);
75        fContainsCurves = true;
76        return fSegments.count();
77    }
78
79    int addT(int segIndex, SkOpContour* other, int otherIndex, const SkPoint& pt, double newT) {
80        setContainsIntercepts();
81        return fSegments[segIndex].addT(&other->fSegments[otherIndex], pt, newT);
82    }
83
84    int addSelfT(int segIndex, const SkPoint& pt, double newT) {
85        setContainsIntercepts();
86        return fSegments[segIndex].addSelfT(pt, newT);
87    }
88
89    const SkPathOpsBounds& bounds() const {
90        return fBounds;
91    }
92
93    bool calcAngles();
94    void calcCoincidentWinding();
95    void calcPartialCoincidentWinding();
96
97    void checkDuplicates() {
98        int segmentCount = fSegments.count();
99        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
100            SkOpSegment& segment = fSegments[sIndex];
101            if (segment.count() > 2) {
102                segment.checkDuplicates();
103            }
104        }
105    }
106
107    void checkEnds() {
108        if (!fContainsCurves) {
109            return;
110        }
111        int segmentCount = fSegments.count();
112        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
113            SkOpSegment* segment = &fSegments[sIndex];
114            if (segment->verb() == SkPath::kLine_Verb) {
115                continue;
116            }
117            if (segment->done()) {
118                continue;   // likely coincident, nothing to do
119            }
120            segment->checkEnds();
121        }
122    }
123
124    void checkMultiples() {
125        int segmentCount = fSegments.count();
126        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
127            SkOpSegment& segment = fSegments[sIndex];
128            if (segment.count() > 2) {
129                segment.checkMultiples();
130            }
131        }
132    }
133
134    void checkSmall() {
135        int segmentCount = fSegments.count();
136        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
137            SkOpSegment& segment = fSegments[sIndex];
138            if (segment.hasSmall()) {
139                segment.checkSmall();
140            }
141        }
142    }
143
144    // if same point has different T values, choose a common T
145    void checkTiny() {
146        int segmentCount = fSegments.count();
147        if (segmentCount <= 2) {
148            return;
149        }
150        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
151            SkOpSegment& segment = fSegments[sIndex];
152            if (segment.hasTiny()) {
153                segment.checkTiny();
154            }
155        }
156    }
157
158    void complete() {
159        setBounds();
160        fContainsIntercepts = false;
161    }
162
163    bool containsCubics() const {
164        return fContainsCubics;
165    }
166
167    bool crosses(const SkOpContour* crosser) const {
168        for (int index = 0; index < fCrosses.count(); ++index) {
169            if (fCrosses[index] == crosser) {
170                return true;
171            }
172        }
173        return false;
174    }
175
176    bool done() const {
177        return fDone;
178    }
179
180    const SkPoint& end() const {
181        const SkOpSegment& segment = fSegments.back();
182        return segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
183    }
184
185    void fixOtherTIndex() {
186        int segmentCount = fSegments.count();
187        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
188            fSegments[sIndex].fixOtherTIndex();
189        }
190    }
191
192    void joinCoincidence() {
193        joinCoincidence(fCoincidences, false);
194        joinCoincidence(fPartialCoincidences, true);
195    }
196
197    SkOpSegment* nonVerticalSegment(int* start, int* end);
198
199    bool operand() const {
200        return fOperand;
201    }
202
203    void reset() {
204        fSegments.reset();
205        fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
206        fContainsCurves = fContainsCubics = fContainsIntercepts = fDone = false;
207    }
208
209    SkTArray<SkOpSegment>& segments() {
210        return fSegments;
211    }
212
213    void setContainsIntercepts() {
214        fContainsIntercepts = true;
215    }
216
217    void setOperand(bool isOp) {
218        fOperand = isOp;
219    }
220
221    void setOppXor(bool isOppXor) {
222        fOppXor = isOppXor;
223        int segmentCount = fSegments.count();
224        for (int test = 0; test < segmentCount; ++test) {
225            fSegments[test].setOppXor(isOppXor);
226        }
227    }
228
229    void setXor(bool isXor) {
230        fXor = isXor;
231    }
232
233    void sortAngles();
234    void sortSegments();
235
236    const SkPoint& start() const {
237        return fSegments.front().pts()[0];
238    }
239
240    void toPath(SkPathWriter* path) const;
241
242    void toPartialBackward(SkPathWriter* path) const {
243        int segmentCount = fSegments.count();
244        for (int test = segmentCount - 1; test >= 0; --test) {
245            fSegments[test].addCurveTo(1, 0, path, true);
246        }
247    }
248
249    void toPartialForward(SkPathWriter* path) const {
250        int segmentCount = fSegments.count();
251        for (int test = 0; test < segmentCount; ++test) {
252            fSegments[test].addCurveTo(0, 1, path, true);
253        }
254    }
255
256    void topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY, SkOpSegment** topStart);
257    SkOpSegment* undoneSegment(int* start, int* end);
258
259    int updateSegment(int index, const SkPoint* pts) {
260        SkOpSegment& segment = fSegments[index];
261        segment.updatePts(pts);
262        return SkPathOpsVerbToPoints(segment.verb()) + 1;
263    }
264
265#if DEBUG_TEST
266    SkTArray<SkOpSegment>& debugSegments() {
267        return fSegments;
268    }
269#endif
270
271#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
272    void debugShowActiveSpans() {
273        for (int index = 0; index < fSegments.count(); ++index) {
274            fSegments[index].debugShowActiveSpans();
275        }
276    }
277#endif
278
279#if DEBUG_SHOW_WINDING
280    int debugShowWindingValues(int totalSegments, int ofInterest);
281    static void debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList);
282#endif
283
284    // available to test routines only
285    void dump() const;
286    void dumpAngles() const;
287    void dumpPts() const;
288    void dumpSpans() const;
289
290private:
291    void calcCommonCoincidentWinding(const SkCoincidence& );
292    void joinCoincidence(const SkTArray<SkCoincidence, true>& , bool partial);
293    void setBounds();
294
295    SkTArray<SkOpSegment> fSegments;
296    SkTArray<SkOpSegment*, true> fSortedSegments;
297    int fFirstSorted;
298    SkTArray<SkCoincidence, true> fCoincidences;
299    SkTArray<SkCoincidence, true> fPartialCoincidences;
300    SkTArray<const SkOpContour*, true> fCrosses;
301    SkPathOpsBounds fBounds;
302    bool fContainsIntercepts;  // FIXME: is this used by anybody?
303    bool fContainsCubics;
304    bool fContainsCurves;
305    bool fDone;
306    bool fOperand;  // true for the second argument to a binary operator
307    bool fXor;
308    bool fOppXor;
309#if defined(SK_DEBUG) || !FORCE_RELEASE
310    int debugID() const { return fID; }
311    int fID;
312#else
313    int debugID() const { return -1; }
314#endif
315};
316
317#endif
318