blob: 669fa6d14df8723335bc9bba6a34601d1364cf98 [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.comf839c032012-10-26 21:03:50 +000020 const int aXorMask, const int bXorMask, PathWrapper& simple) {
caryclark@google.com235f56a2012-09-14 14:19:30 +000021 bool firstContour = true;
caryclark@google.comf839c032012-10-26 21:03:50 +000022 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com235f56a2012-09-14 14:19:30 +000023 do {
caryclark@google.comfb51afb2012-10-19 15:54:16 +000024
25#if SORTABLE_CONTOURS // old way
caryclark@google.com235f56a2012-09-14 14:19:30 +000026 Segment* topStart = findTopContour(contourList);
27 if (!topStart) {
28 break;
29 }
30 // Start at the top. Above the top is outside, below is inside.
31 // follow edges to intersection by changing the index by direction.
32 int index, endIndex;
33 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfb51afb2012-10-19 15:54:16 +000034#else // new way: iterate while top is unsortable
35 int index, endIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +000036 Segment* current = findSortableTop(contourList, index, endIndex, topLeft);
caryclark@google.comfb51afb2012-10-19 15:54:16 +000037 if (!current) {
38 break;
39 }
40#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +000041 int contourWinding;
42 if (firstContour) {
43 contourWinding = 0;
44 firstContour = false;
45 } else {
46 int sumWinding = current->windSum(SkMin32(index, endIndex));
47 // FIXME: don't I have to adjust windSum to get contourWinding?
48 if (sumWinding == SK_MinS32) {
49 sumWinding = current->computeSum(index, endIndex);
50 }
51 if (sumWinding == SK_MinS32) {
52 contourWinding = innerContourCheck(contourList, current,
53 index, endIndex);
54 } else {
55 contourWinding = sumWinding;
56 int spanWinding = current->spanSign(index, endIndex);
57 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
58 if (inner) {
59 contourWinding -= spanWinding;
60 }
61#if DEBUG_WINDING
62 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
63 sumWinding, spanWinding, SkSign32(index - endIndex),
64 inner, contourWinding);
65#endif
66 }
67#if DEBUG_WINDING
68 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
69 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
70#endif
71 }
caryclark@google.comf839c032012-10-26 21:03:50 +000072 // SkPoint lastPt;
caryclark@google.com235f56a2012-09-14 14:19:30 +000073 int winding = contourWinding;
74 int spanWinding = current->spanSign(index, endIndex);
75 int oppWinding = current->oppSign(index, endIndex);
76 bool active = windingIsActive(winding, spanWinding, oppWinding, op);
77 SkTDArray<Span*> chaseArray;
caryclark@google.comc91dfe42012-10-16 12:06:27 +000078 bool unsortable = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +000079 do {
80 #if DEBUG_WINDING
81 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
82 __FUNCTION__, active ? "true" : "false",
83 winding, spanWinding);
84 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +000085 // const SkPoint* firstPt = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +000086 do {
87 SkASSERT(!current->done());
88 int nextStart = index;
89 int nextEnd = endIndex;
90 Segment* next = current->findNextOp(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +000091 nextStart, nextEnd, winding, spanWinding, unsortable, op,
caryclark@google.com235f56a2012-09-14 14:19:30 +000092 aXorMask, bXorMask);
93 if (!next) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +000094 // FIXME: if unsortable, allow partial paths to be later
95 // assembled
96 SkASSERT(!unsortable);
caryclark@google.comf839c032012-10-26 21:03:50 +000097 if (active && simple.hasMove()
98 && current->verb() != SkPath::kLine_Verb
99 && !simple.isClosed()) {
100 /* lastPt = */ current->addCurveTo(index, endIndex, simple, true);
101 SkASSERT(simple.isClosed());
caryclark@google.com235f56a2012-09-14 14:19:30 +0000102 }
103 break;
104 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000105 // if (!firstPt) {
106 // firstPt = &current->addMoveTo(index, simple, active);
107 // }
108 /* lastPt = */ current->addCurveTo(index, endIndex, simple, active);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000109 current = next;
110 index = nextStart;
111 endIndex = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +0000112 } while (!simple.isClosed() && (active || !current->done()));
113 if (simple.hasMove() && active) {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000114 #if DEBUG_PATH_CONSTRUCTION
115 SkDebugf("%s close\n", __FUNCTION__);
116 #endif
117 simple.close();
118 }
119 current = findChase(chaseArray, index, endIndex, contourWinding);
120 #if DEBUG_ACTIVE_SPANS
121 debugShowActiveSpans(contourList);
122 #endif
123 if (!current) {
124 break;
125 }
126 int lesser = SkMin32(index, endIndex);
127 spanWinding = current->spanSign(index, endIndex);
128 winding = current->windSum(lesser);
129 bool inner = useInnerWinding(winding - spanWinding, winding);
130 #if DEBUG_WINDING
131 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
132 " inner=%d result=%d\n",
133 __FUNCTION__, current->debugID(), current->t(lesser),
134 spanWinding, winding, SkSign32(index - endIndex),
135 useInnerWinding(winding - spanWinding, winding),
136 inner ? winding - spanWinding : winding);
137 #endif
138 if (inner) {
139 winding -= spanWinding;
140 }
141 int oppWinding = current->oppSign(index, endIndex);
142 active = windingIsActive(winding, spanWinding, oppWinding, op);
143 } while (true);
144 } while (true);
145}
146
147} // end of Op namespace
148
149
150void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) {
151 result.reset();
152 result.setFillType(SkPath::kEvenOdd_FillType);
153 // turn path into list of segments
154 SkTArray<Op::Contour> contours;
155 // FIXME: add self-intersecting cubics' T values to segment
156 Op::EdgeBuilder builder(one, contours);
157 const int aXorMask = builder.xorMask();
158 builder.addOperand(two);
159 const int bXorMask = builder.xorMask();
160 builder.finish();
161 SkTDArray<Op::Contour*> contourList;
162 makeContourList(contours, contourList);
163 Op::Contour** currentPtr = contourList.begin();
164 if (!currentPtr) {
165 return;
166 }
167 Op::Contour** listEnd = contourList.end();
168 // find all intersections between segments
169 do {
170 Op::Contour** nextPtr = currentPtr;
171 Op::Contour* current = *currentPtr++;
172 Op::Contour* next;
173 do {
174 next = *nextPtr++;
175 } while (addIntersectTs(current, next) && nextPtr != listEnd);
176 } while (currentPtr != listEnd);
177 // eat through coincident edges
178 coincidenceCheck(contourList);
179 fixOtherTIndex(contourList);
180 // construct closed contours
caryclark@google.comf839c032012-10-26 21:03:50 +0000181 Op::PathWrapper wrapper(result);
182 bridgeOp(contourList, op, aXorMask, bXorMask, wrapper);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000183}