blob: 9dac160495ef0738c907537bee0bc292814dc6dc [file] [log] [blame]
caryclark45fa4472015-01-16 07:04:10 -08001/*
2 * Copyright 2014 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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkMatrix.h"
9#include "include/pathops/SkPathOps.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040010#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "src/core/SkPathPriv.h"
12#include "src/pathops/SkOpEdgeBuilder.h"
13#include "src/pathops/SkPathOpsCommon.h"
caryclark5b5ddd72015-05-18 05:12:56 -070014
15static bool one_contour(const SkPath& path) {
Florin Malita14a64302017-05-24 14:53:44 -040016 SkSTArenaAlloc<256> allocator;
caryclark5b5ddd72015-05-18 05:12:56 -070017 int verbCount = path.countVerbs();
Herb Derbyc3cc5fa2017-03-07 11:11:47 -050018 uint8_t* verbs = (uint8_t*) allocator.makeArrayDefault<uint8_t>(verbCount);
caryclark5b5ddd72015-05-18 05:12:56 -070019 (void) path.getVerbs(verbs, verbCount);
20 for (int index = 1; index < verbCount; ++index) {
21 if (verbs[index] == SkPath::kMove_Verb) {
22 return false;
23 }
24 }
25 return true;
26}
27
caryclark51c56782016-11-07 05:09:28 -080028void SkOpBuilder::ReversePath(SkPath* path) {
29 SkPath temp;
30 SkPoint lastPt;
31 SkAssertResult(path->getLastPt(&lastPt));
32 temp.moveTo(lastPt);
33 temp.reversePathTo(*path);
34 temp.close();
35 *path = temp;
36}
37
38bool SkOpBuilder::FixWinding(SkPath* path) {
Mike Reed4241f5e2019-09-14 19:13:23 +000039 SkPath::FillType fillType = path->getFillType();
40 if (fillType == SkPath::kInverseEvenOdd_FillType) {
41 fillType = SkPath::kInverseWinding_FillType;
42 } else if (fillType == SkPath::kEvenOdd_FillType) {
43 fillType = SkPath::kWinding_FillType;
caryclark5b5ddd72015-05-18 05:12:56 -070044 }
reed026beb52015-06-10 14:23:15 -070045 SkPathPriv::FirstDirection dir;
46 if (one_contour(*path) && SkPathPriv::CheapComputeFirstDirection(*path, &dir)) {
47 if (dir != SkPathPriv::kCCW_FirstDirection) {
caryclark51c56782016-11-07 05:09:28 -080048 ReversePath(path);
caryclark5b5ddd72015-05-18 05:12:56 -070049 }
50 path->setFillType(fillType);
caryclarkdae6b972016-06-08 04:28:19 -070051 return true;
caryclark5b5ddd72015-05-18 05:12:56 -070052 }
Florin Malita14a64302017-05-24 14:53:44 -040053 SkSTArenaAlloc<4096> allocator;
caryclark5b5ddd72015-05-18 05:12:56 -070054 SkOpContourHead contourHead;
caryclark55888e42016-07-18 10:01:36 -070055 SkOpGlobalState globalState(&contourHead, &allocator SkDEBUGPARAMS(false)
caryclarkdae6b972016-06-08 04:28:19 -070056 SkDEBUGPARAMS(nullptr));
caryclark55888e42016-07-18 10:01:36 -070057 SkOpEdgeBuilder builder(*path, &contourHead, &globalState);
caryclark7b33bf12016-07-21 08:53:32 -070058 if (builder.unparseable() || !builder.finish()) {
59 return false;
60 }
61 if (!contourHead.count()) {
62 return true;
63 }
caryclarke3a4e992016-09-28 09:22:17 -070064 if (!contourHead.next()) {
65 return false;
66 }
caryclarkeed356d2016-09-14 07:18:20 -070067 contourHead.joinAllSegments();
caryclark5b5ddd72015-05-18 05:12:56 -070068 contourHead.resetReverse();
69 bool writePath = false;
70 SkOpSpan* topSpan;
Cary Clarkab87d7a2016-10-04 10:01:04 -040071 globalState.setPhase(SkOpPhase::kFixWinding);
caryclark5b5ddd72015-05-18 05:12:56 -070072 while ((topSpan = FindSortableTop(&contourHead))) {
73 SkOpSegment* topSegment = topSpan->segment();
74 SkOpContour* topContour = topSegment->contour();
caryclark5b5ddd72015-05-18 05:12:56 -070075 SkASSERT(topContour->isCcw() >= 0);
caryclark4e1a4c92015-05-18 12:56:57 -070076#if DEBUG_WINDING
77 SkDebugf("%s id=%d nested=%d ccw=%d\n", __FUNCTION__,
78 topSegment->debugID(), globalState.nested(), topContour->isCcw());
79#endif
80 if ((globalState.nested() & 1) != SkToBool(topContour->isCcw())) {
caryclark5b5ddd72015-05-18 05:12:56 -070081 topContour->setReverse();
82 writePath = true;
83 }
caryclark55888e42016-07-18 10:01:36 -070084 topContour->markAllDone();
caryclark4e1a4c92015-05-18 12:56:57 -070085 globalState.clearNested();
caryclark5b5ddd72015-05-18 05:12:56 -070086 }
87 if (!writePath) {
88 path->setFillType(fillType);
caryclarkdae6b972016-06-08 04:28:19 -070089 return true;
caryclark5b5ddd72015-05-18 05:12:56 -070090 }
91 SkPath empty;
92 SkPathWriter woundPath(empty);
93 SkOpContour* test = &contourHead;
94 do {
Cary Clarkfa9193d2016-12-21 08:25:00 -050095 if (!test->count()) {
96 continue;
97 }
caryclark5b5ddd72015-05-18 05:12:56 -070098 if (test->reversed()) {
99 test->toReversePath(&woundPath);
100 } else {
101 test->toPath(&woundPath);
102 }
103 } while ((test = test->next()));
104 *path = *woundPath.nativePath();
105 path->setFillType(fillType);
caryclarkdae6b972016-06-08 04:28:19 -0700106 return true;
caryclark5b5ddd72015-05-18 05:12:56 -0700107}
caryclark45fa4472015-01-16 07:04:10 -0800108
109void SkOpBuilder::add(const SkPath& path, SkPathOp op) {
caryclark54359292015-03-26 07:52:43 -0700110 if (0 == fOps.count() && op != kUnion_SkPathOp) {
caryclark45fa4472015-01-16 07:04:10 -0800111 fPathRefs.push_back() = SkPath();
caryclark54359292015-03-26 07:52:43 -0700112 *fOps.append() = kUnion_SkPathOp;
caryclark45fa4472015-01-16 07:04:10 -0800113 }
114 fPathRefs.push_back() = path;
115 *fOps.append() = op;
116}
117
118void SkOpBuilder::reset() {
119 fPathRefs.reset();
120 fOps.reset();
121}
122
123/* OPTIMIZATION: Union doesn't need to be all-or-nothing. A run of three or more convex
124 paths with union ops could be locally resolved and still improve over doing the
125 ops one at a time. */
126bool SkOpBuilder::resolve(SkPath* result) {
caryclark1049f122015-04-20 08:31:59 -0700127 SkPath original = *result;
caryclark45fa4472015-01-16 07:04:10 -0800128 int count = fOps.count();
129 bool allUnion = true;
caryclarkd83f4952015-06-17 08:46:12 -0700130 SkPathPriv::FirstDirection firstDir = SkPathPriv::kUnknown_FirstDirection;
caryclark45fa4472015-01-16 07:04:10 -0800131 for (int index = 0; index < count; ++index) {
132 SkPath* test = &fPathRefs[index];
caryclark54359292015-03-26 07:52:43 -0700133 if (kUnion_SkPathOp != fOps[index] || test->isInverseFillType()) {
caryclark45fa4472015-01-16 07:04:10 -0800134 allUnion = false;
135 break;
136 }
137 // If all paths are convex, track direction, reversing as needed.
138 if (test->isConvex()) {
reed026beb52015-06-10 14:23:15 -0700139 SkPathPriv::FirstDirection dir;
140 if (!SkPathPriv::CheapComputeFirstDirection(*test, &dir)) {
caryclark45fa4472015-01-16 07:04:10 -0800141 allUnion = false;
142 break;
143 }
caryclarkd83f4952015-06-17 08:46:12 -0700144 if (firstDir == SkPathPriv::kUnknown_FirstDirection) {
caryclark45fa4472015-01-16 07:04:10 -0800145 firstDir = dir;
146 } else if (firstDir != dir) {
caryclark51c56782016-11-07 05:09:28 -0800147 ReversePath(test);
caryclark45fa4472015-01-16 07:04:10 -0800148 }
149 continue;
150 }
151 // If the path is not convex but its bounds do not intersect the others, simplify is enough.
152 const SkRect& testBounds = test->getBounds();
153 for (int inner = 0; inner < index; ++inner) {
154 // OPTIMIZE: check to see if the contour bounds do not intersect other contour bounds?
155 if (SkRect::Intersects(fPathRefs[inner].getBounds(), testBounds)) {
156 allUnion = false;
157 break;
158 }
159 }
160 }
161 if (!allUnion) {
162 *result = fPathRefs[0];
163 for (int index = 1; index < count; ++index) {
164 if (!Op(*result, fPathRefs[index], fOps[index], result)) {
165 reset();
caryclark1049f122015-04-20 08:31:59 -0700166 *result = original;
caryclark45fa4472015-01-16 07:04:10 -0800167 return false;
168 }
169 }
170 reset();
171 return true;
172 }
173 SkPath sum;
174 for (int index = 0; index < count; ++index) {
175 if (!Simplify(fPathRefs[index], &fPathRefs[index])) {
176 reset();
caryclark1049f122015-04-20 08:31:59 -0700177 *result = original;
caryclark45fa4472015-01-16 07:04:10 -0800178 return false;
179 }
caryclark218f21a2015-06-29 11:41:52 -0700180 if (!fPathRefs[index].isEmpty()) {
181 // convert the even odd result back to winding form before accumulating it
caryclarkdae6b972016-06-08 04:28:19 -0700182 if (!FixWinding(&fPathRefs[index])) {
183 *result = original;
184 return false;
185 }
caryclark218f21a2015-06-29 11:41:52 -0700186 sum.addPath(fPathRefs[index]);
187 }
caryclark45fa4472015-01-16 07:04:10 -0800188 }
189 reset();
caryclark1049f122015-04-20 08:31:59 -0700190 bool success = Simplify(sum, result);
191 if (!success) {
192 *result = original;
193 }
194 return success;
caryclark45fa4472015-01-16 07:04:10 -0800195}