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