| /* | |
| * 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/SkData.h" | |
| #include "include/core/SkPictureRecorder.h" | |
| #include "include/core/SkSerialProcs.h" | |
| #include "include/core/SkStream.h" | |
| #include "include/private/base/SkTFitsIn.h" | |
| #include "include/private/base/SkTo.h" | |
| #include "src/base/SkMathPriv.h" | |
| #include "src/core/SkCanvasPriv.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/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 | |
| }; | |
| /* 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); | |
| } | |
| 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(); | |
| } | |
| static const int kNestedSKPLimit = 100; // Arbitrarily set | |
| sk_sp<SkPicture> SkPicture::MakeFromStream(SkStream* stream, const SkDeserialProcs* procs) { | |
| return MakeFromStreamPriv(stream, procs, nullptr, kNestedSKPLimit); | |
| } | |
| 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, kNestedSKPLimit); | |
| } | |
| 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, kNestedSKPLimit); | |
| } | |
| 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 (StreamRemainingLengthIsBelow(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) { | |
| 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<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> 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 | |
| // (SkCanvasPriv.h) to avoid unrolling this into a parent picture. | |
| int approximateOpCount(bool) const override { | |
| return kMaxPictureOpsToUnrollInsteadOfRef+1; | |
| } | |
| size_t approximateBytesUsed() const override { return sizeof(*this); } | |
| SkRect cullRect() const override { return fCull; } | |
| SkRect fCull; | |
| }; | |
| return sk_make_sp<Placeholder>(cull); | |
| } |