aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkPicture.cpp
blob: ad8303154614edf207269dab75af78f6f8299202 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/*
 * Copyright 2007 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkPicture.h"

#include "SkAtomics.h"
#include "SkImageGenerator.h"
#include "SkMathPriv.h"
#include "SkPictureCommon.h"
#include "SkPictureData.h"
#include "SkPicturePlayback.h"
#include "SkPicturePriv.h"
#include "SkPictureRecord.h"
#include "SkPictureRecorder.h"
#include "SkSerialProcs.h"
#include "SkTo.h"

// When we read/write the SkPictInfo via a stream, we have a sentinel byte right after the info.
// Note: in the read/write buffer versions, we have a slightly different convention:
//      We have a sentinel int32_t:
//          0 : failure
//          1 : PictureData
//         <0 : -size of the custom data
enum {
    kFailure_TrailingStreamByteAfterPictInfo     = 0,   // nothing follows
    kPictureData_TrailingStreamByteAfterPictInfo = 1,   // SkPictureData follows
    kCustom_TrailingStreamByteAfterPictInfo      = 2,   // -size32 follows
};

/* SkPicture impl.  This handles generic responsibilities like unique IDs and serialization. */

SkPicture::SkPicture() : fUniqueID(0) {}

uint32_t SkPicture::uniqueID() const {
    static uint32_t gNextID = 1;
    uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed);
    while (id == 0) {
        uint32_t next = sk_atomic_fetch_add(&gNextID, 1u);
        if (sk_atomic_compare_exchange(&fUniqueID, &id, next,
                                       sk_memory_order_relaxed,
                                       sk_memory_order_relaxed)) {
            id = next;
        } else {
            // sk_atomic_compare_exchange replaced id with the current value of fUniqueID.
        }
    }
    return id;
}

static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };

SkPictInfo SkPicture::createHeader() const {
    SkPictInfo info;
    // Copy magic bytes at the beginning of the header
    static_assert(sizeof(kMagic) == 8, "");
    static_assert(sizeof(kMagic) == sizeof(info.fMagic), "");
    memcpy(info.fMagic, kMagic, sizeof(kMagic));

    // Set picture info after magic bytes in the header
    info.setVersion(CURRENT_PICTURE_VERSION);
    info.fCullRect = this->cullRect();
    return info;
}

bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
    if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
        return false;
    }
    if (info.getVersion() < MIN_PICTURE_VERSION || info.getVersion() > CURRENT_PICTURE_VERSION) {
        return false;
    }
    return true;
}

bool SkPicture::StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
    if (!stream) {
        return false;
    }

    SkPictInfo info;
    SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
    if (stream->read(&info.fMagic, sizeof(kMagic)) != sizeof(kMagic)) {
        return false;
    }

    uint32_t version;
    if (!stream->readU32(&version)) { return false; }
    info.setVersion(version);
    if (!stream->readScalar(&info.fCullRect.fLeft  )) { return false; }
    if (!stream->readScalar(&info.fCullRect.fTop   )) { return false; }
    if (!stream->readScalar(&info.fCullRect.fRight )) { return false; }
    if (!stream->readScalar(&info.fCullRect.fBottom)) { return false; }
    if (info.getVersion() < SkReadBuffer::kRemoveHeaderFlags_Version) {
        if (!stream->readU32(nullptr)) { return false; }
    }

    if (!IsValidPictInfo(info)) { return false; }

    if (pInfo) { *pInfo = info; }
    return true;
}
bool SkPicture_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
    return SkPicture::StreamIsSKP(stream, pInfo);
}

bool SkPicture::BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) {
    SkPictInfo info;
    SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
    if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) {
        return false;
    }

    info.setVersion(buffer->readUInt());
    buffer->readRect(&info.fCullRect);
    if (info.getVersion() < SkReadBuffer::kRemoveHeaderFlags_Version) {
        (void)buffer->readUInt();   // used to be flags
    }

    if (IsValidPictInfo(info)) {
        if (pInfo) { *pInfo = info; }
        return true;
    }
    return false;
}

