SkPathOpsSimplify.cpp revision 07393cab57ce74a4aae89a31fae9aaa9780fc19d
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#include "SkAddIntersections.h"
8#include "SkOpEdgeBuilder.h"
9#include "SkPathOpsCommon.h"
10#include "SkPathWriter.h"
11
12static bool bridgeWinding(SkTDArray<SkOpContour*>& contourList, SkPathWriter* simple) {
13    bool firstContour = true;
14    bool unsortable = false;
15    bool topUnsortable = false;
16    SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
17    do {
18        int index, endIndex;
19        bool topDone;
20        SkOpSegment* current = FindSortableTop(contourList, &firstContour, &index, &endIndex,
21                &topLeft, &topUnsortable, &topDone, false);
22        if (!current) {
23            if (topUnsortable || !topDone) {
24                topUnsortable = false;
25                SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
26                topLeft.fX = topLeft.fY = SK_ScalarMin;
27                continue;
28            }
29            break;
30        }
31        SkTDArray<SkOpSpan*> chaseArray;
32        do {
33            if (current->activeWinding(index, endIndex)) {
34                do {
35            #if DEBUG_ACTIVE_SPANS
36                    if (!unsortable && current->done()) {
37                        DebugShowActiveSpans(contourList);
38                    }
39            #endif
40                    SkASSERT(unsortable || !current->done());
41                    int nextStart = index;
42                    int nextEnd = endIndex;
43                    SkOpSegment* next = current->findNextWinding(&chaseArray, &nextStart, &nextEnd,
44                            &unsortable);
45                    if (!next) {
46                        if (!unsortable && simple->hasMove()
47                                && current->verb() != SkPath::kLine_Verb
48                                && !simple->isClosed()) {
49                            current->addCurveTo(index, endIndex, simple, true);
50                            SkASSERT(simple->isClosed());
51                        }
52                        break;
53                    }
54        #if DEBUG_FLOW
55            SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
56                    current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
57                    current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
58        #endif
59                    current->addCurveTo(index, endIndex, simple, true);
60                    current = next;
61                    index = nextStart;
62                    endIndex = nextEnd;
63                } while (!simple->isClosed() && (!unsortable
64                        || !current->done(SkMin32(index, endIndex))));
65                if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
66                    SkASSERT(unsortable);
67                    int min = SkMin32(index, endIndex);
68                    if (!current->done(min)) {
69                        current->addCurveTo(index, endIndex, simple, true);
70                        current->markDoneUnary(min);
71                    }
72                }
73                simple->close();
74            } else {
75                SkOpSpan* last = current->markAndChaseDoneUnary(index, endIndex);
76                if (last && !last->fLoop) {
77                    *chaseArray.append() = last;
78                }
79            }
80            current = FindChase(chaseArray, index, endIndex);
81        #if DEBUG_ACTIVE_SPANS
82            DebugShowActiveSpans(contourList);
83        #endif
84            if (!current) {
85                break;
86            }
87        } while (true);
88    } while (true);
89    return simple->someAssemblyRequired();
90}
91
92// returns true if all edges were processed
93static bool bridgeXor(SkTDArray<SkOpContour*>& contourList, SkPathWriter* simple) {
94    SkOpSegment* current;
95    int start, end;
96    bool unsortable = false;
97    bool closable = true;
98    while ((current = FindUndone(contourList, &start, &end))) {
99        do {
100    #if DEBUG_ACTIVE_SPANS
101            if (!unsortable && current->done()) {
102                DebugShowActiveSpans(contourList);
103            }
104    #endif
105            SkASSERT(unsortable || !current->done());
106            int nextStart = start;
107            int nextEnd = end;
108            SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
109            if (!next) {
110                if (!unsortable && simple->hasMove()
111                        && current->verb() != SkPath::kLine_Verb
112                        && !simple->isClosed()) {
113                    current->addCurveTo(start, end, simple, true);
114                    SkASSERT(simple->isClosed());
115                }
116                break;
117            }
118        #if DEBUG_FLOW
119            SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
120                    current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
121                    current->xyAtT(end).fX, current->xyAtT(end).fY);
122        #endif
123            current->addCurveTo(start, end, simple, true);
124            current = next;
125            start = nextStart;
126            end = nextEnd;
127        } while (!simple->isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
128        if (!simple->isClosed()) {
129            SkASSERT(unsortable);
130            int min = SkMin32(start, end);
131            if (!current->done(min)) {
132                current->addCurveTo(start, end, simple, true);
133                current->markDone(min, 1);
134            }
135            closable = false;
136        }
137        simple->close();
138    #if DEBUG_ACTIVE_SPANS
139        DebugShowActiveSpans(contourList);
140    #endif
141    }
142    return closable;
143}
144
145// FIXME : add this as a member of SkPath
146void Simplify(const SkPath& path, SkPath* result) {
147#if DEBUG_SORT || DEBUG_SWAP_TOP
148    gDebugSortCount = gDebugSortCountDefault;
149#endif
150    // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
151    result->reset();
152    result->setFillType(SkPath::kEvenOdd_FillType);
153    SkPathWriter simple(*result);
154
155    // turn path into list of segments
156    SkTArray<SkOpContour> contours;
157    SkOpEdgeBuilder builder(path, contours);
158    builder.finish();
159    SkTDArray<SkOpContour*> contourList;
160    MakeContourList(contours, contourList, false, false);
161    SkOpContour** currentPtr = contourList.begin();
162    if (!currentPtr) {
163        return;
164    }
165    SkOpContour** listEnd = contourList.end();
166    // find all intersections between segments
167    do {
168        SkOpContour** nextPtr = currentPtr;
169        SkOpContour* current = *currentPtr++;
170        if (current->containsCubics()) {
171            AddSelfIntersectTs(current);
172        }
173        SkOpContour* next;
174        do {
175            next = *nextPtr++;
176        } while (AddIntersectTs(current, next) && nextPtr != listEnd);
177    } while (currentPtr != listEnd);
178    // eat through coincident edges
179    CoincidenceCheck(&contourList, 0);
180    FixOtherTIndex(&contourList);
181    SortSegments(&contourList);
182#if DEBUG_ACTIVE_SPANS
183    DebugShowActiveSpans(contourList);
184#endif
185    // construct closed contours
186    if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &simple)
187                : !bridgeXor(contourList, &simple))
188    {  // if some edges could not be resolved, assemble remaining fragments
189        SkPath temp;
190        temp.setFillType(SkPath::kEvenOdd_FillType);
191        SkPathWriter assembled(temp);
192        Assemble(simple, &assembled);
193        *result = *assembled.nativePath();
194    }
195}
196