SkOpContour.cpp revision 07393cab57ce74a4aae89a31fae9aaa9780fc19d
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#include "SkIntersections.h"
8#include "SkOpContour.h"
9#include "SkPathWriter.h"
10#include "TSearch.h"
11
12void SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
13        const SkIntersections& ts, bool swap) {
14    SkCoincidence& coincidence = *fCoincidences.append();
15    coincidence.fContours[0] = this;  // FIXME: no need to store
16    coincidence.fContours[1] = other;
17    coincidence.fSegments[0] = index;
18    coincidence.fSegments[1] = otherIndex;
19    coincidence.fTs[swap][0] = ts[0][0];
20    coincidence.fTs[swap][1] = ts[0][1];
21    coincidence.fTs[!swap][0] = ts[1][0];
22    coincidence.fTs[!swap][1] = ts[1][1];
23    coincidence.fPts[0] = ts.pt(0).asSkPoint();
24    coincidence.fPts[1] = ts.pt(1).asSkPoint();
25}
26
27SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
28    int segmentCount = fSortedSegments.count();
29    SkASSERT(segmentCount > 0);
30    for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
31        SkOpSegment* testSegment = fSortedSegments[sortedIndex];
32        if (testSegment->done()) {
33            continue;
34        }
35        *start = *end = 0;
36        while (testSegment->nextCandidate(start, end)) {
37            if (!testSegment->isVertical(*start, *end)) {
38                return testSegment;
39            }
40        }
41    }
42    return NULL;
43}
44
45// first pass, add missing T values
46// second pass, determine winding values of overlaps
47void SkOpContour::addCoincidentPoints() {
48    int count = fCoincidences.count();
49    for (int index = 0; index < count; ++index) {
50        SkCoincidence& coincidence = fCoincidences[index];
51        SkASSERT(coincidence.fContours[0] == this);
52        int thisIndex = coincidence.fSegments[0];
53        SkOpSegment& thisOne = fSegments[thisIndex];
54        SkOpContour* otherContour = coincidence.fContours[1];
55        int otherIndex = coincidence.fSegments[1];
56        SkOpSegment& other = otherContour->fSegments[otherIndex];
57        if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
58            // OPTIMIZATION: remove from array
59            continue;
60        }
61    #if DEBUG_CONCIDENT
62        thisOne.debugShowTs();
63        other.debugShowTs();
64    #endif
65        double startT = coincidence.fTs[0][0];
66        double endT = coincidence.fTs[0][1];
67        bool cancelers;
68        if ((cancelers = startT > endT)) {
69            SkTSwap(startT, endT);
70            SkTSwap(coincidence.fPts[0], coincidence.fPts[1]);
71        }
72        SkASSERT(!approximately_negative(endT - startT));
73        double oStartT = coincidence.fTs[1][0];
74        double oEndT = coincidence.fTs[1][1];
75        if (oStartT > oEndT) {
76            SkTSwap<double>(oStartT, oEndT);
77            cancelers ^= true;
78        }
79        SkASSERT(!approximately_negative(oEndT - oStartT));
80        bool opp = fOperand ^ otherContour->fOperand;
81        if (cancelers && !opp) {
82            // make sure startT and endT have t entries
83            if (startT > 0 || oEndT < 1
84                    || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
85                thisOne.addTPair(startT, &other, oEndT, true, coincidence.fPts[0]);
86            }
87            if (oStartT > 0 || endT < 1
88                    || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
89                other.addTPair(oStartT, &thisOne, endT, true, coincidence.fPts[1]);
90            }
91        } else {
92            if (startT > 0 || oStartT > 0
93                    || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
94                thisOne.addTPair(startT, &other, oStartT, true, coincidence.fPts[0]);
95            }
96            if (endT < 1 || oEndT < 1
97                    || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
98                other.addTPair(oEndT, &thisOne, endT, true, coincidence.fPts[1]);
99            }
100        }
101    #if DEBUG_CONCIDENT
102        thisOne.debugShowTs();
103        other.debugShowTs();
104    #endif
105    }
106}
107
108void SkOpContour::calcCoincidentWinding() {
109    int count = fCoincidences.count();
110    for (int index = 0; index < count; ++index) {
111        SkCoincidence& coincidence = fCoincidences[index];
112        SkASSERT(coincidence.fContours[0] == this);
113        int thisIndex = coincidence.fSegments[0];
114        SkOpSegment& thisOne = fSegments[thisIndex];
115        if (thisOne.done()) {
116            continue;
117        }
118        SkOpContour* otherContour = coincidence.fContours[1];
119        int otherIndex = coincidence.fSegments[1];
120        SkOpSegment& other = otherContour->fSegments[otherIndex];
121        if (other.done()) {
122            continue;
123        }
124        double startT = coincidence.fTs[0][0];
125        double endT = coincidence.fTs[0][1];
126        bool cancelers;
127        if ((cancelers = startT > endT)) {
128            SkTSwap<double>(startT, endT);
129        }
130        SkASSERT(!approximately_negative(endT - startT));
131        double oStartT = coincidence.fTs[1][0];
132        double oEndT = coincidence.fTs[1][1];
133        if (oStartT > oEndT) {
134            SkTSwap<double>(oStartT, oEndT);
135            cancelers ^= true;
136        }
137        SkASSERT(!approximately_negative(oEndT - oStartT));
138        bool opp = fOperand ^ otherContour->fOperand;
139        if (cancelers && !opp) {
140            // make sure startT and endT have t entries
141            if (!thisOne.done() && !other.done()) {
142                thisOne.addTCancel(startT, endT, &other, oStartT, oEndT);
143            }
144        } else {
145            if (!thisOne.done() && !other.done()) {
146                thisOne.addTCoincident(startT, endT, &other, oStartT, oEndT);
147            }
148        }
149    #if DEBUG_CONCIDENT
150        thisOne.debugShowTs();
151        other.debugShowTs();
152    #endif
153    }
154}
155
156void SkOpContour::sortSegments() {
157    int segmentCount = fSegments.count();
158    fSortedSegments.setReserve(segmentCount);
159    for (int test = 0; test < segmentCount; ++test) {
160        *fSortedSegments.append() = &fSegments[test];
161    }
162    QSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
163    fFirstSorted = 0;
164}
165
166void SkOpContour::toPath(SkPathWriter* path) const {
167    int segmentCount = fSegments.count();
168    const SkPoint& pt = fSegments.front().pts()[0];
169    path->deferredMove(pt);
170    for (int test = 0; test < segmentCount; ++test) {
171        fSegments[test].addCurveTo(0, 1, path, true);
172    }
173    path->close();
174}
175
176void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
177        SkOpSegment** topStart) {
178    int segmentCount = fSortedSegments.count();
179    SkASSERT(segmentCount > 0);
180    int sortedIndex = fFirstSorted;
181    fDone = true;  // may be cleared below
182    for ( ; sortedIndex < segmentCount; ++sortedIndex) {
183        SkOpSegment* testSegment = fSortedSegments[sortedIndex];
184        if (testSegment->done()) {
185            if (sortedIndex == fFirstSorted) {
186                ++fFirstSorted;
187            }
188            continue;
189        }
190        fDone = false;
191        SkPoint testXY = testSegment->activeLeftTop(true, NULL);
192        if (*topStart) {
193            if (testXY.fY < topLeft.fY) {
194                continue;
195            }
196            if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
197                continue;
198            }
199            if (bestXY->fY < testXY.fY) {
200                continue;
201            }
202            if (bestXY->fY == testXY.fY && bestXY->fX < testXY.fX) {
203                continue;
204            }
205        }
206        *topStart = testSegment;
207        *bestXY = testXY;
208    }
209}
210
211SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
212    int segmentCount = fSegments.count();
213    for (int test = 0; test < segmentCount; ++test) {
214        SkOpSegment* testSegment = &fSegments[test];
215        if (testSegment->done()) {
216            continue;
217        }
218        testSegment->undoneSpan(start, end);
219        return testSegment;
220    }
221    return NULL;
222}
223
224#if DEBUG_SHOW_WINDING
225int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) {
226    int count = fSegments.count();
227    int sum = 0;
228    for (int index = 0; index < count; ++index) {
229        sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
230    }
231//      SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
232    return sum;
233}
234
235static void SkOpContour::debugShowWindingValues(const SkTDArray<SkOpContour*>& contourList) {
236//     int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
237//    int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
238    int ofInterest = 1 << 5 | 1 << 8;
239    int total = 0;
240    int index;
241    for (index = 0; index < contourList.count(); ++index) {
242        total += contourList[index]->segments().count();
243    }
244    int sum = 0;
245    for (index = 0; index < contourList.count(); ++index) {
246        sum += contourList[index]->debugShowWindingValues(total, ofInterest);
247    }
248//       SkDebugf("%s total=%d\n", __FUNCTION__, sum);
249}
250#endif
251
252void SkOpContour::setBounds() {
253    int count = fSegments.count();
254    if (count == 0) {
255        SkDebugf("%s empty contour\n", __FUNCTION__);
256        SkASSERT(0);
257        // FIXME: delete empty contour?
258        return;
259    }
260    fBounds = fSegments.front().bounds();
261    for (int index = 1; index < count; ++index) {
262        fBounds.add(fSegments[index].bounds());
263    }
264}
265
266