/src/skia/src/core/SkPath_serial.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2018 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 | | #include "include/core/SkData.h" |
9 | | #include "include/core/SkMath.h" |
10 | | #include "include/private/SkPathRef.h" |
11 | | #include "include/private/SkTPin.h" |
12 | | #include "include/private/SkTo.h" |
13 | | #include "src/core/SkBuffer.h" |
14 | | #include "src/core/SkPathPriv.h" |
15 | | #include "src/core/SkRRectPriv.h" |
16 | | #include "src/core/SkSafeMath.h" |
17 | | |
18 | | #include <cmath> |
19 | | |
20 | | enum SerializationOffsets { |
21 | | kType_SerializationShift = 28, // requires 4 bits |
22 | | kDirection_SerializationShift = 26, // requires 2 bits |
23 | | kFillType_SerializationShift = 8, // requires 8 bits |
24 | | // low-8-bits are version |
25 | | kVersion_SerializationMask = 0xFF, |
26 | | }; |
27 | | |
28 | | enum SerializationVersions { |
29 | | // kPathPrivFirstDirection_Version = 1, |
30 | | // kPathPrivLastMoveToIndex_Version = 2, |
31 | | // kPathPrivTypeEnumVersion = 3, |
32 | | kJustPublicData_Version = 4, // introduced Feb/2018 |
33 | | kVerbsAreStoredForward_Version = 5, // introduced Sept/2019 |
34 | | |
35 | | kMin_Version = kJustPublicData_Version, |
36 | | kCurrent_Version = kVerbsAreStoredForward_Version |
37 | | }; |
38 | | |
39 | | enum SerializationType { |
40 | | kGeneral = 0, |
41 | | kRRect = 1 |
42 | | }; |
43 | | |
44 | 16.7k | static unsigned extract_version(uint32_t packed) { |
45 | 16.7k | return packed & kVersion_SerializationMask; |
46 | 16.7k | } |
47 | | |
48 | 8.25k | static SkPathFillType extract_filltype(uint32_t packed) { |
49 | 8.25k | return static_cast<SkPathFillType>((packed >> kFillType_SerializationShift) & 0x3); |
50 | 8.25k | } |
51 | | |
52 | 8.37k | static SerializationType extract_serializationtype(uint32_t packed) { |
53 | 8.37k | return static_cast<SerializationType>((packed >> kType_SerializationShift) & 0xF); |
54 | 8.37k | } |
55 | | |
56 | | /////////////////////////////////////////////////////////////////////////////////////////////////// |
57 | | |
58 | 9.33k | size_t SkPath::writeToMemoryAsRRect(void* storage) const { |
59 | 9.33k | SkRect oval; |
60 | 9.33k | SkRRect rrect; |
61 | 9.33k | bool isCCW; |
62 | 9.33k | unsigned start; |
63 | 9.33k | if (fPathRef->isOval(&oval, &isCCW, &start)) { |
64 | 702 | rrect.setOval(oval); |
65 | | // Convert to rrect start indices. |
66 | 702 | start *= 2; |
67 | 8.63k | } else if (!fPathRef->isRRect(&rrect, &isCCW, &start)) { |
68 | 6.86k | return 0; |
69 | 6.86k | } |
70 | | |
71 | | // packed header, rrect, start index. |
72 | 2.46k | const size_t sizeNeeded = sizeof(int32_t) + SkRRect::kSizeInMemory + sizeof(int32_t); |
73 | 2.46k | if (!storage) { |
74 | 1.23k | return sizeNeeded; |
75 | 1.23k | } |
76 | | |
77 | 1.23k | int firstDir = isCCW ? (int)SkPathFirstDirection::kCCW : (int)SkPathFirstDirection::kCW; |
78 | 1.23k | int32_t packed = (fFillType << kFillType_SerializationShift) | |
79 | 1.23k | (firstDir << kDirection_SerializationShift) | |
80 | 1.23k | (SerializationType::kRRect << kType_SerializationShift) | |
81 | 1.23k | kCurrent_Version; |
82 | | |
83 | 1.23k | SkWBuffer buffer(storage); |
84 | 1.23k | buffer.write32(packed); |
85 | 1.23k | SkRRectPriv::WriteToBuffer(rrect, &buffer); |
86 | 1.23k | buffer.write32(SkToS32(start)); |
87 | 1.23k | buffer.padToAlign4(); |
88 | 1.23k | SkASSERT(sizeNeeded == buffer.pos()); |
89 | 1.23k | return buffer.pos(); |
90 | 1.23k | } |
91 | | |
92 | 9.33k | size_t SkPath::writeToMemory(void* storage) const { |
93 | 9.33k | SkDEBUGCODE(this->validate();) |
94 | | |
95 | 9.33k | if (size_t bytes = this->writeToMemoryAsRRect(storage)) { |
96 | 2.46k | return bytes; |
97 | 2.46k | } |
98 | | |
99 | 6.86k | int32_t packed = (fFillType << kFillType_SerializationShift) | |
100 | 6.86k | (SerializationType::kGeneral << kType_SerializationShift) | |
101 | 6.86k | kCurrent_Version; |
102 | | |
103 | 6.86k | int32_t pts = fPathRef->countPoints(); |
104 | 6.86k | int32_t cnx = fPathRef->countWeights(); |
105 | 6.86k | int32_t vbs = fPathRef->countVerbs(); |
106 | | |
107 | 6.86k | SkSafeMath safe; |
108 | 6.86k | size_t size = 4 * sizeof(int32_t); |
109 | 6.86k | size = safe.add(size, safe.mul(pts, sizeof(SkPoint))); |
110 | 6.86k | size = safe.add(size, safe.mul(cnx, sizeof(SkScalar))); |
111 | 6.86k | size = safe.add(size, safe.mul(vbs, sizeof(uint8_t))); |
112 | 6.86k | size = safe.alignUp(size, 4); |
113 | 6.86k | if (!safe) { |
114 | 0 | return 0; |
115 | 0 | } |
116 | 6.86k | if (!storage) { |
117 | 3.43k | return size; |
118 | 3.43k | } |
119 | | |
120 | 3.43k | SkWBuffer buffer(storage); |
121 | 3.43k | buffer.write32(packed); |
122 | 3.43k | buffer.write32(pts); |
123 | 3.43k | buffer.write32(cnx); |
124 | 3.43k | buffer.write32(vbs); |
125 | 3.43k | buffer.write(fPathRef->points(), pts * sizeof(SkPoint)); |
126 | 3.43k | buffer.write(fPathRef->conicWeights(), cnx * sizeof(SkScalar)); |
127 | 3.43k | buffer.write(fPathRef->verbsBegin(), vbs * sizeof(uint8_t)); |
128 | 3.43k | buffer.padToAlign4(); |
129 | | |
130 | 3.43k | SkASSERT(buffer.pos() == size); |
131 | 3.43k | return size; |
132 | 3.43k | } |
133 | | |
134 | 0 | sk_sp<SkData> SkPath::serialize() const { |
135 | 0 | size_t size = this->writeToMemory(nullptr); |
136 | 0 | sk_sp<SkData> data = SkData::MakeUninitialized(size); |
137 | 0 | this->writeToMemory(data->writable_data()); |
138 | 0 | return data; |
139 | 0 | } |
140 | | |
141 | | ////////////////////////////////////////////////////////////////////////////////////////////////// |
142 | | // reading |
143 | | |
144 | 8.40k | size_t SkPath::readFromMemory(const void* storage, size_t length) { |
145 | 8.40k | SkRBuffer buffer(storage, length); |
146 | 8.40k | uint32_t packed; |
147 | 8.40k | if (!buffer.readU32(&packed)) { |
148 | 3 | return 0; |
149 | 3 | } |
150 | 8.39k | unsigned version = extract_version(packed); |
151 | 8.39k | if (version < kMin_Version || version > kCurrent_Version) { |
152 | 20 | return 0; |
153 | 20 | } |
154 | | |
155 | 8.37k | if (version == kJustPublicData_Version || version == kVerbsAreStoredForward_Version) { |
156 | 8.37k | return this->readFromMemory_EQ4Or5(storage, length); |
157 | 8.37k | } |
158 | 0 | return 0; |
159 | 0 | } |
160 | | |
161 | 4.44k | size_t SkPath::readAsRRect(const void* storage, size_t length) { |
162 | 4.44k | SkRBuffer buffer(storage, length); |
163 | 4.44k | uint32_t packed; |
164 | 4.44k | if (!buffer.readU32(&packed)) { |
165 | 0 | return 0; |
166 | 0 | } |
167 | | |
168 | 4.44k | SkASSERT(extract_serializationtype(packed) == SerializationType::kRRect); |
169 | | |
170 | 4.44k | uint8_t dir = (packed >> kDirection_SerializationShift) & 0x3; |
171 | 4.44k | SkPathFillType fillType = extract_filltype(packed); |
172 | | |
173 | 4.44k | SkPathDirection rrectDir; |
174 | 4.44k | SkRRect rrect; |
175 | 4.44k | int32_t start; |
176 | 4.44k | switch (dir) { |
177 | 2.37k | case (int)SkPathFirstDirection::kCW: |
178 | 2.37k | rrectDir = SkPathDirection::kCW; |
179 | 2.37k | break; |
180 | 2.07k | case (int)SkPathFirstDirection::kCCW: |
181 | 2.07k | rrectDir = SkPathDirection::kCCW; |
182 | 2.07k | break; |
183 | 1 | default: |
184 | 1 | return 0; |
185 | 4.44k | } |
186 | 4.44k | if (!SkRRectPriv::ReadFromBuffer(&buffer, &rrect)) { |
187 | 40 | return 0; |
188 | 40 | } |
189 | 4.40k | if (!buffer.readS32(&start) || start != SkTPin(start, 0, 7)) { |
190 | 161 | return 0; |
191 | 161 | } |
192 | 4.24k | this->reset(); |
193 | 4.24k | this->addRRect(rrect, rrectDir, SkToUInt(start)); |
194 | 4.24k | this->setFillType(fillType); |
195 | 4.24k | buffer.skipToAlign4(); |
196 | 4.24k | return buffer.pos(); |
197 | 4.24k | } |
198 | | |
199 | 8.37k | size_t SkPath::readFromMemory_EQ4Or5(const void* storage, size_t length) { |
200 | 8.37k | SkRBuffer buffer(storage, length); |
201 | 8.37k | uint32_t packed; |
202 | 8.37k | if (!buffer.readU32(&packed)) { |
203 | 0 | return 0; |
204 | 0 | } |
205 | | |
206 | 8.37k | bool verbsAreReversed = true; |
207 | 8.37k | if (extract_version(packed) == kVerbsAreStoredForward_Version) { |
208 | 161 | verbsAreReversed = false; |
209 | 161 | } |
210 | | |
211 | 8.37k | switch (extract_serializationtype(packed)) { |
212 | 4.44k | case SerializationType::kRRect: |
213 | 4.44k | return this->readAsRRect(storage, length); |
214 | 3.92k | case SerializationType::kGeneral: |
215 | 3.92k | break; // fall out |
216 | 5 | default: |
217 | 5 | return 0; |
218 | 3.92k | } |
219 | | |
220 | 3.92k | int32_t pts, cnx, vbs; |
221 | 3.92k | if (!buffer.readS32(&pts) || !buffer.readS32(&cnx) || !buffer.readS32(&vbs)) { |
222 | 8 | return 0; |
223 | 8 | } |
224 | | |
225 | 3.91k | const SkPoint* points = buffer.skipCount<SkPoint>(pts); |
226 | 3.91k | const SkScalar* conics = buffer.skipCount<SkScalar>(cnx); |
227 | 3.91k | const uint8_t* verbs = buffer.skipCount<uint8_t>(vbs); |
228 | 3.91k | buffer.skipToAlign4(); |
229 | 3.91k | if (!buffer.isValid()) { |
230 | 114 | return 0; |
231 | 114 | } |
232 | 3.80k | SkASSERT(buffer.pos() <= length); |
233 | | |
234 | 3.80k | #define CHECK_POINTS_CONICS(p, c) \ |
235 | 28.7k | do { \ |
236 | 28.7k | if (p && ((pts -= p) < 0)) { \ |
237 | 46 | return 0; \ |
238 | 46 | } \ |
239 | 28.6k | if (c && ((cnx -= c) < 0)) { \ |
240 | 2 | return 0; \ |
241 | 2 | } \ |
242 | 28.6k | } while (0) |
243 | | |
244 | 3.80k | int verbsStep = 1; |
245 | 3.80k | if (verbsAreReversed) { |
246 | 3.68k | verbs += vbs - 1; |
247 | 3.68k | verbsStep = -1; |
248 | 3.68k | } |
249 | | |
250 | 3.80k | SkPath tmp; |
251 | 3.80k | tmp.setFillType(extract_filltype(packed)); |
252 | 3.80k | tmp.incReserve(pts); |
253 | 49.4k | for (int i = 0; i < vbs; ++i) { |
254 | 45.7k | switch (*verbs) { |
255 | 4.11k | case kMove_Verb: |
256 | 4.11k | CHECK_POINTS_CONICS(1, 0); |
257 | 4.10k | tmp.moveTo(*points++); |
258 | 4.10k | break; |
259 | 11.0k | case kLine_Verb: |
260 | 11.0k | CHECK_POINTS_CONICS(1, 0); |
261 | 11.0k | tmp.lineTo(*points++); |
262 | 11.0k | break; |
263 | 1.27k | case kQuad_Verb: |
264 | 1.27k | CHECK_POINTS_CONICS(2, 0); |
265 | 1.26k | tmp.quadTo(points[0], points[1]); |
266 | 1.26k | points += 2; |
267 | 1.26k | break; |
268 | 3.68k | case kConic_Verb: |
269 | 3.68k | CHECK_POINTS_CONICS(2, 1); |
270 | 3.66k | tmp.conicTo(points[0], points[1], *conics++); |
271 | 3.66k | points += 2; |
272 | 3.66k | break; |
273 | 8.63k | case kCubic_Verb: |
274 | 8.63k | CHECK_POINTS_CONICS(3, 0); |
275 | 8.63k | tmp.cubicTo(points[0], points[1], points[2]); |
276 | 8.63k | points += 3; |
277 | 8.63k | break; |
278 | 16.9k | case kClose_Verb: |
279 | 16.9k | tmp.close(); |
280 | 16.9k | break; |
281 | 38 | default: |
282 | 38 | return 0; // bad verb |
283 | 45.6k | } |
284 | 45.6k | verbs += verbsStep; |
285 | 45.6k | } |
286 | 3.80k | #undef CHECK_POINTS_CONICS |
287 | 3.71k | if (pts || cnx) { |
288 | 35 | return 0; // leftover points and/or conics |
289 | 35 | } |
290 | | |
291 | 3.68k | *this = std::move(tmp); |
292 | 3.68k | return buffer.pos(); |
293 | 3.68k | } |