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