/src/skia/src/pathops/SkPathOpsAsWinding.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2018 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 "include/core/SkPath.h" |
8 | | #include "include/core/SkPathBuilder.h" |
9 | | #include "include/core/SkPathTypes.h" |
10 | | #include "include/core/SkPoint.h" |
11 | | #include "include/core/SkRect.h" |
12 | | #include "include/core/SkScalar.h" |
13 | | #include "include/core/SkTypes.h" |
14 | | #include "include/pathops/SkPathOps.h" |
15 | | #include "include/private/base/SkMacros.h" |
16 | | #include "src/core/SkPathPriv.h" |
17 | | #include "src/pathops/SkPathOpsConic.h" |
18 | | #include "src/pathops/SkPathOpsCubic.h" |
19 | | #include "src/pathops/SkPathOpsCurve.h" |
20 | | #include "src/pathops/SkPathOpsPoint.h" |
21 | | #include "src/pathops/SkPathOpsQuad.h" |
22 | | #include "src/pathops/SkPathOpsTypes.h" |
23 | | |
24 | | #include <algorithm> |
25 | | #include <vector> |
26 | | |
27 | | using std::vector; |
28 | | |
29 | | struct Contour { |
30 | | enum class Direction { // SkPathDirection doesn't have 'none' state |
31 | | kCCW = -1, |
32 | | kNone, |
33 | | kCW, |
34 | | }; |
35 | | |
36 | | Contour(const SkRect& bounds, int lastStart, int verbStart) |
37 | | : fBounds(bounds) |
38 | | , fVerbStart(lastStart) |
39 | 17.9k | , fVerbEnd(verbStart) { |
40 | 17.9k | } |
41 | | |
42 | | vector<Contour*> fChildren; |
43 | | const SkRect fBounds; |
44 | | SkPoint fMinXY{SK_ScalarMax, SK_ScalarMax}; |
45 | | const int fVerbStart; |
46 | | const int fVerbEnd; |
47 | | Direction fDirection{Direction::kNone}; |
48 | | bool fContained{false}; |
49 | | bool fReverse{false}; |
50 | | }; |
51 | | |
52 | | static const int kPtCount[] = { 1, 1, 2, 2, 3, 0 }; |
53 | | static const int kPtIndex[] = { 0, 1, 1, 1, 1, 0 }; |
54 | | |
55 | 4.57k | static Contour::Direction to_direction(SkScalar dy) { |
56 | 4.57k | return dy > 0 ? Contour::Direction::kCCW : dy < 0 ? Contour::Direction::kCW : |
57 | 2.52k | Contour::Direction::kNone; |
58 | 4.57k | } |
59 | | |
60 | 30.2k | static int contains_edge(SkPoint pts[4], SkPath::Verb verb, SkScalar weight, const SkPoint& edge) { |
61 | 30.2k | SkRect bounds; |
62 | 30.2k | bounds.setBounds(pts, kPtCount[verb] + 1); |
63 | 30.2k | if (bounds.fTop > edge.fY) { |
64 | 3.36k | return 0; |
65 | 3.36k | } |
66 | 26.8k | if (bounds.fBottom <= edge.fY) { // check to see if y is at line end to avoid double counting |
67 | 5.12k | return 0; |
68 | 5.12k | } |
69 | 21.7k | if (bounds.fLeft >= edge.fX) { |
70 | 15.3k | return 0; |
71 | 15.3k | } |
72 | 6.41k | int winding = 0; |
73 | 6.41k | double tVals[3]; |
74 | 6.41k | Contour::Direction directions[3]; |
75 | | // must intersect horz ray with curve in case it intersects more than once |
76 | 6.41k | int count = (*CurveIntercept[verb * 2])(pts, weight, edge.fY, tVals); |
77 | 6.41k | SkASSERT(between(0, count, 3)); |
78 | | // remove results to the right of edge |
79 | 13.3k | for (int index = 0; index < count; ) { |
80 | 6.94k | SkScalar intersectX = (*CurvePointAtT[verb])(pts, weight, tVals[index]).fX; |
81 | 6.94k | if (intersectX < edge.fX) { |
82 | 4.49k | ++index; |
83 | 4.49k | continue; |
84 | 4.49k | } |
85 | 2.45k | if (intersectX > edge.fX) { |
86 | 1.54k | tVals[index] = tVals[--count]; |
87 | 1.54k | continue; |
88 | 1.54k | } |
89 | | // if intersect x equals edge x, we need to determine if pts is to the left or right of edge |
90 | 908 | if (pts[0].fX < edge.fX && pts[kPtCount[verb]].fX < edge.fX) { |
91 | 88 | ++index; |
92 | 88 | continue; |
93 | 88 | } |
94 | | // TODO : other cases need discriminating. need op angle code to figure it out |
95 | | // example: edge ends 45 degree diagonal going up. If pts is to the left of edge, keep. |
96 | | // if pts is to the right of edge, discard. With code as is, can't distiguish the two cases. |
97 | 820 | tVals[index] = tVals[--count]; |
98 | 820 | } |
99 | | // use first derivative to determine if intersection is contributing +1 or -1 to winding |
100 | 10.9k | for (int index = 0; index < count; ++index) { |
101 | 4.57k | directions[index] = to_direction((*CurveSlopeAtT[verb])(pts, weight, tVals[index]).fY); |
102 | 4.57k | } |
103 | 10.9k | for (int index = 0; index < count; ++index) { |
104 | | // skip intersections that end at edge and go up |
105 | 4.57k | if (zero_or_one(tVals[index]) && Contour::Direction::kCCW != directions[index]) { |
106 | 1.63k | continue; |
107 | 1.63k | } |
108 | 2.94k | winding += (int) directions[index]; |
109 | 2.94k | } |
110 | 6.41k | return winding; // note winding indicates containership, not contour direction |
111 | 21.7k | } SkPathOpsAsWinding.cpp:contains_edge(SkPoint*, SkPath::Verb, float, SkPoint const&) Line | Count | Source | 60 | 30.2k | static int contains_edge(SkPoint pts[4], SkPath::Verb verb, SkScalar weight, const SkPoint& edge) { | 61 | 30.2k | SkRect bounds; | 62 | 30.2k | bounds.setBounds(pts, kPtCount[verb] + 1); | 63 | 30.2k | if (bounds.fTop > edge.fY) { | 64 | 3.36k | return 0; | 65 | 3.36k | } | 66 | 26.8k | if (bounds.fBottom <= edge.fY) { // check to see if y is at line end to avoid double counting | 67 | 5.12k | return 0; | 68 | 5.12k | } | 69 | 21.7k | if (bounds.fLeft >= edge.fX) { | 70 | 15.3k | return 0; | 71 | 15.3k | } | 72 | 6.41k | int winding = 0; | 73 | 6.41k | double tVals[3]; | 74 | 6.41k | Contour::Direction directions[3]; | 75 | | // must intersect horz ray with curve in case it intersects more than once | 76 | 6.41k | int count = (*CurveIntercept[verb * 2])(pts, weight, edge.fY, tVals); | 77 | 6.41k | SkASSERT(between(0, count, 3)); | 78 | | // remove results to the right of edge | 79 | 13.3k | for (int index = 0; index < count; ) { | 80 | 6.94k | SkScalar intersectX = (*CurvePointAtT[verb])(pts, weight, tVals[index]).fX; | 81 | 6.94k | if (intersectX < edge.fX) { | 82 | 4.49k | ++index; | 83 | 4.49k | continue; | 84 | 4.49k | } | 85 | 2.45k | if (intersectX > edge.fX) { | 86 | 1.54k | tVals[index] = tVals[--count]; | 87 | 1.54k | continue; | 88 | 1.54k | } | 89 | | // if intersect x equals edge x, we need to determine if pts is to the left or right of edge | 90 | 908 | if (pts[0].fX < edge.fX && pts[kPtCount[verb]].fX < edge.fX) { | 91 | 88 | ++index; | 92 | 88 | continue; | 93 | 88 | } | 94 | | // TODO : other cases need discriminating. need op angle code to figure it out | 95 | | // example: edge ends 45 degree diagonal going up. If pts is to the left of edge, keep. | 96 | | // if pts is to the right of edge, discard. With code as is, can't distiguish the two cases. | 97 | 820 | tVals[index] = tVals[--count]; | 98 | 820 | } | 99 | | // use first derivative to determine if intersection is contributing +1 or -1 to winding | 100 | 10.9k | for (int index = 0; index < count; ++index) { | 101 | 4.57k | directions[index] = to_direction((*CurveSlopeAtT[verb])(pts, weight, tVals[index]).fY); | 102 | 4.57k | } | 103 | 10.9k | for (int index = 0; index < count; ++index) { | 104 | | // skip intersections that end at edge and go up | 105 | 4.57k | if (zero_or_one(tVals[index]) && Contour::Direction::kCCW != directions[index]) { | 106 | 1.63k | continue; | 107 | 1.63k | } | 108 | 2.94k | winding += (int) directions[index]; | 109 | 2.94k | } | 110 | 6.41k | return winding; // note winding indicates containership, not contour direction | 111 | 21.7k | } |
Unexecuted instantiation: SkPathOpsAsWinding.cpp:contains_edge(SkPoint*, SkPath::Verb, float, SkPoint const&) |
112 | | |
113 | 41.0k | static SkScalar conic_weight(const SkPath::Iter& iter, SkPath::Verb verb) { |
114 | 41.0k | return SkPath::kConic_Verb == verb ? iter.conicWeight() : 1; |
115 | 41.0k | } |
116 | | |
117 | 10.8k | static SkPoint left_edge(SkPoint pts[4], SkPath::Verb verb, SkScalar weight) { |
118 | 10.8k | SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb); |
119 | 10.8k | SkPoint result; |
120 | 10.8k | double t SK_INIT_TO_AVOID_WARNING; |
121 | 10.8k | int roots = 0; |
122 | 10.8k | if (SkPath::kLine_Verb == verb) { |
123 | 6.74k | result = pts[0].fX < pts[1].fX ? pts[0] : pts[1]; |
124 | 6.74k | } else if (SkPath::kQuad_Verb == verb) { |
125 | 2.05k | SkDQuad quad; |
126 | 2.05k | quad.set(pts); |
127 | 2.05k | if (!quad.monotonicInX()) { |
128 | 928 | roots = SkDQuad::FindExtrema(&quad[0].fX, &t); |
129 | 928 | } |
130 | 2.05k | if (roots) { |
131 | 905 | result = quad.ptAtT(t).asSkPoint(); |
132 | 1.14k | } else { |
133 | 1.14k | result = pts[0].fX < pts[2].fX ? pts[0] : pts[2]; |
134 | 1.14k | } |
135 | 2.05k | } else if (SkPath::kConic_Verb == verb) { |
136 | 954 | SkDConic conic; |
137 | 954 | conic.set(pts, weight); |
138 | 954 | if (!conic.monotonicInX()) { |
139 | 419 | roots = SkDConic::FindExtrema(&conic[0].fX, weight, &t); |
140 | 419 | } |
141 | 954 | if (roots) { |
142 | 316 | result = conic.ptAtT(t).asSkPoint(); |
143 | 638 | } else { |
144 | 638 | result = pts[0].fX < pts[2].fX ? pts[0] : pts[2]; |
145 | 638 | } |
146 | 1.06k | } else { |
147 | 1.06k | SkASSERT(SkPath::kCubic_Verb == verb); |
148 | 1.06k | SkDCubic cubic; |
149 | 1.06k | cubic.set(pts); |
150 | 1.06k | if (!cubic.monotonicInX()) { |
151 | 712 | double tValues[2]; |
152 | 712 | roots = SkDCubic::FindExtrema(&cubic[0].fX, tValues); |
153 | 712 | SkASSERT(roots <= 2); |
154 | 1.91k | for (int index = 0; index < roots; ++index) { |
155 | 1.20k | SkPoint temp = cubic.ptAtT(tValues[index]).asSkPoint(); |
156 | 1.20k | if (0 == index || result.fX > temp.fX) { |
157 | 1.04k | result = temp; |
158 | 1.04k | } |
159 | 1.20k | } |
160 | 712 | } |
161 | 1.06k | if (roots) { |
162 | 689 | result = cubic.ptAtT(t).asSkPoint(); |
163 | 689 | } else { |
164 | 377 | result = pts[0].fX < pts[3].fX ? pts[0] : pts[3]; |
165 | 377 | } |
166 | 1.06k | } |
167 | 10.8k | return result; |
168 | 10.8k | } |
169 | | |
170 | | class OpAsWinding { |
171 | | public: |
172 | | enum class Edge { |
173 | | kInitial, |
174 | | kCompare, |
175 | | }; |
176 | | |
177 | | OpAsWinding(const SkPath& path) |
178 | 657 | : fPath(path) { |
179 | 657 | } |
180 | | |
181 | 657 | void contourBounds(vector<Contour>* containers) { |
182 | 657 | SkRect bounds; |
183 | 657 | bounds.setEmpty(); |
184 | 657 | int lastStart = 0; |
185 | 657 | int verbStart = 0; |
186 | 63.7k | for (auto [verb, pts, w] : SkPathPriv::Iterate(fPath)) { |
187 | 63.7k | if (SkPathVerb::kMove == verb) { |
188 | 20.1k | if (!bounds.isEmpty()) { |
189 | 16.7k | containers->emplace_back(bounds, lastStart, verbStart); |
190 | 16.7k | lastStart = verbStart; |
191 | 16.7k | } |
192 | 20.1k | bounds.setBounds(&pts[kPtIndex[SkPath::kMove_Verb]], kPtCount[SkPath::kMove_Verb]); |
193 | 20.1k | } |
194 | 63.7k | if (SkPathVerb::kLine <= verb && verb <= SkPathVerb::kCubic) { |
195 | 25.0k | SkRect verbBounds; |
196 | 25.0k | verbBounds.setBounds(&pts[kPtIndex[(int)verb]], kPtCount[(int)verb]); |
197 | 25.0k | bounds.joinPossiblyEmptyRect(verbBounds); |
198 | 25.0k | } |
199 | 63.7k | ++verbStart; |
200 | 63.7k | } |
201 | 657 | if (!bounds.isEmpty()) { |
202 | 604 | containers->emplace_back(bounds, lastStart, ++verbStart); |
203 | 604 | } |
204 | 657 | } |
205 | | |
206 | 18.2k | Contour::Direction getDirection(Contour& contour) { |
207 | 18.2k | SkPath::Iter iter(fPath, true); |
208 | 18.2k | int verbCount = -1; |
209 | 18.2k | SkPath::Verb verb; |
210 | 18.2k | SkPoint pts[4]; |
211 | | |
212 | 18.2k | SkScalar total_signed_area = 0; |
213 | 10.1M | do { |
214 | 10.1M | verb = iter.next(pts); |
215 | 10.1M | if (++verbCount < contour.fVerbStart) { |
216 | 3.70M | continue; |
217 | 3.70M | } |
218 | 6.42M | if (verbCount >= contour.fVerbEnd) { |
219 | 6.35M | continue; |
220 | 6.35M | } |
221 | 71.8k | if (SkPath::kLine_Verb > verb || verb > SkPath::kCubic_Verb) { |
222 | 32.8k | continue; |
223 | 32.8k | } |
224 | | |
225 | 39.0k | switch (verb) |
226 | 39.0k | { |
227 | 31.2k | case SkPath::kLine_Verb: |
228 | 31.2k | total_signed_area += (pts[0].fY - pts[1].fY) * (pts[0].fX + pts[1].fX); |
229 | 31.2k | break; |
230 | 3.78k | case SkPath::kQuad_Verb: |
231 | 5.89k | case SkPath::kConic_Verb: |
232 | 5.89k | total_signed_area += (pts[0].fY - pts[2].fY) * (pts[0].fX + pts[2].fX); |
233 | 5.89k | break; |
234 | 1.81k | case SkPath::kCubic_Verb: |
235 | 1.81k | total_signed_area += (pts[0].fY - pts[3].fY) * (pts[0].fX + pts[3].fX); |
236 | 1.81k | break; |
237 | 0 | default: |
238 | 0 | break; |
239 | 39.0k | } |
240 | 10.1M | } while (SkPath::kDone_Verb != verb); |
241 | | |
242 | 18.2k | return total_signed_area < 0 ? Contour::Direction::kCCW: Contour::Direction::kCW; |
243 | 18.2k | } |
244 | | |
245 | 19.0k | int nextEdge(Contour& contour, Edge edge) { |
246 | 19.0k | SkPath::Iter iter(fPath, true); |
247 | 19.0k | SkPoint pts[4]; |
248 | 19.0k | SkPath::Verb verb; |
249 | 19.0k | int verbCount = -1; |
250 | 19.0k | int winding = 0; |
251 | 10.3M | do { |
252 | 10.3M | verb = iter.next(pts); |
253 | 10.3M | if (++verbCount < contour.fVerbStart) { |
254 | 3.74M | continue; |
255 | 3.74M | } |
256 | 6.57M | if (verbCount >= contour.fVerbEnd) { |
257 | 6.49M | continue; |
258 | 6.49M | } |
259 | 76.7k | if (SkPath::kLine_Verb > verb || verb > SkPath::kCubic_Verb) { |
260 | 33.8k | continue; |
261 | 33.8k | } |
262 | 42.8k | bool horizontal = true; |
263 | 46.6k | for (int index = 1; index <= kPtCount[verb]; ++index) { |
264 | 44.8k | if (pts[0].fY != pts[index].fY) { |
265 | 41.0k | horizontal = false; |
266 | 41.0k | break; |
267 | 41.0k | } |
268 | 44.8k | } |
269 | 42.8k | if (horizontal) { |
270 | 1.80k | continue; |
271 | 1.80k | } |
272 | 41.0k | if (edge == Edge::kCompare) { |
273 | 30.2k | winding += contains_edge(pts, verb, conic_weight(iter, verb), contour.fMinXY); |
274 | 30.2k | continue; |
275 | 30.2k | } |
276 | 10.8k | SkASSERT(edge == Edge::kInitial); |
277 | 10.8k | SkPoint minXY = left_edge(pts, verb, conic_weight(iter, verb)); |
278 | 10.8k | if (minXY.fX > contour.fMinXY.fX) { |
279 | 3.79k | continue; |
280 | 3.79k | } |
281 | 7.02k | if (minXY.fX == contour.fMinXY.fX) { |
282 | 3.03k | if (minXY.fY != contour.fMinXY.fY) { |
283 | 605 | continue; |
284 | 605 | } |
285 | 3.03k | } |
286 | 6.42k | contour.fMinXY = minXY; |
287 | 10.3M | } while (SkPath::kDone_Verb != verb); |
288 | 0 | return winding; |
289 | 19.0k | } OpAsWinding::nextEdge(Contour&, OpAsWinding::Edge) Line | Count | Source | 245 | 19.0k | int nextEdge(Contour& contour, Edge edge) { | 246 | 19.0k | SkPath::Iter iter(fPath, true); | 247 | 19.0k | SkPoint pts[4]; | 248 | 19.0k | SkPath::Verb verb; | 249 | 19.0k | int verbCount = -1; | 250 | 19.0k | int winding = 0; | 251 | 10.3M | do { | 252 | 10.3M | verb = iter.next(pts); | 253 | 10.3M | if (++verbCount < contour.fVerbStart) { | 254 | 3.74M | continue; | 255 | 3.74M | } | 256 | 6.57M | if (verbCount >= contour.fVerbEnd) { | 257 | 6.49M | continue; | 258 | 6.49M | } | 259 | 76.7k | if (SkPath::kLine_Verb > verb || verb > SkPath::kCubic_Verb) { | 260 | 33.8k | continue; | 261 | 33.8k | } | 262 | 42.8k | bool horizontal = true; | 263 | 46.6k | for (int index = 1; index <= kPtCount[verb]; ++index) { | 264 | 44.8k | if (pts[0].fY != pts[index].fY) { | 265 | 41.0k | horizontal = false; | 266 | 41.0k | break; | 267 | 41.0k | } | 268 | 44.8k | } | 269 | 42.8k | if (horizontal) { | 270 | 1.80k | continue; | 271 | 1.80k | } | 272 | 41.0k | if (edge == Edge::kCompare) { | 273 | 30.2k | winding += contains_edge(pts, verb, conic_weight(iter, verb), contour.fMinXY); | 274 | 30.2k | continue; | 275 | 30.2k | } | 276 | 10.8k | SkASSERT(edge == Edge::kInitial); | 277 | 10.8k | SkPoint minXY = left_edge(pts, verb, conic_weight(iter, verb)); | 278 | 10.8k | if (minXY.fX > contour.fMinXY.fX) { | 279 | 3.79k | continue; | 280 | 3.79k | } | 281 | 7.02k | if (minXY.fX == contour.fMinXY.fX) { | 282 | 3.03k | if (minXY.fY != contour.fMinXY.fY) { | 283 | 605 | continue; | 284 | 605 | } | 285 | 3.03k | } | 286 | 6.42k | contour.fMinXY = minXY; | 287 | 10.3M | } while (SkPath::kDone_Verb != verb); | 288 | 0 | return winding; | 289 | 19.0k | } |
Unexecuted instantiation: OpAsWinding::nextEdge(Contour&, OpAsWinding::Edge) |
290 | | |
291 | 15.4k | bool containerContains(Contour& contour, Contour& test) { |
292 | | // find outside point on lesser contour |
293 | | // arbitrarily, choose non-horizontal edge where point <= bounds left |
294 | | // note that if leftmost point is control point, may need tight bounds |
295 | | // to find edge with minimum-x |
296 | 15.4k | if (SK_ScalarMax == test.fMinXY.fX) { |
297 | 2.04k | this->nextEdge(test, Edge::kInitial); |
298 | 2.04k | } |
299 | | // find all edges on greater equal or to the left of one on lesser |
300 | 15.4k | contour.fMinXY = test.fMinXY; |
301 | 15.4k | int winding = this->nextEdge(contour, Edge::kCompare); |
302 | | // if edge is up, mark contour cw, otherwise, ccw |
303 | | // sum of greater edges direction should be cw, 0, ccw |
304 | 15.4k | test.fContained = winding != 0; |
305 | 15.4k | return -1 <= winding && winding <= 1; |
306 | 15.4k | } |
307 | | |
308 | 953k | void inParent(Contour& contour, Contour& parent) { |
309 | | // move contour into sibling list contained by parent |
310 | 974k | for (auto test : parent.fChildren) { |
311 | 974k | if (test->fBounds.contains(contour.fBounds)) { |
312 | 936k | inParent(contour, *test); |
313 | 936k | return; |
314 | 936k | } |
315 | 974k | } |
316 | | // move parent's children into contour's children if contained by contour |
317 | 35.4k | for (auto iter = parent.fChildren.begin(); iter != parent.fChildren.end(); ) { |
318 | 18.1k | if (contour.fBounds.contains((*iter)->fBounds)) { |
319 | 10.9k | contour.fChildren.push_back(*iter); |
320 | 10.9k | iter = parent.fChildren.erase(iter); |
321 | 10.9k | continue; |
322 | 10.9k | } |
323 | 7.22k | ++iter; |
324 | 7.22k | } |
325 | 17.3k | parent.fChildren.push_back(&contour); |
326 | 17.3k | } |
327 | | |
328 | 17.1k | bool checkContainerChildren(Contour* parent, Contour* child) { |
329 | 17.1k | for (auto grandChild : child->fChildren) { |
330 | 15.6k | if (!checkContainerChildren(child, grandChild)) { |
331 | 271 | return false; |
332 | 271 | } |
333 | 15.6k | } |
334 | 16.9k | if (parent) { |
335 | 15.4k | if (!containerContains(*parent, *child)) { |
336 | 32 | return false; |
337 | 32 | } |
338 | 15.4k | } |
339 | 16.8k | return true; |
340 | 16.9k | } |
341 | | |
342 | 16.7k | bool markReverse(Contour* parent, Contour* child) { |
343 | 16.7k | bool reversed = false; |
344 | 16.7k | for (auto grandChild : child->fChildren) { |
345 | 15.2k | reversed |= markReverse(grandChild->fContained ? child : parent, grandChild); |
346 | 15.2k | } |
347 | | |
348 | 16.7k | child->fDirection = getDirection(*child); |
349 | 16.7k | if (parent && parent->fDirection == child->fDirection) { |
350 | 2.37k | child->fReverse = true; |
351 | 2.37k | child->fDirection = (Contour::Direction) -(int) child->fDirection; |
352 | 2.37k | return true; |
353 | 2.37k | } |
354 | 14.3k | return reversed; |
355 | 16.7k | } |
356 | | |
357 | 254 | SkPath reverseMarkedContours(vector<Contour>& contours, SkPathFillType fillType) { |
358 | 254 | SkPathPriv::Iterate iterate(fPath); |
359 | 254 | auto iter = iterate.begin(); |
360 | 254 | int verbCount = 0; |
361 | | |
362 | 254 | SkPathBuilder result; |
363 | 254 | result.setFillType(fillType); |
364 | 9.11k | for (const Contour& contour : contours) { |
365 | 9.11k | SkPathBuilder reverse; |
366 | 9.11k | SkPathBuilder* temp = contour.fReverse ? &reverse : &result; |
367 | 42.3k | for (; iter != iterate.end() && verbCount < contour.fVerbEnd; ++iter, ++verbCount) { |
368 | 33.2k | auto [verb, pts, w] = *iter; |
369 | 33.2k | switch (verb) { |
370 | 10.5k | case SkPathVerb::kMove: |
371 | 10.5k | temp->moveTo(pts[0]); |
372 | 10.5k | break; |
373 | 9.83k | case SkPathVerb::kLine: |
374 | 9.83k | temp->lineTo(pts[1]); |
375 | 9.83k | break; |
376 | 1.41k | case SkPathVerb::kQuad: |
377 | 1.41k | temp->quadTo(pts[1], pts[2]); |
378 | 1.41k | break; |
379 | 1.04k | case SkPathVerb::kConic: |
380 | 1.04k | temp->conicTo(pts[1], pts[2], *w); |
381 | 1.04k | break; |
382 | 771 | case SkPathVerb::kCubic: |
383 | 771 | temp->cubicTo(pts[1], pts[2], pts[3]); |
384 | 771 | break; |
385 | 9.66k | case SkPathVerb::kClose: |
386 | 9.66k | temp->close(); |
387 | 9.66k | break; |
388 | 33.2k | } |
389 | 33.2k | } |
390 | 9.11k | if (contour.fReverse) { |
391 | 2.37k | SkASSERT(temp == &reverse); |
392 | 2.37k | SkPathPriv::ReverseAddPath(&result, reverse.detach()); |
393 | 2.37k | } |
394 | 9.11k | } |
395 | 254 | return result.detach(); |
396 | 254 | } OpAsWinding::reverseMarkedContours(std::__1::vector<Contour, std::__1::allocator<Contour> >&, SkPathFillType) Line | Count | Source | 357 | 254 | SkPath reverseMarkedContours(vector<Contour>& contours, SkPathFillType fillType) { | 358 | 254 | SkPathPriv::Iterate iterate(fPath); | 359 | 254 | auto iter = iterate.begin(); | 360 | 254 | int verbCount = 0; | 361 | | | 362 | 254 | SkPathBuilder result; | 363 | 254 | result.setFillType(fillType); | 364 | 9.11k | for (const Contour& contour : contours) { | 365 | 9.11k | SkPathBuilder reverse; | 366 | 9.11k | SkPathBuilder* temp = contour.fReverse ? &reverse : &result; | 367 | 42.3k | for (; iter != iterate.end() && verbCount < contour.fVerbEnd; ++iter, ++verbCount) { | 368 | 33.2k | auto [verb, pts, w] = *iter; | 369 | 33.2k | switch (verb) { | 370 | 10.5k | case SkPathVerb::kMove: | 371 | 10.5k | temp->moveTo(pts[0]); | 372 | 10.5k | break; | 373 | 9.83k | case SkPathVerb::kLine: | 374 | 9.83k | temp->lineTo(pts[1]); | 375 | 9.83k | break; | 376 | 1.41k | case SkPathVerb::kQuad: | 377 | 1.41k | temp->quadTo(pts[1], pts[2]); | 378 | 1.41k | break; | 379 | 1.04k | case SkPathVerb::kConic: | 380 | 1.04k | temp->conicTo(pts[1], pts[2], *w); | 381 | 1.04k | break; | 382 | 771 | case SkPathVerb::kCubic: | 383 | 771 | temp->cubicTo(pts[1], pts[2], pts[3]); | 384 | 771 | break; | 385 | 9.66k | case SkPathVerb::kClose: | 386 | 9.66k | temp->close(); | 387 | 9.66k | break; | 388 | 33.2k | } | 389 | 33.2k | } | 390 | 9.11k | if (contour.fReverse) { | 391 | 2.37k | SkASSERT(temp == &reverse); | 392 | 2.37k | SkPathPriv::ReverseAddPath(&result, reverse.detach()); | 393 | 2.37k | } | 394 | 9.11k | } | 395 | 254 | return result.detach(); | 396 | 254 | } |
Unexecuted instantiation: OpAsWinding::reverseMarkedContours(std::__1::vector<Contour, std::__1::allocator<Contour> >&, SkPathFillType) |
397 | | |
398 | | private: |
399 | | const SkPath& fPath; |
400 | | }; |
401 | | |
402 | 438 | static bool set_result_path(SkPath* result, const SkPath& path, SkPathFillType fillType) { |
403 | 438 | *result = path; |
404 | 438 | result->setFillType(fillType); |
405 | 438 | return true; |
406 | 438 | } |
407 | | |
408 | 726 | bool AsWinding(const SkPath& path, SkPath* result) { |
409 | 726 | if (!path.isFinite()) { |
410 | 2 | return false; |
411 | 2 | } |
412 | 724 | SkPathFillType fillType = path.getFillType(); |
413 | 724 | if (fillType == SkPathFillType::kWinding |
414 | 724 | || fillType == SkPathFillType::kInverseWinding ) { |
415 | 38 | return set_result_path(result, path, fillType); |
416 | 38 | } |
417 | 686 | fillType = path.isInverseFillType() ? SkPathFillType::kInverseWinding : |
418 | 686 | SkPathFillType::kWinding; |
419 | 686 | if (path.isEmpty() || path.isConvex()) { |
420 | 29 | return set_result_path(result, path, fillType); |
421 | 29 | } |
422 | | // count contours |
423 | 657 | vector<Contour> contours; // one per contour |
424 | 657 | OpAsWinding winder(path); |
425 | 657 | winder.contourBounds(&contours); |
426 | 657 | if (contours.size() <= 1) { |
427 | 19 | return set_result_path(result, path, fillType); |
428 | 19 | } |
429 | | // create contour bounding box tree |
430 | 638 | Contour sorted(SkRect(), 0, 0); |
431 | 17.3k | for (auto& contour : contours) { |
432 | 17.3k | winder.inParent(contour, sorted); |
433 | 17.3k | } |
434 | | // if sorted has no grandchildren, no child has to fix its children's winding |
435 | 638 | if (std::all_of(sorted.fChildren.begin(), sorted.fChildren.end(), |
436 | 841 | [](const Contour* contour) -> bool { return contour->fChildren.empty(); } )) { |
437 | 14 | return set_result_path(result, path, fillType); |
438 | 14 | } |
439 | | // starting with outermost and moving inward, see if one path contains another |
440 | 1.51k | for (auto contour : sorted.fChildren) { |
441 | 1.51k | winder.nextEdge(*contour, OpAsWinding::Edge::kInitial); |
442 | 1.51k | contour->fDirection = winder.getDirection(*contour); |
443 | 1.51k | if (!winder.checkContainerChildren(nullptr, contour)) { |
444 | 32 | return false; |
445 | 32 | } |
446 | 1.51k | } |
447 | | // starting with outermost and moving inward, mark paths to reverse |
448 | 592 | bool reversed = false; |
449 | 1.47k | for (auto contour : sorted.fChildren) { |
450 | 1.47k | reversed |= winder.markReverse(nullptr, contour); |
451 | 1.47k | } |
452 | 592 | if (!reversed) { |
453 | 338 | return set_result_path(result, path, fillType); |
454 | 338 | } |
455 | 254 | *result = winder.reverseMarkedContours(contours, fillType); |
456 | 254 | return true; |
457 | 592 | } |