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