blob: 73da28454ce0214a4da2ee5bfc4e03b4c7c207bf [file]
/*
* 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 "include/core/SkPicture.h"
#include "include/core/SkBBHFactory.h"
#include "include/core/SkData.h"
#include "include/core/SkPictureRecorder.h"
#include "include/core/SkSerialProcs.h"
#include "include/core/SkStream.h"
#include "include/private/SkTFitsIn.h"
#include "include/private/SkTo.h"
#include "src/core/SkCanvasPriv.h"
#include "src/core/SkMathPriv.h"
#include "src/core/SkPictureData.h"
#include "src/core/SkPicturePlayback.h"
#include "src/core/SkPicturePriv.h"
#include "src/core/SkPictureRecord.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkRecord.h"
#include "src/core/SkRecordDraw.h"
#include "src/core/SkRecords.h"
#include "src/core/SkResourceCache.h"
#include "src/core/SkStreamPriv.h"
#include "src/core/SkWriteBuffer.h"
#include <atomic>
#include <cstring>
#include <memory>
// 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
};
/* This handles generating unique IDs */
static uint32_t next_picture_id() {
static std::atomic<uint32_t> nextID{1};
uint32_t id;
do {
id = nextID.fetch_add(+1, std::memory_order_relaxed);
} while (id == 0);
return id;
}
SkPicture::SkPicture(const SkRect& cull,
sk_sp<const SkRecord> record,
std::unique_ptr<const SkSnapshotArray> drawablePicts,
sk_sp<const SkBBoxHierarchy> bbh,
size_t approxBytesUsedBySubPictures)
: fUniqueID(next_picture_id())
, fCullRect(cull)
, fApproxBytesUsedBySubPictures(approxBytesUsedBySubPictures)
, fRecord(std::move(record))
, fDrawablePicts(std::move(drawablePicts))
, fBBH(std::move(bbh)) {}
SkPicture::~SkPicture() {
if (fAddedToCache.load()) {
SkResourceCache::PostPurgeSharedID(SkPicturePriv::MakeSharedID(fUniqueID));
}
}
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(SkPicturePriv::kCurrent_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() < SkPicturePriv::kMin_Version ||
info.getVersion() > SkPicturePriv::kCurrent_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 (pInfo) {
*pInfo = info;
}
return IsValidPictInfo(info);
}
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 (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 MakeFromStreamPriv(stream, procs, nullptr, SkPicturePriv::kDefaultRecursionLimit);
}
sk_sp<SkPicture> SkPicture::MakeFromData(const void* data, size_t size,
const SkDeserialProcs* procs) {
if (!data) {
return nullptr;
}
SkMemoryStream stream(data, size);
return MakeFromStreamPriv(&stream, procs, nullptr, SkPicturePriv::kDefaultRecursionLimit);
}
sk_sp<SkPicture> SkPicture::MakeFromData(const SkData* data, const SkDeserialProcs* procs) {
if (!data) {
return nullptr;
}
SkMemoryStream stream(data->data(), data->size());
return MakeFromStreamPriv(&stream, procs, nullptr, SkPicturePriv::kDefaultRecursionLimit);
}
sk_sp<SkPicture> SkPicture::MakeFromStreamPriv(SkStream* stream, const SkDeserialProcs* procsPtr,
SkTypefacePlayback* typefaces, int recursionLimit) {
if (recursionLimit <= 0) {
return nullptr;
}
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,
recursionLimit));
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);
if (SkStreamPriv::RemainingLengthIsBelow(stream, size)) {
return nullptr;
}
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 out to error return
break;
}
return nullptr;
}
sk_sp<SkPicture> SkPicturePriv::MakeFromBuffer(SkReadBuffer& buffer) {
if (!buffer.isValid()) {
return nullptr;
}
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(info.fCullRect.roundOut(), 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<const 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;
}
// Private serialize.
// SkPictureData::serialize makes a first pass on all subpictures, indicated by textBlobsOnly=true,
// to fill typefaceSet.
void SkPicture::serialize(SkWStream* stream, const SkSerialProcs* procsPtr,
SkRefCntSet* typefaceSet, bool textBlobsOnly) 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, textBlobsOnly);
} 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.serialProcs())) {
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> SkPicturePriv::MakeEmptyPicture() {
return SkPicturePriv::MakePicture(
SkRect::MakeEmpty(), sk_make_sp<SkRecord>(), nullptr, nullptr, 0);
}
sk_sp<SkPicture> SkPicturePriv::MakePicture(const SkRect& cull,
sk_sp<const SkRecord> record,
std::unique_ptr<const SkSnapshotArray> drawablePicts,
sk_sp<const SkBBoxHierarchy> bbh,
size_t approxBytesUsedBySubPictures) {
return sk_sp<SkPicture>(new SkPicture(cull,
std::move(record),
std::move(drawablePicts),
std::move(bbh),
approxBytesUsedBySubPictures));
}
void SkPicture::playback(SkCanvas* canvas, AbortCallback* callback) const {
SkASSERT(canvas);
if (this->isPlaceholder()) {
return;
}
const bool useBBH = !canvas->getLocalClipBounds().contains(this->cullRect());
SkRecordDraw(*fRecord,
canvas,
fDrawablePicts ? fDrawablePicts->begin() : nullptr,
nullptr,
fDrawablePicts ? fDrawablePicts->count() : 0,
useBBH ? fBBH.get() : nullptr,
callback);
}
struct NestedApproxOpCounter {
int fCount = 0;
template <typename T> void operator()(const T& op) { fCount += 1; }
void operator()(const SkRecords::DrawPicture& op) {
fCount += op.picture->approximateOpCount(true);
}
};
int SkPicture::approximateOpCount(bool nested) const {
if (this->isPlaceholder()) {
// approximateOpCount() needs to be greater than kMaxPictureOpsToUnrollInsteadOfRef
// (SkCanvasPriv.h) to avoid unrolling this into a parent picture.
return kMaxPictureOpsToUnrollInsteadOfRef + 1;
}
if (nested) {
NestedApproxOpCounter visitor;
for (int i = 0; i < fRecord->count(); i++) {
fRecord->visit(i, visitor);
}
return visitor.fCount;
} else {
return fRecord->count();
}
}
size_t SkPicture::approximateBytesUsed() const {
if (this->isPlaceholder()) {
return sizeof(*this);
}
size_t bytes = sizeof(*this) + fRecord->bytesUsed() + fApproxBytesUsedBySubPictures;
if (fBBH) {
bytes += fBBH->bytesUsed();
}
return bytes;
}
sk_sp<SkPicture> SkPicture::MakePlaceholder(SkRect cull) {
return SkPicturePriv::MakePicture(cull, nullptr, nullptr, nullptr, 0);
}