ShapeOps.cpp revision fb51afb03e76c5701fffaa847584a8b7b2c18a7e
1235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com/*
2235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com * Copyright 2012 Google Inc.
3235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com *
4235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com * Use of this source code is governed by a BSD-style license that can be
5235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com * found in the LICENSE file.
6235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com */
7235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com#include "Simplify.h"
8235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com
9235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.comnamespace Op {
10235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com
11235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com#include "Simplify.cpp"
12235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com
13235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.comstatic bool windingIsActive(int winding, int spanWinding, int oppWinding,
14235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        const ShapeOp op) {
15235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
16235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            && (!winding || !spanWinding || winding == -spanWinding);
17235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com}
18235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com
19055c7c299cb47eebd360b809ad58a0006e2e55f7skia.committer@gmail.comstatic void bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
20235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        const int aXorMask, const int bXorMask, SkPath& simple) {
21235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    bool firstContour = true;
22235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    do {
23fb51afb03e76c5701fffaa847584a8b7b2c18a7ecaryclark@google.com
24fb51afb03e76c5701fffaa847584a8b7b2c18a7ecaryclark@google.com#if SORTABLE_CONTOURS // old way
25235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        Segment* topStart = findTopContour(contourList);
26235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        if (!topStart) {
27235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            break;
28235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        }
29235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        // Start at the top. Above the top is outside, below is inside.
30235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        // follow edges to intersection by changing the index by direction.
31235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        int index, endIndex;
32235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        Segment* current = topStart->findTop(index, endIndex);
33fb51afb03e76c5701fffaa847584a8b7b2c18a7ecaryclark@google.com#else // new way: iterate while top is unsortable
34fb51afb03e76c5701fffaa847584a8b7b2c18a7ecaryclark@google.com        int index, endIndex;
35fb51afb03e76c5701fffaa847584a8b7b2c18a7ecaryclark@google.com        Segment* current = findSortableTop(contourList, index, endIndex);
36fb51afb03e76c5701fffaa847584a8b7b2c18a7ecaryclark@google.com        if (!current) {
37fb51afb03e76c5701fffaa847584a8b7b2c18a7ecaryclark@google.com            break;
38fb51afb03e76c5701fffaa847584a8b7b2c18a7ecaryclark@google.com        }
39fb51afb03e76c5701fffaa847584a8b7b2c18a7ecaryclark@google.com#endif
40235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        int contourWinding;
41235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        if (firstContour) {
42235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            contourWinding = 0;
43235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            firstContour = false;
44235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        } else {
45235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            int sumWinding = current->windSum(SkMin32(index, endIndex));
46235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            // FIXME: don't I have to adjust windSum to get contourWinding?
47235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            if (sumWinding == SK_MinS32) {
48235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                sumWinding = current->computeSum(index, endIndex);
49235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            }
50235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            if (sumWinding == SK_MinS32) {
51235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                contourWinding = innerContourCheck(contourList, current,
52235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                        index, endIndex);
53235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            } else {
54235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                contourWinding = sumWinding;
55235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                int spanWinding = current->spanSign(index, endIndex);
56235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
57235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                if (inner) {
58235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                    contourWinding -= spanWinding;
59235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                }
60235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com#if DEBUG_WINDING
61235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
62235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                        sumWinding, spanWinding, SkSign32(index - endIndex),
63235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                        inner, contourWinding);
64235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com#endif
65235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            }
66235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com#if DEBUG_WINDING
67235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com         //   SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
68235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
69235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com#endif
70235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        }
71235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        SkPoint lastPt;
72235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        int winding = contourWinding;
73235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        int spanWinding = current->spanSign(index, endIndex);
74235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        int oppWinding = current->oppSign(index, endIndex);
75235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        bool active = windingIsActive(winding, spanWinding, oppWinding, op);
76235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        SkTDArray<Span*> chaseArray;
77c91dfe417a51f73c28ecf2708df1e0bee942c6eacaryclark@google.com        bool unsortable = false;
78235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        do {
79235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        #if DEBUG_WINDING
80235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
81235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                    __FUNCTION__, active ? "true" : "false",
82235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                    winding, spanWinding);
83235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        #endif
84235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            const SkPoint* firstPt = NULL;
85235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            do {
86235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                SkASSERT(!current->done());
87235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                int nextStart = index;
88235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                int nextEnd = endIndex;
89235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                Segment* next = current->findNextOp(chaseArray, active,
90c91dfe417a51f73c28ecf2708df1e0bee942c6eacaryclark@google.com                        nextStart, nextEnd, winding, spanWinding, unsortable, op,
91235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                        aXorMask, bXorMask);
92235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                if (!next) {
93c91dfe417a51f73c28ecf2708df1e0bee942c6eacaryclark@google.com                    // FIXME: if unsortable, allow partial paths to be later
94c91dfe417a51f73c28ecf2708df1e0bee942c6eacaryclark@google.com                    // assembled
95c91dfe417a51f73c28ecf2708df1e0bee942c6eacaryclark@google.com                    SkASSERT(!unsortable);
96235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                    if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
97235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                        lastPt = current->addCurveTo(index, endIndex, simple, true);
98235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                        SkASSERT(*firstPt == lastPt);
99235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                    }
100235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                    break;
101235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                }
102235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                if (!firstPt) {
103235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                    firstPt = &current->addMoveTo(index, simple, active);
104235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                }
105235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                lastPt = current->addCurveTo(index, endIndex, simple, active);
106235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                current = next;
107235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                index = nextStart;
108235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                endIndex = nextEnd;
109235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            } while (*firstPt != lastPt && (active || !current->done()));
110235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            if (firstPt && active) {
111235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        #if DEBUG_PATH_CONSTRUCTION
112235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                SkDebugf("%s close\n", __FUNCTION__);
113235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        #endif
114235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                simple.close();
115235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            }
116235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            current = findChase(chaseArray, index, endIndex, contourWinding);
117235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        #if DEBUG_ACTIVE_SPANS
118235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            debugShowActiveSpans(contourList);
119235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        #endif
120235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            if (!current) {
121235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                break;
122235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            }
123235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            int lesser = SkMin32(index, endIndex);
124235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            spanWinding = current->spanSign(index, endIndex);
125235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            winding = current->windSum(lesser);
126235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            bool inner = useInnerWinding(winding - spanWinding, winding);
127235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        #if DEBUG_WINDING
128235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
129235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                    " inner=%d result=%d\n",
130235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                    __FUNCTION__, current->debugID(), current->t(lesser),
131235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                    spanWinding, winding, SkSign32(index - endIndex),
132235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                    useInnerWinding(winding - spanWinding, winding),
133235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                    inner ? winding - spanWinding : winding);
134235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        #endif
135235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            if (inner) {
136235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com                winding -= spanWinding;
137235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            }
138235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            int oppWinding = current->oppSign(index, endIndex);
139235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            active = windingIsActive(winding, spanWinding, oppWinding, op);
140235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        } while (true);
141235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    } while (true);
142235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com}
143235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com
144235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com} // end of Op namespace
145235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com
146235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com
147235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.comvoid operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) {
148235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    result.reset();
149235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    result.setFillType(SkPath::kEvenOdd_FillType);
150235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    // turn path into list of segments
151235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    SkTArray<Op::Contour> contours;
152235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    // FIXME: add self-intersecting cubics' T values to segment
153235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    Op::EdgeBuilder builder(one, contours);
154235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    const int aXorMask = builder.xorMask();
155235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    builder.addOperand(two);
156235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    const int bXorMask = builder.xorMask();
157235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    builder.finish();
158235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    SkTDArray<Op::Contour*> contourList;
159235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    makeContourList(contours, contourList);
160235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    Op::Contour** currentPtr = contourList.begin();
161235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    if (!currentPtr) {
162235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        return;
163235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    }
164235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    Op::Contour** listEnd = contourList.end();
165235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    // find all intersections between segments
166235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    do {
167235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        Op::Contour** nextPtr = currentPtr;
168235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        Op::Contour* current = *currentPtr++;
169235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        Op::Contour* next;
170235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        do {
171235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com            next = *nextPtr++;
172235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com        } while (addIntersectTs(current, next) && nextPtr != listEnd);
173235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    } while (currentPtr != listEnd);
174235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    // eat through coincident edges
175235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    coincidenceCheck(contourList);
176235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    fixOtherTIndex(contourList);
177235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    // construct closed contours
178235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com    bridgeOp(contourList, op, aXorMask, bXorMask, result);
179235f56a92f6eb6accbb243e11b3c45e3798f38f2caryclark@google.com}
180