diff options
Diffstat (limited to 'gfx/skia/skia/src/utils/SkMultiPictureDocument.cpp')
-rw-r--r-- | gfx/skia/skia/src/utils/SkMultiPictureDocument.cpp | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/utils/SkMultiPictureDocument.cpp b/gfx/skia/skia/src/utils/SkMultiPictureDocument.cpp new file mode 100644 index 0000000000..39fc8ab90e --- /dev/null +++ b/gfx/skia/skia/src/utils/SkMultiPictureDocument.cpp @@ -0,0 +1,224 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "src/utils/SkMultiPictureDocument.h" + +#include "include/core/SkCanvas.h" +#include "include/core/SkData.h" +#include "include/core/SkDocument.h" +#include "include/core/SkPicture.h" +#include "include/core/SkPictureRecorder.h" +#include "include/core/SkRect.h" +#include "include/core/SkScalar.h" +#include "include/core/SkSerialProcs.h" +#include "include/core/SkStream.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkTArray.h" +#include "include/private/base/SkTo.h" +#include "include/utils/SkNWayCanvas.h" +#include "src/utils/SkMultiPictureDocumentPriv.h" + +#include <algorithm> +#include <climits> +#include <cstdint> +#include <cstring> +#include <functional> + +/* + File format: + BEGINNING_OF_FILE: + kMagic + uint32_t version_number (==2) + uint32_t page_count + { + float sizeX + float sizeY + } * page_count + skp file +*/ + +namespace { +// The unique file signature for this file type. +static constexpr char kMagic[] = "Skia Multi-Picture Doc\n\n"; + +static constexpr char kEndPage[] = "SkMultiPictureEndPage"; + +const uint32_t kVersion = 2; + +static SkSize join(const SkTArray<SkSize>& sizes) { + SkSize joined = {0, 0}; + for (SkSize s : sizes) { + joined = SkSize{std::max(joined.width(), s.width()), std::max(joined.height(), s.height())}; + } + return joined; +} + +struct MultiPictureDocument final : public SkDocument { + const SkSerialProcs fProcs; + SkPictureRecorder fPictureRecorder; + SkSize fCurrentPageSize; + SkTArray<sk_sp<SkPicture>> fPages; + SkTArray<SkSize> fSizes; + std::function<void(const SkPicture*)> fOnEndPage; + MultiPictureDocument(SkWStream* s, const SkSerialProcs* procs, + std::function<void(const SkPicture*)> onEndPage) + : SkDocument(s) + , fProcs(procs ? *procs : SkSerialProcs()) + , fOnEndPage(onEndPage) + {} + ~MultiPictureDocument() override { this->close(); } + + SkCanvas* onBeginPage(SkScalar w, SkScalar h) override { + fCurrentPageSize.set(w, h); + return fPictureRecorder.beginRecording(w, h); + } + void onEndPage() override { + fSizes.push_back(fCurrentPageSize); + sk_sp<SkPicture> lastPage = fPictureRecorder.finishRecordingAsPicture(); + fPages.push_back(lastPage); + if (fOnEndPage) { + fOnEndPage(lastPage.get()); + } + } + void onClose(SkWStream* wStream) override { + SkASSERT(wStream); + SkASSERT(wStream->bytesWritten() == 0); + wStream->writeText(kMagic); + wStream->write32(kVersion); + wStream->write32(SkToU32(fPages.size())); + for (SkSize s : fSizes) { + wStream->write(&s, sizeof(s)); + } + SkSize bigsize = join(fSizes); + SkCanvas* c = fPictureRecorder.beginRecording(SkRect::MakeSize(bigsize)); + for (const sk_sp<SkPicture>& page : fPages) { + c->drawPicture(page); + // Annotations must include some data. + c->drawAnnotation(SkRect::MakeEmpty(), kEndPage, SkData::MakeWithCString("X")); + } + sk_sp<SkPicture> p = fPictureRecorder.finishRecordingAsPicture(); + p->serialize(wStream, &fProcs); + fPages.clear(); + fSizes.clear(); + return; + } + void onAbort() override { + fPages.clear(); + fSizes.clear(); + } +}; +} // namespace + +sk_sp<SkDocument> SkMakeMultiPictureDocument(SkWStream* wStream, const SkSerialProcs* procs, + std::function<void(const SkPicture*)> onEndPage) { + return sk_make_sp<MultiPictureDocument>(wStream, procs, onEndPage); +} + +//////////////////////////////////////////////////////////////////////////////// + +int SkMultiPictureDocumentReadPageCount(SkStreamSeekable* stream) { + if (!stream) { + return 0; + } + stream->seek(0); + const size_t size = sizeof(kMagic) - 1; + char buffer[size]; + if (size != stream->read(buffer, size) || 0 != memcmp(kMagic, buffer, size)) { + stream = nullptr; + return 0; + } + uint32_t versionNumber; + if (!stream->readU32(&versionNumber) || versionNumber != kVersion) { + return 0; + } + uint32_t pageCount; + if (!stream->readU32(&pageCount) || pageCount > INT_MAX) { + return 0; + } + // leave stream position right here. + return SkTo<int>(pageCount); +} + +bool SkMultiPictureDocumentReadPageSizes(SkStreamSeekable* stream, + SkDocumentPage* dstArray, + int dstArrayCount) { + if (!dstArray || dstArrayCount < 1) { + return false; + } + int pageCount = SkMultiPictureDocumentReadPageCount(stream); + if (pageCount < 1 || pageCount != dstArrayCount) { + return false; + } + for (int i = 0; i < pageCount; ++i) { + SkSize& s = dstArray[i].fSize; + if (sizeof(s) != stream->read(&s, sizeof(s))) { + return false; + } + } + // leave stream position right here. + return true; +} + +namespace { +struct PagerCanvas : public SkNWayCanvas { + SkPictureRecorder fRecorder; + SkDocumentPage* fDst; + int fCount; + int fIndex = 0; + PagerCanvas(SkISize wh, SkDocumentPage* dst, int count) + : SkNWayCanvas(wh.width(), wh.height()), fDst(dst), fCount(count) { + this->nextCanvas(); + } + void nextCanvas() { + if (fIndex < fCount) { + SkRect bounds = SkRect::MakeSize(fDst[fIndex].fSize); + this->addCanvas(fRecorder.beginRecording(bounds)); + } + } + void onDrawAnnotation(const SkRect& r, const char* key, SkData* d) override { + if (0 == strcmp(key, kEndPage)) { + this->removeAll(); + if (fIndex < fCount) { + fDst[fIndex].fPicture = fRecorder.finishRecordingAsPicture(); + ++fIndex; + } + this->nextCanvas(); + } else { + this->SkNWayCanvas::onDrawAnnotation(r, key, d); + } + } +}; +} // namespace + +bool SkMultiPictureDocumentRead(SkStreamSeekable* stream, + SkDocumentPage* dstArray, + int dstArrayCount, + const SkDeserialProcs* procs) { + if (!SkMultiPictureDocumentReadPageSizes(stream, dstArray, dstArrayCount)) { + return false; + } + SkSize joined = {0.0f, 0.0f}; + for (int i = 0; i < dstArrayCount; ++i) { + joined = SkSize{std::max(joined.width(), dstArray[i].fSize.width()), + std::max(joined.height(), dstArray[i].fSize.height())}; + } + + auto picture = SkPicture::MakeFromStream(stream, procs); + if (!picture) { + return false; + } + + PagerCanvas canvas(joined.toCeil(), dstArray, dstArrayCount); + // Must call playback(), not drawPicture() to reach + // PagerCanvas::onDrawAnnotation(). + picture->playback(&canvas); + if (canvas.fIndex != dstArrayCount) { + SkDEBUGF("Malformed SkMultiPictureDocument: canvas.fIndex=%d dstArrayCount=%d\n", + canvas.fIndex, dstArrayCount); + } + return true; +} |