/src/skia/include/private/SkPathRef.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2012 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 | | |
8 | | #ifndef SkPathRef_DEFINED |
9 | | #define SkPathRef_DEFINED |
10 | | |
11 | | #include "include/core/SkArc.h" |
12 | | #include "include/core/SkPoint.h" |
13 | | #include "include/core/SkRect.h" |
14 | | #include "include/core/SkRefCnt.h" |
15 | | #include "include/core/SkScalar.h" |
16 | | #include "include/core/SkTypes.h" |
17 | | #include "include/private/SkIDChangeListener.h" |
18 | | #include "include/private/base/SkDebug.h" |
19 | | #include "include/private/base/SkTArray.h" |
20 | | #include "include/private/base/SkTo.h" |
21 | | |
22 | | #include <atomic> |
23 | | #include <cstddef> |
24 | | #include <cstdint> |
25 | | #include <tuple> |
26 | | #include <utility> |
27 | | |
28 | | class SkMatrix; |
29 | | class SkRRect; |
30 | | |
31 | | // These are computed from a stream of verbs |
32 | | struct SkPathVerbAnalysis { |
33 | | bool valid; |
34 | | int points, weights; |
35 | | unsigned segmentMask; |
36 | | }; |
37 | | SkPathVerbAnalysis sk_path_analyze_verbs(const uint8_t verbs[], int count); |
38 | | |
39 | | |
40 | | /** |
41 | | * Holds the path verbs and points. It is versioned by a generation ID. None of its public methods |
42 | | * modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an |
43 | | * SkPathRef::Editor object. Installing the editor resets the generation ID. It also performs |
44 | | * copy-on-write if the SkPathRef is shared by multiple SkPaths. The caller passes the Editor's |
45 | | * constructor a pointer to a sk_sp<SkPathRef>, which may be updated to point to a new SkPathRef |
46 | | * after the editor's constructor returns. |
47 | | * |
48 | | * The points and verbs are stored in a single allocation. The points are at the begining of the |
49 | | * allocation while the verbs are stored at end of the allocation, in reverse order. Thus the points |
50 | | * and verbs both grow into the middle of the allocation until the meet. To access verb i in the |
51 | | * verb array use ref.verbs()[~i] (because verbs() returns a pointer just beyond the first |
52 | | * logical verb or the last verb in memory). |
53 | | */ |
54 | | |
55 | | class SK_API SkPathRef final : public SkNVRefCnt<SkPathRef> { |
56 | | public: |
57 | | // See https://bugs.chromium.org/p/skia/issues/detail?id=13817 for how these sizes were |
58 | | // determined. |
59 | | using PointsArray = skia_private::STArray<4, SkPoint>; |
60 | | using VerbsArray = skia_private::STArray<4, uint8_t>; |
61 | | using ConicWeightsArray = skia_private::STArray<2, SkScalar>; |
62 | | |
63 | | enum class PathType : uint8_t { |
64 | | kGeneral, |
65 | | kOval, |
66 | | kOpenOval, // An unclosed oval, as is generated by canvas2d ellipse or arc |
67 | | kRRect, |
68 | | kArc, |
69 | | }; |
70 | | |
71 | | SkPathRef(PointsArray points, VerbsArray verbs, ConicWeightsArray weights, |
72 | | unsigned segmentMask) |
73 | | : fPoints(std::move(points)) |
74 | | , fVerbs(std::move(verbs)) |
75 | | , fConicWeights(std::move(weights)) |
76 | 995k | { |
77 | 995k | fBoundsIsDirty = true; // this also invalidates fIsFinite |
78 | 995k | fGenerationID = 0; // recompute |
79 | 995k | fSegmentMask = segmentMask; |
80 | 995k | fType = PathType::kGeneral; |
81 | | // The next two values don't matter unless fType is kOval or kRRect |
82 | 995k | fRRectOrOvalIsCCW = false; |
83 | 995k | fRRectOrOvalStartIdx = 0xAC; |
84 | 995k | fArcOval.setEmpty(); |
85 | 995k | fArcStartAngle = fArcSweepAngle = 0.0f; |
86 | 995k | fArcType = SkArc::Type::kArc; |
87 | 995k | SkDEBUGCODE(fEditorsAttached.store(0);) |
88 | | |
89 | 995k | this->computeBounds(); // do this now, before we worry about multiple owners/threads |
90 | 995k | SkDEBUGCODE(this->validate();) |
91 | 995k | } SkPathRef::SkPathRef(skia_private::STArray<4, SkPoint, true>, skia_private::STArray<4, unsigned char, true>, skia_private::STArray<2, float, true>, unsigned int) Line | Count | Source | 76 | 497k | { | 77 | 497k | fBoundsIsDirty = true; // this also invalidates fIsFinite | 78 | 497k | fGenerationID = 0; // recompute | 79 | 497k | fSegmentMask = segmentMask; | 80 | 497k | fType = PathType::kGeneral; | 81 | | // The next two values don't matter unless fType is kOval or kRRect | 82 | 497k | fRRectOrOvalIsCCW = false; | 83 | 497k | fRRectOrOvalStartIdx = 0xAC; | 84 | 497k | fArcOval.setEmpty(); | 85 | 497k | fArcStartAngle = fArcSweepAngle = 0.0f; | 86 | 497k | fArcType = SkArc::Type::kArc; | 87 | 497k | SkDEBUGCODE(fEditorsAttached.store(0);) | 88 | | | 89 | 497k | this->computeBounds(); // do this now, before we worry about multiple owners/threads | 90 | 497k | SkDEBUGCODE(this->validate();) | 91 | 497k | } |
SkPathRef::SkPathRef(skia_private::STArray<4, SkPoint, true>, skia_private::STArray<4, unsigned char, true>, skia_private::STArray<2, float, true>, unsigned int) Line | Count | Source | 76 | 497k | { | 77 | 497k | fBoundsIsDirty = true; // this also invalidates fIsFinite | 78 | 497k | fGenerationID = 0; // recompute | 79 | 497k | fSegmentMask = segmentMask; | 80 | 497k | fType = PathType::kGeneral; | 81 | | // The next two values don't matter unless fType is kOval or kRRect | 82 | 497k | fRRectOrOvalIsCCW = false; | 83 | 497k | fRRectOrOvalStartIdx = 0xAC; | 84 | 497k | fArcOval.setEmpty(); | 85 | 497k | fArcStartAngle = fArcSweepAngle = 0.0f; | 86 | 497k | fArcType = SkArc::Type::kArc; | 87 | 497k | SkDEBUGCODE(fEditorsAttached.store(0);) | 88 | | | 89 | 497k | this->computeBounds(); // do this now, before we worry about multiple owners/threads | 90 | 497k | SkDEBUGCODE(this->validate();) | 91 | 497k | } |
|
92 | | |
93 | | class Editor { |
94 | | public: |
95 | | Editor(sk_sp<SkPathRef>* pathRef, |
96 | | int incReserveVerbs = 0, |
97 | | int incReservePoints = 0, |
98 | | int incReserveConics = 0); |
99 | | |
100 | 657M | ~Editor() { SkDEBUGCODE(fPathRef->fEditorsAttached--;) } SkPathRef::Editor::~Editor() Line | Count | Source | 100 | 328M | ~Editor() { SkDEBUGCODE(fPathRef->fEditorsAttached--;) } |
SkPathRef::Editor::~Editor() Line | Count | Source | 100 | 328M | ~Editor() { SkDEBUGCODE(fPathRef->fEditorsAttached--;) } |
|
101 | | |
102 | | /** |
103 | | * Returns the array of points. |
104 | | */ |
105 | 110k | SkPoint* writablePoints() { return fPathRef->getWritablePoints(); } |
106 | 0 | const SkPoint* points() const { return fPathRef->points(); } |
107 | | |
108 | | /** |
109 | | * Gets the ith point. Shortcut for this->points() + i |
110 | | */ |
111 | 1.33M | SkPoint* atPoint(int i) { return fPathRef->getWritablePoints() + i; } |
112 | 0 | const SkPoint* atPoint(int i) const { return &fPathRef->fPoints[i]; } |
113 | | |
114 | | /** |
115 | | * Adds the verb and allocates space for the number of points indicated by the verb. The |
116 | | * return value is a pointer to where the points for the verb should be written. |
117 | | * 'weight' is only used if 'verb' is kConic_Verb |
118 | | */ |
119 | 638M | SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight = 0) { |
120 | 638M | SkDEBUGCODE(fPathRef->validate();) |
121 | 638M | return fPathRef->growForVerb(verb, weight); |
122 | 638M | } SkPathRef::Editor::growForVerb(int, float) Line | Count | Source | 119 | 319M | SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight = 0) { | 120 | 319M | SkDEBUGCODE(fPathRef->validate();) | 121 | 319M | return fPathRef->growForVerb(verb, weight); | 122 | 319M | } |
SkPathRef::Editor::growForVerb(int, float) Line | Count | Source | 119 | 319M | SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight = 0) { | 120 | 319M | SkDEBUGCODE(fPathRef->validate();) | 121 | 319M | return fPathRef->growForVerb(verb, weight); | 122 | 319M | } |
|
123 | | |
124 | | /** |
125 | | * Allocates space for multiple instances of a particular verb and the |
126 | | * requisite points & weights. |
127 | | * The return pointer points at the first new point (indexed normally [<i>]). |
128 | | * If 'verb' is kConic_Verb, 'weights' will return a pointer to the |
129 | | * space for the conic weights (indexed normally). |
130 | | */ |
131 | | SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, |
132 | | int numVbs, |
133 | 2.92M | SkScalar** weights = nullptr) { |
134 | 2.92M | return fPathRef->growForRepeatedVerb(verb, numVbs, weights); |
135 | 2.92M | } |
136 | | |
137 | | /** |
138 | | * Concatenates all verbs from 'path' onto the pathRef's verbs array. Increases the point |
139 | | * count by the number of points in 'path', and the conic weight count by the number of |
140 | | * conics in 'path'. |
141 | | * |
142 | | * Returns pointers to the uninitialized points and conic weights data. |
143 | | */ |
144 | 7.46M | std::tuple<SkPoint*, SkScalar*> growForVerbsInPath(const SkPathRef& path) { |
145 | 7.46M | return fPathRef->growForVerbsInPath(path); |
146 | 7.46M | } |
147 | | |
148 | | /** |
149 | | * Resets the path ref to a new verb and point count. The new verbs and points are |
150 | | * uninitialized. |
151 | | */ |
152 | 0 | void resetToSize(int newVerbCnt, int newPointCnt, int newConicCount) { |
153 | 0 | fPathRef->resetToSize(newVerbCnt, newPointCnt, newConicCount); |
154 | 0 | } |
155 | | |
156 | | /** |
157 | | * Gets the path ref that is wrapped in the Editor. |
158 | | */ |
159 | 110k | SkPathRef* pathRef() { return fPathRef; } |
160 | | |
161 | 73.3k | void setIsOval(bool isCCW, unsigned start, bool isClosed) { |
162 | 73.3k | fPathRef->setIsOval(isCCW, start, isClosed); |
163 | 73.3k | } |
164 | | |
165 | 13.3k | void setIsRRect(bool isCCW, unsigned start) { |
166 | 13.3k | fPathRef->setIsRRect(isCCW, start); |
167 | 13.3k | } |
168 | | |
169 | 2.78k | void setIsArc(const SkArc& arc) { |
170 | 2.78k | fPathRef->setIsArc(arc); |
171 | 2.78k | } |
172 | | |
173 | 182k | void setBounds(const SkRect& rect) { fPathRef->setBounds(rect); } |
174 | | |
175 | | private: |
176 | | SkPathRef* fPathRef; |
177 | | }; |
178 | | |
179 | | class SK_API Iter { |
180 | | public: |
181 | | Iter(); |
182 | | Iter(const SkPathRef&); |
183 | | |
184 | | void setPathRef(const SkPathRef&); |
185 | | |
186 | | /** Return the next verb in this iteration of the path. When all |
187 | | segments have been visited, return kDone_Verb. |
188 | | |
189 | | If any point in the path is non-finite, return kDone_Verb immediately. |
190 | | |
191 | | @param pts The points representing the current verb and/or segment |
192 | | This must not be NULL. |
193 | | @return The verb for the current segment |
194 | | */ |
195 | | uint8_t next(SkPoint pts[4]); |
196 | | uint8_t peek() const; |
197 | | |
198 | 0 | SkScalar conicWeight() const { return *fConicWeights; } |
199 | | |
200 | | private: |
201 | | const SkPoint* fPts; |
202 | | const uint8_t* fVerbs; |
203 | | const uint8_t* fVerbStop; |
204 | | const SkScalar* fConicWeights; |
205 | | }; |
206 | | |
207 | | public: |
208 | | /** |
209 | | * Gets a path ref with no verbs or points. |
210 | | */ |
211 | | static SkPathRef* CreateEmpty(); |
212 | | |
213 | | /** |
214 | | * Returns true if all of the points in this path are finite, meaning there |
215 | | * are no infinities and no NaNs. |
216 | | */ |
217 | 109M | bool isFinite() const { |
218 | 109M | if (fBoundsIsDirty) { |
219 | 3.97M | this->computeBounds(); |
220 | 3.97M | } |
221 | 109M | return SkToBool(fIsFinite); |
222 | 109M | } |
223 | | |
224 | | /** |
225 | | * Returns a mask, where each bit corresponding to a SegmentMask is |
226 | | * set if the path contains 1 or more segments of that type. |
227 | | * Returns 0 for an empty path (no segments). |
228 | | */ |
229 | 15.4M | uint32_t getSegmentMasks() const { return fSegmentMask; } |
230 | | |
231 | | /** Returns true if the path is an oval. |
232 | | * |
233 | | * @param rect returns the bounding rect of this oval. It's a circle |
234 | | * if the height and width are the same. |
235 | | * @param isCCW is the oval CCW (or CW if false). |
236 | | * @param start indicates where the contour starts on the oval (see |
237 | | * SkPath::addOval for intepretation of the index). |
238 | | * |
239 | | * @return true if this path is an oval. |
240 | | * Tracking whether a path is an oval is considered an |
241 | | * optimization for performance and so some paths that are in |
242 | | * fact ovals can report false. |
243 | | */ |
244 | 99.4k | bool isOval(SkRect* rect, bool* isCCW, unsigned* start) const { |
245 | 99.4k | if (fType == PathType::kOval) { |
246 | 995 | if (rect) { |
247 | 995 | *rect = this->getBounds(); |
248 | 995 | } |
249 | 995 | if (isCCW) { |
250 | 995 | *isCCW = SkToBool(fRRectOrOvalIsCCW); |
251 | 995 | } |
252 | 995 | if (start) { |
253 | 680 | *start = fRRectOrOvalStartIdx; |
254 | 680 | } |
255 | 995 | } |
256 | | |
257 | 99.4k | return fType == PathType::kOval; |
258 | 99.4k | } |
259 | | |
260 | | bool isRRect(SkRRect* rrect, bool* isCCW, unsigned* start) const; |
261 | | |
262 | 0 | bool isArc(SkArc* arc) const { |
263 | 0 | if (fType == PathType::kArc) { |
264 | 0 | if (arc) { |
265 | 0 | *arc = SkArc::Make(fArcOval, fArcStartAngle, fArcSweepAngle, fArcType); |
266 | 0 | } |
267 | 0 | } |
268 | |
|
269 | 0 | return fType == PathType::kArc; |
270 | 0 | } |
271 | | |
272 | 208k | bool hasComputedBounds() const { |
273 | 208k | return !fBoundsIsDirty; |
274 | 208k | } |
275 | | |
276 | | /** Returns the bounds of the path's points. If the path contains 0 or 1 |
277 | | points, the bounds is set to (0,0,0,0), and isEmpty() will return true. |
278 | | Note: this bounds may be larger than the actual shape, since curves |
279 | | do not extend as far as their control points. |
280 | | */ |
281 | 21.6M | const SkRect& getBounds() const { |
282 | 21.6M | if (fBoundsIsDirty) { |
283 | 464k | this->computeBounds(); |
284 | 464k | } |
285 | 21.6M | return fBounds; |
286 | 21.6M | } |
287 | | |
288 | | SkRRect getRRect() const; |
289 | | |
290 | | /** |
291 | | * Transforms a path ref by a matrix, allocating a new one only if necessary. |
292 | | */ |
293 | | static void CreateTransformedCopy(sk_sp<SkPathRef>* dst, |
294 | | const SkPathRef& src, |
295 | | const SkMatrix& matrix); |
296 | | |
297 | | // static SkPathRef* CreateFromBuffer(SkRBuffer* buffer); |
298 | | |
299 | | /** |
300 | | * Rollsback a path ref to zero verbs and points with the assumption that the path ref will be |
301 | | * repopulated with approximately the same number of verbs and points. A new path ref is created |
302 | | * only if necessary. |
303 | | */ |
304 | | static void Rewind(sk_sp<SkPathRef>* pathRef); |
305 | | |
306 | | ~SkPathRef(); |
307 | 461M | int countPoints() const { return fPoints.size(); } |
308 | 203M | int countVerbs() const { return fVerbs.size(); } |
309 | 14.9M | int countWeights() const { return fConicWeights.size(); } |
310 | | |
311 | | size_t approximateBytesUsed() const; |
312 | | |
313 | | /** |
314 | | * Returns a pointer one beyond the first logical verb (last verb in memory order). |
315 | | */ |
316 | 316M | const uint8_t* verbsBegin() const { return fVerbs.begin(); } |
317 | | |
318 | | /** |
319 | | * Returns a const pointer to the first verb in memory (which is the last logical verb). |
320 | | */ |
321 | 247M | const uint8_t* verbsEnd() const { return fVerbs.end(); } |
322 | | |
323 | | /** |
324 | | * Returns a const pointer to the first point. |
325 | | */ |
326 | 271M | const SkPoint* points() const { return fPoints.begin(); } |
327 | | |
328 | | /** |
329 | | * Shortcut for this->points() + this->countPoints() |
330 | | */ |
331 | 2.95M | const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); } |
332 | | |
333 | 242M | const SkScalar* conicWeights() const { return fConicWeights.begin(); } |
334 | 2.95M | const SkScalar* conicWeightsEnd() const { return fConicWeights.end(); } |
335 | | |
336 | | /** |
337 | | * Convenience methods for getting to a verb or point by index. |
338 | | */ |
339 | 19.8M | uint8_t atVerb(int index) const { return fVerbs[index]; } |
340 | 12.1M | const SkPoint& atPoint(int index) const { return fPoints[index]; } |
341 | | |
342 | | bool operator== (const SkPathRef& ref) const; |
343 | | |
344 | | void interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const; |
345 | | |
346 | | /** |
347 | | * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the |
348 | | * same ID then they have the same verbs and points. However, two path refs may have the same |
349 | | * contents but different genIDs. |
350 | | * skbug.com/1762 for background on why fillType is necessary (for now). |
351 | | */ |
352 | | uint32_t genID(uint8_t fillType) const; |
353 | | |
354 | | void addGenIDChangeListener(sk_sp<SkIDChangeListener>); // Threadsafe. |
355 | | int genIDChangeListenerCount(); // Threadsafe |
356 | | |
357 | | bool dataMatchesVerbs() const; |
358 | | bool isValid() const; |
359 | | SkDEBUGCODE(void validate() const { SkASSERT(this->isValid()); } ) |
360 | | |
361 | | /** |
362 | | * Resets this SkPathRef to a clean state. |
363 | | */ |
364 | | void reset(); |
365 | | |
366 | 4.91M | bool isInitialEmptyPathRef() const { |
367 | 4.91M | return fGenerationID == kEmptyGenID; |
368 | 4.91M | } |
369 | | |
370 | | private: |
371 | | enum SerializationOffsets { |
372 | | kLegacyRRectOrOvalStartIdx_SerializationShift = 28, // requires 3 bits, ignored. |
373 | | kLegacyRRectOrOvalIsCCW_SerializationShift = 27, // requires 1 bit, ignored. |
374 | | kLegacyIsRRect_SerializationShift = 26, // requires 1 bit, ignored. |
375 | | kIsFinite_SerializationShift = 25, // requires 1 bit |
376 | | kLegacyIsOval_SerializationShift = 24, // requires 1 bit, ignored. |
377 | | kSegmentMask_SerializationShift = 0 // requires 4 bits (deprecated) |
378 | | }; |
379 | | |
380 | 167M | SkPathRef(int numVerbs = 0, int numPoints = 0, int numConics = 0) { |
381 | 167M | fBoundsIsDirty = true; // this also invalidates fIsFinite |
382 | 167M | fGenerationID = kEmptyGenID; |
383 | 167M | fSegmentMask = 0; |
384 | 167M | fType = PathType::kGeneral; |
385 | | // The next two values don't matter unless fType is kOval or kRRect |
386 | 167M | fRRectOrOvalIsCCW = false; |
387 | 167M | fRRectOrOvalStartIdx = 0xAC; |
388 | 167M | fArcOval.setEmpty(); |
389 | 167M | fArcStartAngle = fArcSweepAngle = 0.0f; |
390 | 167M | fArcType = SkArc::Type::kArc; |
391 | 167M | if (numPoints > 0) { |
392 | 6.60M | fPoints.reserve_exact(numPoints); |
393 | 6.60M | } |
394 | 167M | if (numVerbs > 0) { |
395 | 6.60M | fVerbs.reserve_exact(numVerbs); |
396 | 6.60M | } |
397 | 167M | if (numConics > 0) { |
398 | 29.1k | fConicWeights.reserve_exact(numConics); |
399 | 29.1k | } |
400 | 167M | SkDEBUGCODE(fEditorsAttached.store(0);) |
401 | 167M | SkDEBUGCODE(this->validate();) |
402 | 167M | } SkPathRef::SkPathRef(int, int, int) Line | Count | Source | 380 | 83.6M | SkPathRef(int numVerbs = 0, int numPoints = 0, int numConics = 0) { | 381 | 83.6M | fBoundsIsDirty = true; // this also invalidates fIsFinite | 382 | 83.6M | fGenerationID = kEmptyGenID; | 383 | 83.6M | fSegmentMask = 0; | 384 | 83.6M | fType = PathType::kGeneral; | 385 | | // The next two values don't matter unless fType is kOval or kRRect | 386 | 83.6M | fRRectOrOvalIsCCW = false; | 387 | 83.6M | fRRectOrOvalStartIdx = 0xAC; | 388 | 83.6M | fArcOval.setEmpty(); | 389 | 83.6M | fArcStartAngle = fArcSweepAngle = 0.0f; | 390 | 83.6M | fArcType = SkArc::Type::kArc; | 391 | 83.6M | if (numPoints > 0) { | 392 | 3.30M | fPoints.reserve_exact(numPoints); | 393 | 3.30M | } | 394 | 83.6M | if (numVerbs > 0) { | 395 | 3.30M | fVerbs.reserve_exact(numVerbs); | 396 | 3.30M | } | 397 | 83.6M | if (numConics > 0) { | 398 | 14.5k | fConicWeights.reserve_exact(numConics); | 399 | 14.5k | } | 400 | 83.6M | SkDEBUGCODE(fEditorsAttached.store(0);) | 401 | 83.6M | SkDEBUGCODE(this->validate();) | 402 | 83.6M | } |
SkPathRef::SkPathRef(int, int, int) Line | Count | Source | 380 | 83.6M | SkPathRef(int numVerbs = 0, int numPoints = 0, int numConics = 0) { | 381 | 83.6M | fBoundsIsDirty = true; // this also invalidates fIsFinite | 382 | 83.6M | fGenerationID = kEmptyGenID; | 383 | 83.6M | fSegmentMask = 0; | 384 | 83.6M | fType = PathType::kGeneral; | 385 | | // The next two values don't matter unless fType is kOval or kRRect | 386 | 83.6M | fRRectOrOvalIsCCW = false; | 387 | 83.6M | fRRectOrOvalStartIdx = 0xAC; | 388 | 83.6M | fArcOval.setEmpty(); | 389 | 83.6M | fArcStartAngle = fArcSweepAngle = 0.0f; | 390 | 83.6M | fArcType = SkArc::Type::kArc; | 391 | 83.6M | if (numPoints > 0) { | 392 | 3.30M | fPoints.reserve_exact(numPoints); | 393 | 3.30M | } | 394 | 83.6M | if (numVerbs > 0) { | 395 | 3.30M | fVerbs.reserve_exact(numVerbs); | 396 | 3.30M | } | 397 | 83.6M | if (numConics > 0) { | 398 | 14.5k | fConicWeights.reserve_exact(numConics); | 399 | 14.5k | } | 400 | 83.6M | SkDEBUGCODE(fEditorsAttached.store(0);) | 401 | 83.6M | SkDEBUGCODE(this->validate();) | 402 | 83.6M | } |
|
403 | | |
404 | | void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints, int additionalReserveConics); |
405 | | |
406 | | // Return true if the computed bounds are finite. |
407 | 4.93M | static bool ComputePtBounds(SkRect* bounds, const SkPathRef& ref) { |
408 | 4.93M | return bounds->setBoundsCheck(ref.points(), ref.countPoints()); |
409 | 4.93M | } |
410 | | |
411 | | // called, if dirty, by getBounds() |
412 | 4.93M | void computeBounds() const { |
413 | 4.93M | SkDEBUGCODE(this->validate();) |
414 | | // TODO: remove fBoundsIsDirty and fIsFinite, |
415 | | // using an inverted rect instead of fBoundsIsDirty and always recalculating fIsFinite. |
416 | 4.93M | SkASSERT(fBoundsIsDirty); |
417 | | |
418 | 4.93M | fIsFinite = ComputePtBounds(&fBounds, *this); |
419 | 4.93M | fBoundsIsDirty = false; |
420 | 4.93M | } SkPathRef::computeBounds() const Line | Count | Source | 412 | 4.93M | void computeBounds() const { | 413 | 4.93M | SkDEBUGCODE(this->validate();) | 414 | | // TODO: remove fBoundsIsDirty and fIsFinite, | 415 | | // using an inverted rect instead of fBoundsIsDirty and always recalculating fIsFinite. | 416 | 4.93M | SkASSERT(fBoundsIsDirty); | 417 | | | 418 | 4.93M | fIsFinite = ComputePtBounds(&fBounds, *this); | 419 | 4.93M | fBoundsIsDirty = false; | 420 | 4.93M | } |
SkPathRef::computeBounds() const Line | Count | Source | 412 | 4 | void computeBounds() const { | 413 | 4 | SkDEBUGCODE(this->validate();) | 414 | | // TODO: remove fBoundsIsDirty and fIsFinite, | 415 | | // using an inverted rect instead of fBoundsIsDirty and always recalculating fIsFinite. | 416 | 4 | SkASSERT(fBoundsIsDirty); | 417 | | | 418 | 4 | fIsFinite = ComputePtBounds(&fBounds, *this); | 419 | 4 | fBoundsIsDirty = false; | 420 | 4 | } |
|
421 | | |
422 | 182k | void setBounds(const SkRect& rect) { |
423 | 182k | SkASSERT(rect.fLeft <= rect.fRight && rect.fTop <= rect.fBottom); |
424 | 182k | fBounds = rect; |
425 | 182k | fBoundsIsDirty = false; |
426 | 182k | fIsFinite = fBounds.isFinite(); |
427 | 182k | } |
428 | | |
429 | | /** Makes additional room but does not change the counts or change the genID */ |
430 | 647M | void incReserve(int additionalVerbs, int additionalPoints, int additionalConics) { |
431 | 647M | SkDEBUGCODE(this->validate();) |
432 | | // Use reserve() so that if there is not enough space, the array will grow with some |
433 | | // additional space. This ensures repeated calls to grow won't always allocate. |
434 | 647M | if (additionalPoints > 0) { |
435 | 566k | fPoints.reserve(fPoints.size() + additionalPoints); |
436 | 566k | } |
437 | 647M | if (additionalVerbs > 0) { |
438 | 566k | fVerbs.reserve(fVerbs.size() + additionalVerbs); |
439 | 566k | } |
440 | 647M | if (additionalConics > 0) { |
441 | 206k | fConicWeights.reserve(fConicWeights.size() + additionalConics); |
442 | 206k | } |
443 | 647M | SkDEBUGCODE(this->validate();) |
444 | 647M | } SkPathRef::incReserve(int, int, int) Line | Count | Source | 430 | 323M | void incReserve(int additionalVerbs, int additionalPoints, int additionalConics) { | 431 | 323M | SkDEBUGCODE(this->validate();) | 432 | | // Use reserve() so that if there is not enough space, the array will grow with some | 433 | | // additional space. This ensures repeated calls to grow won't always allocate. | 434 | 323M | if (additionalPoints > 0) { | 435 | 283k | fPoints.reserve(fPoints.size() + additionalPoints); | 436 | 283k | } | 437 | 323M | if (additionalVerbs > 0) { | 438 | 283k | fVerbs.reserve(fVerbs.size() + additionalVerbs); | 439 | 283k | } | 440 | 323M | if (additionalConics > 0) { | 441 | 103k | fConicWeights.reserve(fConicWeights.size() + additionalConics); | 442 | 103k | } | 443 | 323M | SkDEBUGCODE(this->validate();) | 444 | 323M | } |
SkPathRef::incReserve(int, int, int) Line | Count | Source | 430 | 323M | void incReserve(int additionalVerbs, int additionalPoints, int additionalConics) { | 431 | 323M | SkDEBUGCODE(this->validate();) | 432 | | // Use reserve() so that if there is not enough space, the array will grow with some | 433 | | // additional space. This ensures repeated calls to grow won't always allocate. | 434 | 323M | if (additionalPoints > 0) { | 435 | 283k | fPoints.reserve(fPoints.size() + additionalPoints); | 436 | 283k | } | 437 | 323M | if (additionalVerbs > 0) { | 438 | 283k | fVerbs.reserve(fVerbs.size() + additionalVerbs); | 439 | 283k | } | 440 | 323M | if (additionalConics > 0) { | 441 | 103k | fConicWeights.reserve(fConicWeights.size() + additionalConics); | 442 | 103k | } | 443 | 323M | SkDEBUGCODE(this->validate();) | 444 | 323M | } |
|
445 | | |
446 | | /** |
447 | | * Resets all state except that of the verbs, points, and conic-weights. |
448 | | * Intended to be called from other functions that reset state. |
449 | | */ |
450 | 7.19M | void commonReset() { |
451 | 7.19M | SkDEBUGCODE(this->validate();) |
452 | 7.19M | this->callGenIDChangeListeners(); |
453 | 7.19M | fBoundsIsDirty = true; // this also invalidates fIsFinite |
454 | 7.19M | fGenerationID = 0; |
455 | | |
456 | 7.19M | fSegmentMask = 0; |
457 | 7.19M | fType = PathType::kGeneral; |
458 | 7.19M | } Line | Count | Source | 450 | 3.59M | void commonReset() { | 451 | 3.59M | SkDEBUGCODE(this->validate();) | 452 | 3.59M | this->callGenIDChangeListeners(); | 453 | 3.59M | fBoundsIsDirty = true; // this also invalidates fIsFinite | 454 | 3.59M | fGenerationID = 0; | 455 | | | 456 | 3.59M | fSegmentMask = 0; | 457 | 3.59M | fType = PathType::kGeneral; | 458 | 3.59M | } |
Line | Count | Source | 450 | 3.59M | void commonReset() { | 451 | 3.59M | SkDEBUGCODE(this->validate();) | 452 | 3.59M | this->callGenIDChangeListeners(); | 453 | 3.59M | fBoundsIsDirty = true; // this also invalidates fIsFinite | 454 | 3.59M | fGenerationID = 0; | 455 | | | 456 | 3.59M | fSegmentMask = 0; | 457 | 3.59M | fType = PathType::kGeneral; | 458 | 3.59M | } |
|
459 | | |
460 | | /** Resets the path ref with verbCount verbs and pointCount points, all uninitialized. Also |
461 | | * allocates space for reserveVerb additional verbs and reservePoints additional points.*/ |
462 | | void resetToSize(int verbCount, int pointCount, int conicCount, |
463 | | int reserveVerbs = 0, int reservePoints = 0, |
464 | 3.62M | int reserveConics = 0) { |
465 | 3.62M | this->commonReset(); |
466 | | // Use reserve_exact() so the arrays are sized to exactly fit the data. |
467 | 3.62M | fPoints.reserve_exact(pointCount + reservePoints); |
468 | 3.62M | fPoints.resize_back(pointCount); |
469 | | |
470 | 3.62M | fVerbs.reserve_exact(verbCount + reserveVerbs); |
471 | 3.62M | fVerbs.resize_back(verbCount); |
472 | | |
473 | 3.62M | fConicWeights.reserve_exact(conicCount + reserveConics); |
474 | 3.62M | fConicWeights.resize_back(conicCount); |
475 | 3.62M | SkDEBUGCODE(this->validate();) |
476 | 3.62M | } SkPathRef::resetToSize(int, int, int, int, int, int) Line | Count | Source | 464 | 1.81M | int reserveConics = 0) { | 465 | 1.81M | this->commonReset(); | 466 | | // Use reserve_exact() so the arrays are sized to exactly fit the data. | 467 | 1.81M | fPoints.reserve_exact(pointCount + reservePoints); | 468 | 1.81M | fPoints.resize_back(pointCount); | 469 | | | 470 | 1.81M | fVerbs.reserve_exact(verbCount + reserveVerbs); | 471 | 1.81M | fVerbs.resize_back(verbCount); | 472 | | | 473 | 1.81M | fConicWeights.reserve_exact(conicCount + reserveConics); | 474 | 1.81M | fConicWeights.resize_back(conicCount); | 475 | 1.81M | SkDEBUGCODE(this->validate();) | 476 | 1.81M | } |
SkPathRef::resetToSize(int, int, int, int, int, int) Line | Count | Source | 464 | 1.81M | int reserveConics = 0) { | 465 | 1.81M | this->commonReset(); | 466 | | // Use reserve_exact() so the arrays are sized to exactly fit the data. | 467 | 1.81M | fPoints.reserve_exact(pointCount + reservePoints); | 468 | 1.81M | fPoints.resize_back(pointCount); | 469 | | | 470 | 1.81M | fVerbs.reserve_exact(verbCount + reserveVerbs); | 471 | 1.81M | fVerbs.resize_back(verbCount); | 472 | | | 473 | 1.81M | fConicWeights.reserve_exact(conicCount + reserveConics); | 474 | 1.81M | fConicWeights.resize_back(conicCount); | 475 | 1.81M | SkDEBUGCODE(this->validate();) | 476 | 1.81M | } |
|
477 | | |
478 | | /** |
479 | | * Increases the verb count by numVbs and point count by the required amount. |
480 | | * The new points are uninitialized. All the new verbs are set to the specified |
481 | | * verb. If 'verb' is kConic_Verb, 'weights' will return a pointer to the |
482 | | * uninitialized conic weights. |
483 | | */ |
484 | | SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, int numVbs, SkScalar** weights); |
485 | | |
486 | | /** |
487 | | * Increases the verb count 1, records the new verb, and creates room for the requisite number |
488 | | * of additional points. A pointer to the first point is returned. Any new points are |
489 | | * uninitialized. |
490 | | */ |
491 | | SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight); |
492 | | |
493 | | /** |
494 | | * Concatenates all verbs from 'path' onto our own verbs array. Increases the point count by the |
495 | | * number of points in 'path', and the conic weight count by the number of conics in 'path'. |
496 | | * |
497 | | * Returns pointers to the uninitialized points and conic weights data. |
498 | | */ |
499 | | std::tuple<SkPoint*, SkScalar*> growForVerbsInPath(const SkPathRef& path); |
500 | | |
501 | | /** |
502 | | * Private, non-const-ptr version of the public function verbsMemBegin(). |
503 | | */ |
504 | 0 | uint8_t* verbsBeginWritable() { return fVerbs.begin(); } |
505 | | |
506 | | /** |
507 | | * Called the first time someone calls CreateEmpty to actually create the singleton. |
508 | | */ |
509 | | friend SkPathRef* sk_create_empty_pathref(); |
510 | | |
511 | 75.1k | void setIsOval(bool isCCW, unsigned start, bool isClosed) { |
512 | 75.1k | fType = isClosed ? PathType::kOval : PathType::kOpenOval; |
513 | 75.1k | fRRectOrOvalIsCCW = isCCW; |
514 | 75.1k | fRRectOrOvalStartIdx = SkToU8(start); |
515 | 75.1k | } |
516 | | |
517 | 279k | void setIsRRect(bool isCCW, unsigned start) { |
518 | 279k | fType = PathType::kRRect; |
519 | 279k | fRRectOrOvalIsCCW = isCCW; |
520 | 279k | fRRectOrOvalStartIdx = SkToU8(start); |
521 | 279k | } |
522 | | |
523 | 2.78k | void setIsArc(const SkArc& arc) { |
524 | 2.78k | fType = PathType::kArc; |
525 | 2.78k | fArcOval = arc.fOval; |
526 | 2.78k | fArcStartAngle = arc.fStartAngle; |
527 | 2.78k | fArcSweepAngle = arc.fSweepAngle; |
528 | 2.78k | fArcType = arc.fType; |
529 | 2.78k | } |
530 | | |
531 | | // called only by the editor. Note that this is not a const function. |
532 | 2.89M | SkPoint* getWritablePoints() { |
533 | 2.89M | SkDEBUGCODE(this->validate();) |
534 | 2.89M | fType = PathType::kGeneral; |
535 | 2.89M | return fPoints.begin(); |
536 | 2.89M | } SkPathRef::getWritablePoints() Line | Count | Source | 532 | 1.44M | SkPoint* getWritablePoints() { | 533 | 1.44M | SkDEBUGCODE(this->validate();) | 534 | 1.44M | fType = PathType::kGeneral; | 535 | 1.44M | return fPoints.begin(); | 536 | 1.44M | } |
SkPathRef::getWritablePoints() Line | Count | Source | 532 | 1.44M | SkPoint* getWritablePoints() { | 533 | 1.44M | SkDEBUGCODE(this->validate();) | 534 | 1.44M | fType = PathType::kGeneral; | 535 | 1.44M | return fPoints.begin(); | 536 | 1.44M | } |
|
537 | | |
538 | 0 | const SkPoint* getPoints() const { |
539 | 0 | SkDEBUGCODE(this->validate();) |
540 | 0 | return fPoints.begin(); |
541 | 0 | } Unexecuted instantiation: SkPathRef::getPoints() const Unexecuted instantiation: SkPathRef::getPoints() const |
542 | | |
543 | | void callGenIDChangeListeners(); |
544 | | |
545 | | mutable SkRect fBounds; |
546 | | |
547 | | enum { |
548 | | kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs. |
549 | | }; |
550 | | mutable uint32_t fGenerationID; |
551 | | SkIDChangeListener::List fGenIDChangeListeners; |
552 | | |
553 | | PointsArray fPoints; |
554 | | VerbsArray fVerbs; |
555 | | ConicWeightsArray fConicWeights; |
556 | | |
557 | | SkDEBUGCODE(std::atomic<int> fEditorsAttached;) // assert only one editor in use at any time. |
558 | | |
559 | | mutable uint8_t fBoundsIsDirty; |
560 | | mutable bool fIsFinite; // only meaningful if bounds are valid |
561 | | |
562 | | PathType fType; |
563 | | // Both the circle and rrect special cases have a notion of direction and starting point |
564 | | // The next two variables store that information for either. |
565 | | bool fRRectOrOvalIsCCW; |
566 | | uint8_t fRRectOrOvalStartIdx; |
567 | | uint8_t fSegmentMask; |
568 | | // If the path is an arc, these four variables store that information. |
569 | | // We should just store an SkArc, but alignment would cost us 8 more bytes. |
570 | | SkArc::Type fArcType; |
571 | | SkRect fArcOval; |
572 | | SkScalar fArcStartAngle; |
573 | | SkScalar fArcSweepAngle; |
574 | | |
575 | | friend class PathRefTest_Private; |
576 | | friend class ForceIsRRect_Private; // unit test isRRect |
577 | | friend class SkPath; |
578 | | friend class SkPathBuilder; |
579 | | friend class SkPathPriv; |
580 | | }; |
581 | | |
582 | | #endif |