| /* | |
| * 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; | |
| int fDepth = 0; | |
| const SkSnapshotArray* fDrawablePicts; | |
| NestedApproxOpCounter(const SkSnapshotArray* drawablePicts, int depth) | |
| : fDepth(depth), fDrawablePicts(drawablePicts) {} | |
| template <typename T> void operator()(const T& op) { fCount++; } | |
| void operator()(const SkRecords::DrawPicture& op) { | |
| fCount += 1 + SkPicturePriv::ApproximateOpCount(op.picture.get(), fDepth + 1, true); | |
| } | |
| void operator()(const SkRecords::DrawDrawable& op) { | |
| // Adding one for the DrawDrawable itself at the beginning | |
| fCount++; | |
| if (!fDrawablePicts) return; | |
| if (0 <= op.index && op.index < fDrawablePicts->count()) { | |
| fCount += SkPicturePriv::ApproximateOpCount( | |
| fDrawablePicts->at(op.index), fDepth + 1, true); | |
| } | |
| } | |
| }; | |
| int SkPicturePriv::ApproximateOpCount(const SkPicture* pic, int depth, bool nested) { | |
| if (!pic || depth > SkPicturePriv::kDefaultRecursionLimit) { | |
| return 0; | |
| } | |
| if (pic->isPlaceholder()) { | |
| // approximateOpCount() needs to be greater than kMaxPictureOpsToUnrollInsteadOfRef | |
| // (SkCanvasPriv.h) to avoid unrolling this into a parent picture. | |
| return kMaxPictureOpsToUnrollInsteadOfRef + 1; | |
| } | |
| if (!nested) { | |
| return pic->fRecord->count(); | |
| } | |
| NestedApproxOpCounter visitor(pic->fDrawablePicts.get(), depth); | |
| for (int i = 0; i < pic->fRecord->count(); i++) { | |
| pic->fRecord->visit(i, visitor); | |
| } | |
| return visitor.fCount; | |
| } | |
| int SkPicture::approximateOpCount(bool nested) const { | |
| return SkPicturePriv::ApproximateOpCount(this, 0, nested); | |
| } | |
| 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); | |
| } |