sk_sp<SkPicture> SkPicture::Forwardport(const SkPictInfo& info,
                                        const SkPictureData* data,
                                        SkReadBuffer* buffer) {
    if (!data) {
        return nullptr;
    }
    if (!data->opData()) {
        return nullptr;
    }
    SkPicturePlayback playback(data);
    SkPictureRecorder r;
    playback.draw(r.beginRecording(info.fCullRect), nullptr/*no callback*/, buffer);
    return r.finishRecordingAsPicture();
}

sk_sp<SkPicture> SkPicture::MakeFromStream(SkStream* stream, const SkDeserialProcs* procs) {
    return MakeFromStream(stream, procs, nullptr);
}

sk_sp<SkPicture> SkPicture::MakeFromData(const void* data, size_t size,
                                         const SkDeserialProcs* procs) {
    if (!data) {
        return nullptr;
    }
    SkMemoryStream stream(data, size);
    return MakeFromStream(&stream, procs, nullptr);
}

sk_sp<SkPicture> SkPicture::MakeFromData(const SkData* data, const SkDeserialProcs* procs) {
    if (!data) {
        return nullptr;
    }
    SkMemoryStream stream(data->data(), data->size());
    return MakeFromStream(&stream, procs, nullptr);
}

sk_sp<SkPicture> SkPicture::MakeFromStream(SkStream* stream, const SkDeserialProcs* procsPtr,
                                           SkTypefacePlayback* typefaces) {
    SkPictInfo info;
    if (!StreamIsSKP(stream, &info)) {
        return nullptr;
    }

    SkDeserialProcs procs;
    if (procsPtr) {
        procs = *procsPtr;
    }

    uint8_t trailingStreamByteAfterPictInfo;
    if (!stream->readU8(&trailingStreamByteAfterPictInfo)) { return nullptr; }
    switch (trailingStreamByteAfterPictInfo) {
        case kPictureData_TrailingStreamByteAfterPictInfo: {
            std::unique_ptr<SkPictureData> data(
                    SkPictureData::CreateFromStream(stream, info, procs, typefaces));
            return Forwardport(info, data.get(), nullptr);
        }
        case kCustom_TrailingStreamByteAfterPictInfo: {
            int32_t ssize;
            if (!stream->readS32(&ssize) || ssize >= 0 || !procs.fPictureProc) {
                return nullptr;
            }
            size_t size = sk_negate_to_size_t(ssize);
            auto data = SkData::MakeUninitialized(size);
            if (stream->read(data->writable_data(), size) != size) {
                return nullptr;
            }
            return procs.fPictureProc(data->data(), size, procs.fPictureCtx);
        }
        default:    // fall through to error return
            break;
    }
    return nullptr;
}

sk_sp<SkPicture> SkPicturePriv::MakeFromBuffer(SkReadBuffer& buffer) {
    SkPictInfo info;
    if (!SkPicture::BufferIsSKP(&buffer, &info)) {
        return nullptr;
    }
    // size should be 0, 1, or negative
    int32_t ssize = buffer.read32();
    if (ssize < 0) {
        const SkDeserialProcs& procs = buffer.fProcs;
        if (!procs.fPictureProc) {
            return nullptr;
        }
        size_t size = sk_negate_to_size_t(ssize);
        return procs.fPictureProc(buffer.skip(size), size, procs.fPictureCtx);
    }
    if (ssize != 1) {
        // 1 is the magic 'size' that means SkPictureData follows
        return nullptr;
    }
   std::unique_ptr<SkPictureData> data(SkPictureData::CreateFromBuffer(buffer, info));
    return SkPicture::Forwardport(info, data.get(), &buffer);
}

