Flutter Engine
The Flutter Engine
SkOpBuilder.cpp
Go to the documentation of this file.
1/*
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
11#include "include/core/SkRect.h"
19#include "src/core/SkPathPriv.h"
27
28#include <cstdint>
29
30static bool one_contour(const SkPath& path) {
31 SkSTArenaAlloc<256> allocator;
32 int verbCount = path.countVerbs();
33 uint8_t* verbs = (uint8_t*) allocator.makeArrayDefault<uint8_t>(verbCount);
34 (void) path.getVerbs(verbs, verbCount);
35 for (int index = 1; index < verbCount; ++index) {
36 if (verbs[index] == SkPath::kMove_Verb) {
37 return false;
38 }
39 }
40 return true;
41}
42
43void SkOpBuilder::ReversePath(SkPath* path) {
44 SkPath temp;
45 SkPoint lastPt;
46 SkAssertResult(path->getLastPt(&lastPt));
47 temp.moveTo(lastPt);
48 temp.reversePathTo(*path);
49 temp.close();
50 *path = temp;
51}
52
53bool SkOpBuilder::FixWinding(SkPath* path) {
54 SkPathFillType fillType = path->getFillType();
55 if (fillType == SkPathFillType::kInverseEvenOdd) {
57 } else if (fillType == SkPathFillType::kEvenOdd) {
58 fillType = SkPathFillType::kWinding;
59 }
60 if (one_contour(*path)) {
64 ReversePath(path);
65 }
66 path->setFillType(fillType);
67 return true;
68 }
69 }
70 SkSTArenaAlloc<4096> allocator;
71 SkOpContourHead contourHead;
72 SkOpGlobalState globalState(&contourHead, &allocator SkDEBUGPARAMS(false)
73 SkDEBUGPARAMS(nullptr));
74 SkOpEdgeBuilder builder(*path, &contourHead, &globalState);
75 if (builder.unparseable() || !builder.finish()) {
76 return false;
77 }
78 if (!contourHead.count()) {
79 return true;
80 }
81 if (!contourHead.next()) {
82 return false;
83 }
84 contourHead.joinAllSegments();
85 contourHead.resetReverse();
86 bool writePath = false;
87 SkOpSpan* topSpan;
88 globalState.setPhase(SkOpPhase::kFixWinding);
89 while ((topSpan = FindSortableTop(&contourHead))) {
90 SkOpSegment* topSegment = topSpan->segment();
91 SkOpContour* topContour = topSegment->contour();
92 SkASSERT(topContour->isCcw() >= 0);
93#if DEBUG_WINDING
94 SkDebugf("%s id=%d nested=%d ccw=%d\n", __FUNCTION__,
95 topSegment->debugID(), globalState.nested(), topContour->isCcw());
96#endif
97 if ((globalState.nested() & 1) != SkToBool(topContour->isCcw())) {
98 topContour->setReverse();
99 writePath = true;
100 }
101 topContour->markAllDone();
102 globalState.clearNested();
103 }
104 if (!writePath) {
105 path->setFillType(fillType);
106 return true;
107 }
109 SkPathWriter woundPath(empty);
110 SkOpContour* test = &contourHead;
111 do {
112 if (!test->count()) {
113 continue;
114 }
115 if (test->reversed()) {
116 test->toReversePath(&woundPath);
117 } else {
118 test->toPath(&woundPath);
119 }
120 } while ((test = test->next()));
121 *path = *woundPath.nativePath();
122 path->setFillType(fillType);
123 return true;
124}
125
127 if (fOps.empty() && op != kUnion_SkPathOp) {
128 fPathRefs.push_back() = SkPath();
129 *fOps.append() = kUnion_SkPathOp;
130 }
131 fPathRefs.push_back() = path;
132 *fOps.append() = op;
133}
134
135void SkOpBuilder::reset() {
136 fPathRefs.clear();
137 fOps.reset();
138}
139
140/* OPTIMIZATION: Union doesn't need to be all-or-nothing. A run of three or more convex
141 paths with union ops could be locally resolved and still improve over doing the
142 ops one at a time. */
144 SkPath original = *result;
145 int count = fOps.size();
146 bool allUnion = true;
148 for (int index = 0; index < count; ++index) {
149 SkPath* test = &fPathRefs[index];
150 if (kUnion_SkPathOp != fOps[index] || test->isInverseFillType()) {
151 allUnion = false;
152 break;
153 }
154 // If all paths are convex, track direction, reversing as needed.
155 if (test->isConvex()) {
158 allUnion = false;
159 break;
160 }
161 if (firstDir == SkPathFirstDirection::kUnknown) {
162 firstDir = dir;
163 } else if (firstDir != dir) {
164 ReversePath(test);
165 }
166 continue;
167 }
168 // If the path is not convex but its bounds do not intersect the others, simplify is enough.
169 const SkRect& testBounds = test->getBounds();
170 for (int inner = 0; inner < index; ++inner) {
171 // OPTIMIZE: check to see if the contour bounds do not intersect other contour bounds?
172 if (SkRect::Intersects(fPathRefs[inner].getBounds(), testBounds)) {
173 allUnion = false;
174 break;
175 }
176 }
177 }
178 if (!allUnion) {
179 *result = fPathRefs[0];
180 for (int index = 1; index < count; ++index) {
181 if (!Op(*result, fPathRefs[index], fOps[index], result)) {
182 reset();
183 *result = original;
184 return false;
185 }
186 }
187 reset();
188 return true;
189 }
190 SkPath sum;
191 for (int index = 0; index < count; ++index) {
192 if (!Simplify(fPathRefs[index], &fPathRefs[index])) {
193 reset();
194 *result = original;
195 return false;
196 }
197 if (!fPathRefs[index].isEmpty()) {
198 // convert the even odd result back to winding form before accumulating it
199 if (!FixWinding(&fPathRefs[index])) {
200 *result = original;
201 return false;
202 }
203 sum.addPath(fPathRefs[index]);
204 }
205 }
206 reset();
207 bool success = Simplify(sum, result);
208 if (!success) {
209 *result = original;
210 }
211 return success;
212}
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
int count
Definition: FontMgrTest.cpp:50
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static bool one_contour(const SkPath &path)
Definition: SkOpBuilder.cpp:30
SkPathFirstDirection
Definition: SkPathEnums.h:19
SkOpSpan * FindSortableTop(SkOpContourHead *)
#define SkDEBUGPARAMS(...)
SkPathOp
Definition: SkPathOps.h:22
@ kUnion_SkPathOp
union (inclusive-or) the two paths
Definition: SkPathOps.h:25
bool SK_API Simplify(const SkPath &path, SkPath *result)
SkPathFillType
Definition: SkPathTypes.h:11
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
T * makeArrayDefault(size_t count)
Definition: SkArenaAlloc.h:171
void add(const SkPath &path, SkPathOp _operator)
bool resolve(SkPath *result)
void joinAllSegments()
Definition: SkOpContour.h:414
int count() const
Definition: SkOpContour.h:84
void setReverse()
Definition: SkOpContour.h:343
int isCcw() const
Definition: SkOpContour.h:206
void markAllDone()
Definition: SkOpContour.h:223
SkOpContour * next()
Definition: SkOpContour.h:266
void resetReverse()
Definition: SkOpContour.h:298
int debugID() const
Definition: SkOpSegment.h:154
SkOpContour * contour() const
Definition: SkOpSegment.h:130
SkOpSegment * segment() const
Definition: SkOpSpan.h:318
static SkPathFirstDirection ComputeFirstDirection(const SkPath &)
Definition: SkPath.cpp:2627
Definition: SkPath.h:59
SkPath & moveTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:688
SkPath & addPath(const SkPath &src, SkScalar dx, SkScalar dy, AddPathMode mode=kAppend_AddPathMode)
Definition: SkPath.cpp:1506
@ kMove_Verb
Definition: SkPath.h:1466
SkPath & close()
Definition: SkPath.cpp:823
int size() const
Definition: SkTDArray.h:138
bool empty() const
Definition: SkTDArray.h:135
void reset()
Definition: SkTDArray.h:171
T * append()
Definition: SkTDArray.h:191
EMSCRIPTEN_KEEPALIVE void empty()
GAsyncResult * result
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition: switches.h:145