1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#pragma once
#include "include/core/SkPathBuilder.h"
namespace pk {
static_assert(0 == static_cast<int>(SkPathFillType::kWinding), "fill_type_mismatch");
static_assert(1 == static_cast<int>(SkPathFillType::kEvenOdd), "fill_type_mismatch");
static_assert(2 == static_cast<int>(SkPathFillType::kInverseWinding), "fill_type_mismatch");
static_assert(3 == static_cast<int>(SkPathFillType::kInverseEvenOdd), "fill_type_mismatch");
class SkPathPriv {
public:
#ifdef PK_BUILD_FOR_ANDROID_FRAMEWORK
static const int kPathRefGenIDBitCnt = 30; // leave room for the fill type (skbug.com/1762)
#else
static const int kPathRefGenIDBitCnt = 32;
#endif
/**
* Return the opposite of the specified direction. kUnknown is its own
* opposite.
*/
static SkPathFirstDirection OppositeFirstDirection(SkPathFirstDirection dir) {
static const SkPathFirstDirection gOppositeDir[] = {
SkPathFirstDirection::kCCW, SkPathFirstDirection::kCW, SkPathFirstDirection::kUnknown,
};
return gOppositeDir[(unsigned)dir];
}
/**
* Tries to compute the direction of the outer-most non-degenerate
* contour. If it can be computed, return that direction. If it cannot be determined,
* or the contour is known to be convex, return kUnknown. If the direction was determined,
* it is cached to make subsequent calls return quickly.
*/
static SkPathFirstDirection ComputeFirstDirection(const SkPath&);
// In some scenarios (e.g. fill or convexity checking all but the last leading move to are
// irrelevant to behavior). SkPath::injectMoveToIfNeeded should ensure that this is always at
// least 1.
static int LeadingMoveToCount(const SkPath& path) {
int verbCount = path.countVerbs();
auto verbs = path.fPathRef->verbsBegin();
for (int i = 0; i < verbCount; i++) {
if (verbs[i] != SkPath::Verb::kMove_Verb) {
return i;
}
}
return verbCount; // path is all move verbs
}
/**
* Iterates through a raw range of path verbs, points, and conics. All values are returned
* unaltered.
*
* NOTE: This class's definition will be moved into SkPathPriv once RangeIter is removed.
*/
using RangeIter = SkPath::RangeIter;
/**
* Iterable object for traversing verbs, points, and conic weights in a path:
*
* for (auto [verb, pts, weights] : SkPathPriv::Iterate(skPath)) {
* ...
* }
*/
struct Iterate {
public:
Iterate(const SkPath& path)
: Iterate(path.fPathRef->verbsBegin(),
// Don't allow iteration through non-finite points.
(!path.isFinite()) ? path.fPathRef->verbsBegin()
: path.fPathRef->verbsEnd(),
path.fPathRef->points(), path.fPathRef->conicWeights()) {
}
Iterate(const uint8_t* verbsBegin, const uint8_t* verbsEnd, const SkPoint* points,
const SkScalar* weights)
: fVerbsBegin(verbsBegin), fVerbsEnd(verbsEnd), fPoints(points), fWeights(weights) {
}
SkPath::RangeIter begin() { return {fVerbsBegin, fPoints, fWeights}; }
SkPath::RangeIter end() { return {fVerbsEnd, nullptr, nullptr}; }
private:
const uint8_t* fVerbsBegin;
const uint8_t* fVerbsEnd;
const SkPoint* fPoints;
const SkScalar* fWeights;
};
/** Returns true if constructed by addCircle(), addOval(); and in some cases,
addRoundRect(), addRRect(). SkPath constructed with conicTo() or rConicTo() will not
return true though SkPath draws oval.
rect receives bounds of oval.
dir receives SkPathDirection of oval: kCW_Direction if clockwise, kCCW_Direction if
counterclockwise.
start receives start of oval: 0 for top, 1 for right, 2 for bottom, 3 for left.
rect, dir, and start are unmodified if oval is not found.
Triggers performance optimizations on some GPU surface implementations.
@param rect storage for bounding SkRect of oval; may be nullptr
@param dir storage for SkPathDirection; may be nullptr
@param start storage for start of oval; may be nullptr
@return true if SkPath was constructed by method that reduces to oval
*/
static bool IsOval(const SkPath& path, SkRect* rect, SkPathDirection* dir, unsigned* start) {
bool isCCW = false;
bool result = path.fPathRef->isOval(rect, &isCCW, start);
if (dir && result) {
*dir = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW;
}
return result;
}
/** Returns true if constructed by addRoundRect(), addRRect(); and if construction
is not empty, not SkRect, and not oval. SkPath constructed with other calls
will not return true though SkPath draws SkRRect.
rrect receives bounds of SkRRect.
dir receives SkPathDirection of oval: kCW_Direction if clockwise, kCCW_Direction if
counterclockwise.
start receives start of SkRRect: 0 for top, 1 for right, 2 for bottom, 3 for left.
rrect, dir, and start are unmodified if SkRRect is not found.
Triggers performance optimizations on some GPU surface implementations.
@param rrect storage for bounding SkRect of SkRRect; may be nullptr
@param dir storage for SkPathDirection; may be nullptr
@param start storage for start of SkRRect; may be nullptr
@return true if SkPath contains only SkRRect
*/
static bool IsRRect(const SkPath& path, SkRRect* rrect, SkPathDirection* dir,
unsigned* start) {
bool isCCW = false;
bool result = path.fPathRef->isRRect(rrect, &isCCW, start);
if (dir && result) {
*dir = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW;
}
return result;
}
// Returns number of valid points for each verb, not including the "starter"
// point that the Iterator adds for line/quad/conic/cubic
static int PtsInVerb(unsigned verb) {
static const uint8_t gPtsInVerb[] = {
1, // kMove pts[0]
1, // kLine pts[0..1]
2, // kQuad pts[0..2]
2, // kConic pts[0..2]
3, // kCubic pts[0..3]
0, // kClose
0 // kDone
};
return gPtsInVerb[verb];
}
static bool IsAxisAligned(const SkPath& path);
static bool AllPointsEq(const SkPoint pts[], int count) {
for (int i = 1; i < count; ++i) {
if (pts[0] != pts[i]) {
return false;
}
}
return true;
}
static bool IsRectContour(const SkPath&, bool allowPartial, int* currVerb,
const SkPoint** ptsPtr, bool* isClosed, SkPathDirection* direction,
SkRect* rect);
static void SetConvexity(const SkPath& path, SkPathConvexity c) {
path.setConvexity(c);
}
static void SetConvexity(SkPathBuilder* builder, SkPathConvexity c) {
builder->privateSetConvexity(c);
}
static void ReverseAddPath(SkPathBuilder* builder, const SkPath& reverseMe) {
builder->privateReverseAddPath(reverseMe);
}
};
// Lightweight variant of SkPath::Iter that only returns segments (e.g. lines/conics).
// Does not return kMove or kClose.
// Always "auto-closes" each contour.
// Roughly the same as SkPath::Iter(path, true), but does not return moves or closes
//
class SkPathEdgeIter {
const uint8_t* fVerbs;
const uint8_t* fVerbsStop;
const SkPoint* fPts;
const SkPoint* fMoveToPtr;
const SkScalar* fConicWeights;
SkPoint fScratch[2]; // for auto-close lines
bool fNeedsCloseLine;
bool fNextIsNewContour;
enum {
kIllegalEdgeValue = 99
};
public:
SkPathEdgeIter(const SkPath& path);
SkScalar conicWeight() const {
return *fConicWeights;
}
enum class Edge {
kLine = SkPath::kLine_Verb,
kQuad = SkPath::kQuad_Verb,
kConic = SkPath::kConic_Verb,
kCubic = SkPath::kCubic_Verb,
};
static SkPath::Verb EdgeToVerb(Edge e) {
return SkPath::Verb(e);
}
struct Result {
const SkPoint* fPts; // points for the segment, or null if done
Edge fEdge;
bool fIsNewContour;
// Returns true when it holds an Edge, false when the path is done.
operator bool() { return fPts != nullptr; }
};
Result next() {
auto closeline = [&]() {
fScratch[0] = fPts[-1];
fScratch[1] = *fMoveToPtr;
fNeedsCloseLine = false;
fNextIsNewContour = true;
return Result{ fScratch, Edge::kLine, false };
};
for (;;) {
if (fVerbs == fVerbsStop) {
return fNeedsCloseLine
? closeline()
: Result{ nullptr, Edge(kIllegalEdgeValue), false };
}
const auto v = *fVerbs++;
switch (v) {
case SkPath::kMove_Verb: {
if (fNeedsCloseLine) {
auto res = closeline();
fMoveToPtr = fPts++;
return res;
}
fMoveToPtr = fPts++;
fNextIsNewContour = true;
} break;
case SkPath::kClose_Verb:
if (fNeedsCloseLine) return closeline();
break;
default: {
// Actual edge.
const int pts_count = (v+2) / 2,
cws_count = (v & (v-1)) / 2;
fNeedsCloseLine = true;
fPts += pts_count;
fConicWeights += cws_count;
bool isNewContour = fNextIsNewContour;
fNextIsNewContour = false;
return { &fPts[-(pts_count + 1)], Edge(v), isNewContour };
}
}
}
}
};
} // namespace pk