/*
|
* 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 "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"
|
#include <atomic>
|
|
// 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() {
|
static std::atomic<uint32_t> nextID{1};
|
do {
|
fUniqueID = nextID.fetch_add(+1, std::memory_order_relaxed);
|
} while (fUniqueID == 0);
|
}
|
|
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.getDeserialProcs();
|
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);
|
}
|