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