blob: f33c8b183f0ba0ecd196316ecee78fbe31424860 [file] [log] [blame]
caryclark@google.com235f56a2012-09-14 14:19:30 +00001/*
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 "Simplify.h"
8
9namespace Op {
10
11#include "Simplify.cpp"
12
13static bool windingIsActive(int winding, int spanWinding, int oppWinding,
14 const ShapeOp op) {
15 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
16 && (!winding || !spanWinding || winding == -spanWinding);
17}
18
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +000019static void bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
caryclark@google.com235f56a2012-09-14 14:19:30 +000020 const int aXorMask, const int bXorMask, SkPath& simple) {
21 bool firstContour = true;
22 do {
23 Segment* topStart = findTopContour(contourList);
24 if (!topStart) {
25 break;
26 }
27 // Start at the top. Above the top is outside, below is inside.
28 // follow edges to intersection by changing the index by direction.
29 int index, endIndex;
30 Segment* current = topStart->findTop(index, endIndex);
31 int contourWinding;
32 if (firstContour) {
33 contourWinding = 0;
34 firstContour = false;
35 } else {
36 int sumWinding = current->windSum(SkMin32(index, endIndex));
37 // FIXME: don't I have to adjust windSum to get contourWinding?
38 if (sumWinding == SK_MinS32) {
39 sumWinding = current->computeSum(index, endIndex);
40 }
41 if (sumWinding == SK_MinS32) {
42 contourWinding = innerContourCheck(contourList, current,
43 index, endIndex);
44 } else {
45 contourWinding = sumWinding;
46 int spanWinding = current->spanSign(index, endIndex);
47 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
48 if (inner) {
49 contourWinding -= spanWinding;
50 }
51#if DEBUG_WINDING
52 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
53 sumWinding, spanWinding, SkSign32(index - endIndex),
54 inner, contourWinding);
55#endif
56 }
57#if DEBUG_WINDING
58 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
59 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
60#endif
61 }
62 SkPoint lastPt;
63 int winding = contourWinding;
64 int spanWinding = current->spanSign(index, endIndex);
65 int oppWinding = current->oppSign(index, endIndex);
66 bool active = windingIsActive(winding, spanWinding, oppWinding, op);
67 SkTDArray<Span*> chaseArray;
caryclark@google.comc91dfe42012-10-16 12:06:27 +000068 bool unsortable = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +000069 do {
70 #if DEBUG_WINDING
71 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
72 __FUNCTION__, active ? "true" : "false",
73 winding, spanWinding);
74 #endif
75 const SkPoint* firstPt = NULL;
76 do {
77 SkASSERT(!current->done());
78 int nextStart = index;
79 int nextEnd = endIndex;
80 Segment* next = current->findNextOp(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +000081 nextStart, nextEnd, winding, spanWinding, unsortable, op,
caryclark@google.com235f56a2012-09-14 14:19:30 +000082 aXorMask, bXorMask);
83 if (!next) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +000084 // FIXME: if unsortable, allow partial paths to be later
85 // assembled
86 SkASSERT(!unsortable);
caryclark@google.com235f56a2012-09-14 14:19:30 +000087 if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
88 lastPt = current->addCurveTo(index, endIndex, simple, true);
89 SkASSERT(*firstPt == lastPt);
90 }
91 break;
92 }
93 if (!firstPt) {
94 firstPt = &current->addMoveTo(index, simple, active);
95 }
96 lastPt = current->addCurveTo(index, endIndex, simple, active);
97 current = next;
98 index = nextStart;
99 endIndex = nextEnd;
100 } while (*firstPt != lastPt && (active || !current->done()));
101 if (firstPt && active) {
102 #if DEBUG_PATH_CONSTRUCTION
103 SkDebugf("%s close\n", __FUNCTION__);
104 #endif
105 simple.close();
106 }
107 current = findChase(chaseArray, index, endIndex, contourWinding);
108 #if DEBUG_ACTIVE_SPANS
109 debugShowActiveSpans(contourList);
110 #endif
111 if (!current) {
112 break;
113 }
114 int lesser = SkMin32(index, endIndex);
115 spanWinding = current->spanSign(index, endIndex);
116 winding = current->windSum(lesser);
117 bool inner = useInnerWinding(winding - spanWinding, winding);
118 #if DEBUG_WINDING
119 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
120 " inner=%d result=%d\n",
121 __FUNCTION__, current->debugID(), current->t(lesser),
122 spanWinding, winding, SkSign32(index - endIndex),
123 useInnerWinding(winding - spanWinding, winding),
124 inner ? winding - spanWinding : winding);
125 #endif
126 if (inner) {
127 winding -= spanWinding;
128 }
129 int oppWinding = current->oppSign(index, endIndex);
130 active = windingIsActive(winding, spanWinding, oppWinding, op);
131 } while (true);
132 } while (true);
133}
134
135} // end of Op namespace
136
137
138void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) {
139 result.reset();
140 result.setFillType(SkPath::kEvenOdd_FillType);
141 // turn path into list of segments
142 SkTArray<Op::Contour> contours;
143 // FIXME: add self-intersecting cubics' T values to segment
144 Op::EdgeBuilder builder(one, contours);
145 const int aXorMask = builder.xorMask();
146 builder.addOperand(two);
147 const int bXorMask = builder.xorMask();
148 builder.finish();
149 SkTDArray<Op::Contour*> contourList;
150 makeContourList(contours, contourList);
151 Op::Contour** currentPtr = contourList.begin();
152 if (!currentPtr) {
153 return;
154 }
155 Op::Contour** listEnd = contourList.end();
156 // find all intersections between segments
157 do {
158 Op::Contour** nextPtr = currentPtr;
159 Op::Contour* current = *currentPtr++;
160 Op::Contour* next;
161 do {
162 next = *nextPtr++;
163 } while (addIntersectTs(current, next) && nextPtr != listEnd);
164 } while (currentPtr != listEnd);
165 // eat through coincident edges
166 coincidenceCheck(contourList);
167 fixOtherTIndex(contourList);
168 // construct closed contours
169 bridgeOp(contourList, op, aXorMask, bXorMask, result);
170}