1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2014 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathTypes.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/pathops/SkPathOps.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTDArray.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkArenaAlloc.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPathEnums.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPathPriv.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkOpContour.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkOpEdgeBuilder.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkOpSegment.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkOpSpan.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkPathOpsCommon.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkPathOpsTypes.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/pathops/SkPathWriter.h"
27*c8dee2aaSAndroid Build Coastguard Worker
28*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
29*c8dee2aaSAndroid Build Coastguard Worker
one_contour(const SkPath & path)30*c8dee2aaSAndroid Build Coastguard Worker static bool one_contour(const SkPath& path) {
31*c8dee2aaSAndroid Build Coastguard Worker SkSTArenaAlloc<256> allocator;
32*c8dee2aaSAndroid Build Coastguard Worker int verbCount = path.countVerbs();
33*c8dee2aaSAndroid Build Coastguard Worker uint8_t* verbs = (uint8_t*) allocator.makeArrayDefault<uint8_t>(verbCount);
34*c8dee2aaSAndroid Build Coastguard Worker (void) path.getVerbs(verbs, verbCount);
35*c8dee2aaSAndroid Build Coastguard Worker for (int index = 1; index < verbCount; ++index) {
36*c8dee2aaSAndroid Build Coastguard Worker if (verbs[index] == SkPath::kMove_Verb) {
37*c8dee2aaSAndroid Build Coastguard Worker return false;
38*c8dee2aaSAndroid Build Coastguard Worker }
39*c8dee2aaSAndroid Build Coastguard Worker }
40*c8dee2aaSAndroid Build Coastguard Worker return true;
41*c8dee2aaSAndroid Build Coastguard Worker }
42*c8dee2aaSAndroid Build Coastguard Worker
ReversePath(SkPath * path)43*c8dee2aaSAndroid Build Coastguard Worker void SkOpBuilder::ReversePath(SkPath* path) {
44*c8dee2aaSAndroid Build Coastguard Worker SkPath temp;
45*c8dee2aaSAndroid Build Coastguard Worker SkPoint lastPt;
46*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(path->getLastPt(&lastPt));
47*c8dee2aaSAndroid Build Coastguard Worker temp.moveTo(lastPt);
48*c8dee2aaSAndroid Build Coastguard Worker temp.reversePathTo(*path);
49*c8dee2aaSAndroid Build Coastguard Worker temp.close();
50*c8dee2aaSAndroid Build Coastguard Worker *path = temp;
51*c8dee2aaSAndroid Build Coastguard Worker }
52*c8dee2aaSAndroid Build Coastguard Worker
FixWinding(SkPath * path)53*c8dee2aaSAndroid Build Coastguard Worker bool SkOpBuilder::FixWinding(SkPath* path) {
54*c8dee2aaSAndroid Build Coastguard Worker SkPathFillType fillType = path->getFillType();
55*c8dee2aaSAndroid Build Coastguard Worker if (fillType == SkPathFillType::kInverseEvenOdd) {
56*c8dee2aaSAndroid Build Coastguard Worker fillType = SkPathFillType::kInverseWinding;
57*c8dee2aaSAndroid Build Coastguard Worker } else if (fillType == SkPathFillType::kEvenOdd) {
58*c8dee2aaSAndroid Build Coastguard Worker fillType = SkPathFillType::kWinding;
59*c8dee2aaSAndroid Build Coastguard Worker }
60*c8dee2aaSAndroid Build Coastguard Worker if (one_contour(*path)) {
61*c8dee2aaSAndroid Build Coastguard Worker SkPathFirstDirection dir = SkPathPriv::ComputeFirstDirection(*path);
62*c8dee2aaSAndroid Build Coastguard Worker if (dir != SkPathFirstDirection::kUnknown) {
63*c8dee2aaSAndroid Build Coastguard Worker if (dir == SkPathFirstDirection::kCW) {
64*c8dee2aaSAndroid Build Coastguard Worker ReversePath(path);
65*c8dee2aaSAndroid Build Coastguard Worker }
66*c8dee2aaSAndroid Build Coastguard Worker path->setFillType(fillType);
67*c8dee2aaSAndroid Build Coastguard Worker return true;
68*c8dee2aaSAndroid Build Coastguard Worker }
69*c8dee2aaSAndroid Build Coastguard Worker }
70*c8dee2aaSAndroid Build Coastguard Worker SkSTArenaAlloc<4096> allocator;
71*c8dee2aaSAndroid Build Coastguard Worker SkOpContourHead contourHead;
72*c8dee2aaSAndroid Build Coastguard Worker SkOpGlobalState globalState(&contourHead, &allocator SkDEBUGPARAMS(false)
73*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGPARAMS(nullptr));
74*c8dee2aaSAndroid Build Coastguard Worker SkOpEdgeBuilder builder(*path, &contourHead, &globalState);
75*c8dee2aaSAndroid Build Coastguard Worker if (builder.unparseable() || !builder.finish()) {
76*c8dee2aaSAndroid Build Coastguard Worker return false;
77*c8dee2aaSAndroid Build Coastguard Worker }
78*c8dee2aaSAndroid Build Coastguard Worker if (!contourHead.count()) {
79*c8dee2aaSAndroid Build Coastguard Worker return true;
80*c8dee2aaSAndroid Build Coastguard Worker }
81*c8dee2aaSAndroid Build Coastguard Worker if (!contourHead.next()) {
82*c8dee2aaSAndroid Build Coastguard Worker return false;
83*c8dee2aaSAndroid Build Coastguard Worker }
84*c8dee2aaSAndroid Build Coastguard Worker contourHead.joinAllSegments();
85*c8dee2aaSAndroid Build Coastguard Worker contourHead.resetReverse();
86*c8dee2aaSAndroid Build Coastguard Worker bool writePath = false;
87*c8dee2aaSAndroid Build Coastguard Worker SkOpSpan* topSpan;
88*c8dee2aaSAndroid Build Coastguard Worker globalState.setPhase(SkOpPhase::kFixWinding);
89*c8dee2aaSAndroid Build Coastguard Worker while ((topSpan = FindSortableTop(&contourHead))) {
90*c8dee2aaSAndroid Build Coastguard Worker SkOpSegment* topSegment = topSpan->segment();
91*c8dee2aaSAndroid Build Coastguard Worker SkOpContour* topContour = topSegment->contour();
92*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(topContour->isCcw() >= 0);
93*c8dee2aaSAndroid Build Coastguard Worker #if DEBUG_WINDING
94*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("%s id=%d nested=%d ccw=%d\n", __FUNCTION__,
95*c8dee2aaSAndroid Build Coastguard Worker topSegment->debugID(), globalState.nested(), topContour->isCcw());
96*c8dee2aaSAndroid Build Coastguard Worker #endif
97*c8dee2aaSAndroid Build Coastguard Worker if ((globalState.nested() & 1) != SkToBool(topContour->isCcw())) {
98*c8dee2aaSAndroid Build Coastguard Worker topContour->setReverse();
99*c8dee2aaSAndroid Build Coastguard Worker writePath = true;
100*c8dee2aaSAndroid Build Coastguard Worker }
101*c8dee2aaSAndroid Build Coastguard Worker topContour->markAllDone();
102*c8dee2aaSAndroid Build Coastguard Worker globalState.clearNested();
103*c8dee2aaSAndroid Build Coastguard Worker }
104*c8dee2aaSAndroid Build Coastguard Worker if (!writePath) {
105*c8dee2aaSAndroid Build Coastguard Worker path->setFillType(fillType);
106*c8dee2aaSAndroid Build Coastguard Worker return true;
107*c8dee2aaSAndroid Build Coastguard Worker }
108*c8dee2aaSAndroid Build Coastguard Worker SkPath empty;
109*c8dee2aaSAndroid Build Coastguard Worker SkPathWriter woundPath(empty);
110*c8dee2aaSAndroid Build Coastguard Worker SkOpContour* test = &contourHead;
111*c8dee2aaSAndroid Build Coastguard Worker do {
112*c8dee2aaSAndroid Build Coastguard Worker if (!test->count()) {
113*c8dee2aaSAndroid Build Coastguard Worker continue;
114*c8dee2aaSAndroid Build Coastguard Worker }
115*c8dee2aaSAndroid Build Coastguard Worker if (test->reversed()) {
116*c8dee2aaSAndroid Build Coastguard Worker test->toReversePath(&woundPath);
117*c8dee2aaSAndroid Build Coastguard Worker } else {
118*c8dee2aaSAndroid Build Coastguard Worker test->toPath(&woundPath);
119*c8dee2aaSAndroid Build Coastguard Worker }
120*c8dee2aaSAndroid Build Coastguard Worker } while ((test = test->next()));
121*c8dee2aaSAndroid Build Coastguard Worker *path = *woundPath.nativePath();
122*c8dee2aaSAndroid Build Coastguard Worker path->setFillType(fillType);
123*c8dee2aaSAndroid Build Coastguard Worker return true;
124*c8dee2aaSAndroid Build Coastguard Worker }
125*c8dee2aaSAndroid Build Coastguard Worker
add(const SkPath & path,SkPathOp op)126*c8dee2aaSAndroid Build Coastguard Worker void SkOpBuilder::add(const SkPath& path, SkPathOp op) {
127*c8dee2aaSAndroid Build Coastguard Worker if (fOps.empty() && op != kUnion_SkPathOp) {
128*c8dee2aaSAndroid Build Coastguard Worker fPathRefs.push_back() = SkPath();
129*c8dee2aaSAndroid Build Coastguard Worker *fOps.append() = kUnion_SkPathOp;
130*c8dee2aaSAndroid Build Coastguard Worker }
131*c8dee2aaSAndroid Build Coastguard Worker fPathRefs.push_back() = path;
132*c8dee2aaSAndroid Build Coastguard Worker *fOps.append() = op;
133*c8dee2aaSAndroid Build Coastguard Worker }
134*c8dee2aaSAndroid Build Coastguard Worker
reset()135*c8dee2aaSAndroid Build Coastguard Worker void SkOpBuilder::reset() {
136*c8dee2aaSAndroid Build Coastguard Worker fPathRefs.clear();
137*c8dee2aaSAndroid Build Coastguard Worker fOps.reset();
138*c8dee2aaSAndroid Build Coastguard Worker }
139*c8dee2aaSAndroid Build Coastguard Worker
140*c8dee2aaSAndroid Build Coastguard Worker /* OPTIMIZATION: Union doesn't need to be all-or-nothing. A run of three or more convex
141*c8dee2aaSAndroid Build Coastguard Worker paths with union ops could be locally resolved and still improve over doing the
142*c8dee2aaSAndroid Build Coastguard Worker ops one at a time. */
resolve(SkPath * result)143*c8dee2aaSAndroid Build Coastguard Worker bool SkOpBuilder::resolve(SkPath* result) {
144*c8dee2aaSAndroid Build Coastguard Worker SkPath original = *result;
145*c8dee2aaSAndroid Build Coastguard Worker int count = fOps.size();
146*c8dee2aaSAndroid Build Coastguard Worker bool allUnion = true;
147*c8dee2aaSAndroid Build Coastguard Worker SkPathFirstDirection firstDir = SkPathFirstDirection::kUnknown;
148*c8dee2aaSAndroid Build Coastguard Worker for (int index = 0; index < count; ++index) {
149*c8dee2aaSAndroid Build Coastguard Worker SkPath* test = &fPathRefs[index];
150*c8dee2aaSAndroid Build Coastguard Worker if (kUnion_SkPathOp != fOps[index] || test->isInverseFillType()) {
151*c8dee2aaSAndroid Build Coastguard Worker allUnion = false;
152*c8dee2aaSAndroid Build Coastguard Worker break;
153*c8dee2aaSAndroid Build Coastguard Worker }
154*c8dee2aaSAndroid Build Coastguard Worker // If all paths are convex, track direction, reversing as needed.
155*c8dee2aaSAndroid Build Coastguard Worker if (test->isConvex()) {
156*c8dee2aaSAndroid Build Coastguard Worker SkPathFirstDirection dir = SkPathPriv::ComputeFirstDirection(*test);
157*c8dee2aaSAndroid Build Coastguard Worker if (dir == SkPathFirstDirection::kUnknown) {
158*c8dee2aaSAndroid Build Coastguard Worker allUnion = false;
159*c8dee2aaSAndroid Build Coastguard Worker break;
160*c8dee2aaSAndroid Build Coastguard Worker }
161*c8dee2aaSAndroid Build Coastguard Worker if (firstDir == SkPathFirstDirection::kUnknown) {
162*c8dee2aaSAndroid Build Coastguard Worker firstDir = dir;
163*c8dee2aaSAndroid Build Coastguard Worker } else if (firstDir != dir) {
164*c8dee2aaSAndroid Build Coastguard Worker ReversePath(test);
165*c8dee2aaSAndroid Build Coastguard Worker }
166*c8dee2aaSAndroid Build Coastguard Worker continue;
167*c8dee2aaSAndroid Build Coastguard Worker }
168*c8dee2aaSAndroid Build Coastguard Worker // If the path is not convex but its bounds do not intersect the others, simplify is enough.
169*c8dee2aaSAndroid Build Coastguard Worker const SkRect& testBounds = test->getBounds();
170*c8dee2aaSAndroid Build Coastguard Worker for (int inner = 0; inner < index; ++inner) {
171*c8dee2aaSAndroid Build Coastguard Worker // OPTIMIZE: check to see if the contour bounds do not intersect other contour bounds?
172*c8dee2aaSAndroid Build Coastguard Worker if (SkRect::Intersects(fPathRefs[inner].getBounds(), testBounds)) {
173*c8dee2aaSAndroid Build Coastguard Worker allUnion = false;
174*c8dee2aaSAndroid Build Coastguard Worker break;
175*c8dee2aaSAndroid Build Coastguard Worker }
176*c8dee2aaSAndroid Build Coastguard Worker }
177*c8dee2aaSAndroid Build Coastguard Worker }
178*c8dee2aaSAndroid Build Coastguard Worker if (!allUnion) {
179*c8dee2aaSAndroid Build Coastguard Worker *result = fPathRefs[0];
180*c8dee2aaSAndroid Build Coastguard Worker for (int index = 1; index < count; ++index) {
181*c8dee2aaSAndroid Build Coastguard Worker if (!Op(*result, fPathRefs[index], fOps[index], result)) {
182*c8dee2aaSAndroid Build Coastguard Worker reset();
183*c8dee2aaSAndroid Build Coastguard Worker *result = original;
184*c8dee2aaSAndroid Build Coastguard Worker return false;
185*c8dee2aaSAndroid Build Coastguard Worker }
186*c8dee2aaSAndroid Build Coastguard Worker }
187*c8dee2aaSAndroid Build Coastguard Worker reset();
188*c8dee2aaSAndroid Build Coastguard Worker return true;
189*c8dee2aaSAndroid Build Coastguard Worker }
190*c8dee2aaSAndroid Build Coastguard Worker SkPath sum;
191*c8dee2aaSAndroid Build Coastguard Worker for (int index = 0; index < count; ++index) {
192*c8dee2aaSAndroid Build Coastguard Worker if (!Simplify(fPathRefs[index], &fPathRefs[index])) {
193*c8dee2aaSAndroid Build Coastguard Worker reset();
194*c8dee2aaSAndroid Build Coastguard Worker *result = original;
195*c8dee2aaSAndroid Build Coastguard Worker return false;
196*c8dee2aaSAndroid Build Coastguard Worker }
197*c8dee2aaSAndroid Build Coastguard Worker if (!fPathRefs[index].isEmpty()) {
198*c8dee2aaSAndroid Build Coastguard Worker // convert the even odd result back to winding form before accumulating it
199*c8dee2aaSAndroid Build Coastguard Worker if (!FixWinding(&fPathRefs[index])) {
200*c8dee2aaSAndroid Build Coastguard Worker *result = original;
201*c8dee2aaSAndroid Build Coastguard Worker return false;
202*c8dee2aaSAndroid Build Coastguard Worker }
203*c8dee2aaSAndroid Build Coastguard Worker sum.addPath(fPathRefs[index]);
204*c8dee2aaSAndroid Build Coastguard Worker }
205*c8dee2aaSAndroid Build Coastguard Worker }
206*c8dee2aaSAndroid Build Coastguard Worker reset();
207*c8dee2aaSAndroid Build Coastguard Worker bool success = Simplify(sum, result);
208*c8dee2aaSAndroid Build Coastguard Worker if (!success) {
209*c8dee2aaSAndroid Build Coastguard Worker *result = original;
210*c8dee2aaSAndroid Build Coastguard Worker }
211*c8dee2aaSAndroid Build Coastguard Worker return success;
212*c8dee2aaSAndroid Build Coastguard Worker }
213