SkPictureData* SkPicture::backport() const {
    SkPictInfo info = this->createHeader();
    SkPictureRecord rec(SkISize::Make(info.fCullRect.width(), info.fCullRect.height()), 0/*flags*/);
    rec.beginRecording();
        this->playback(&rec);
    rec.endRecording();
    return new SkPictureData(rec, info);
}

void SkPicture::serialize(SkWStream* stream, const SkSerialProcs* procs) const {
    this->serialize(stream, procs, nullptr);
}

sk_sp<SkData> SkPicture::serialize(const SkSerialProcs* procs) const {
    SkDynamicMemoryWStream stream;
    this->serialize(&stream, procs, nullptr);
    return stream.detachAsData();
}

static sk_sp<SkData> custom_serialize(const SkPicture* picture, const SkSerialProcs& procs) {
    if (procs.fPictureProc) {
        auto data = procs.fPictureProc(const_cast<SkPicture*>(picture), procs.fPictureCtx);
        if (data) {
            size_t size = data->size();
            if (!SkTFitsIn<int32_t>(size) || size <= 1) {
                return SkData::MakeEmpty();
            }
            return data;
        }
    }
    return nullptr;
}

static bool write_pad32(SkWStream* stream, const void* data, size_t size) {
    if (!stream->write(data, size)) {
        return false;
    }
    if (size & 3) {
        uint32_t zero = 0;
        return stream->write(&zero, 4 - (size & 3));
    }
    return true;
}

void SkPicture::serialize(SkWStream* stream, const SkSerialProcs* procsPtr,
                          SkRefCntSet* typefaceSet) const {
    SkSerialProcs procs;
    if (procsPtr) {
        procs = *procsPtr;
    }

    SkPictInfo info = this->createHeader();
    stream->write(&info, sizeof(info));

    if (auto custom = custom_serialize(this, procs)) {
        int32_t size = SkToS32(custom->size());
        if (size == 0) {
            stream->write8(kFailure_TrailingStreamByteAfterPictInfo);
            return;
        }
        stream->write8(kCustom_TrailingStreamByteAfterPictInfo);
        stream->write32(-size);    // negative for custom format
        write_pad32(stream, custom->data(), size);
        return;
    }

    std::unique_ptr<SkPictureData> data(this->backport());
    if (data) {
        stream->write8(kPictureData_TrailingStreamByteAfterPictInfo);
        data->serialize(stream, procs, typefaceSet);
    } else {
        stream->write8(kFailure_TrailingStreamByteAfterPictInfo);
    }
}

void SkPicturePriv::Flatten(const sk_sp<const SkPicture> picture, SkWriteBuffer& buffer) {
    SkPictInfo info = picture->createHeader();
    std::unique_ptr<SkPictureData> data(picture->backport());

    buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic));
    buffer.writeUInt(info.getVersion());
    buffer.writeRect(info.fCullRect);

    if (auto custom = custom_serialize(picture.get(), buffer.fProcs)) {
        int32_t size = SkToS32(custom->size());
        buffer.write32(-size);    // negative for custom format
        buffer.writePad32(custom->data(), size);
        return;
    }

    if (data) {
        buffer.write32(1); // special size meaning SkPictureData
        data->flatten(buffer);
    } else {
        buffer.write32(0); // signal no content
    }
}

sk_sp<SkPicture> SkPicture::MakePlaceholder(SkRect cull) {
    struct Placeholder : public SkPicture {
          explicit Placeholder(SkRect cull) : fCull(cull) {}

          void playback(SkCanvas*, AbortCallback*) const override { }

          // approximateOpCount() needs to be greater than kMaxPictureOpsToUnrollInsteadOfRef
          // in SkCanvas.cpp to avoid that unrolling.  SK_MaxS32 can't not be big enough!
          int    approximateOpCount()   const override { return SK_MaxS32; }
          size_t approximateBytesUsed() const override { return sizeof(*this); }
          SkRect cullRect()             const override { return fCull; }

          SkRect fCull;
    };
    return sk_make_sp<Placeholder>(cull);
}