2 * Copyright 2016 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "src/utils/SkMultiPictureDocument.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkData.h"
12 #include "include/core/SkDocument.h"
13 #include "include/core/SkPicture.h"
14 #include "include/core/SkPictureRecorder.h"
15 #include "include/core/SkRect.h"
16 #include "include/core/SkScalar.h"
17 #include "include/core/SkSerialProcs.h"
18 #include "include/core/SkStream.h"
19 #include "include/private/SkTArray.h"
20 #include "include/private/SkTo.h"
21 #include "include/utils/SkNWayCanvas.h"
22 #include "src/utils/SkMultiPictureDocumentPriv.h"
33 uint32_t version_number (==2)
43 // The unique file signature for this file type.
44 static constexpr char kMagic[] = "Skia Multi-Picture Doc\n\n";
46 static constexpr char kEndPage[] = "SkMultiPictureEndPage";
48 const uint32_t kVersion = 2;
50 static SkSize join(const SkTArray<SkSize>& sizes) {
51 SkSize joined = {0, 0};
52 for (SkSize s : sizes) {
53 joined = SkSize{std::max(joined.width(), s.width()), std::max(joined.height(), s.height())};
58 struct MultiPictureDocument final : public SkDocument {
59 const SkSerialProcs fProcs;
60 SkPictureRecorder fPictureRecorder;
61 SkSize fCurrentPageSize;
62 SkTArray<sk_sp<SkPicture>> fPages;
63 SkTArray<SkSize> fSizes;
64 std::function<void(const SkPicture*)> fOnEndPage;
65 MultiPictureDocument(SkWStream* s, const SkSerialProcs* procs,
66 std::function<void(const SkPicture*)> onEndPage)
68 , fProcs(procs ? *procs : SkSerialProcs())
69 , fOnEndPage(onEndPage)
71 ~MultiPictureDocument() override { this->close(); }
73 SkCanvas* onBeginPage(SkScalar w, SkScalar h) override {
74 fCurrentPageSize.set(w, h);
75 return fPictureRecorder.beginRecording(w, h);
77 void onEndPage() override {
78 fSizes.push_back(fCurrentPageSize);
79 sk_sp<SkPicture> lastPage = fPictureRecorder.finishRecordingAsPicture();
80 fPages.push_back(lastPage);
82 fOnEndPage(lastPage.get());
85 void onClose(SkWStream* wStream) override {
87 SkASSERT(wStream->bytesWritten() == 0);
88 wStream->writeText(kMagic);
89 wStream->write32(kVersion);
90 wStream->write32(SkToU32(fPages.count()));
91 for (SkSize s : fSizes) {
92 wStream->write(&s, sizeof(s));
94 SkSize bigsize = join(fSizes);
95 SkCanvas* c = fPictureRecorder.beginRecording(SkRect::MakeSize(bigsize));
96 for (const sk_sp<SkPicture>& page : fPages) {
98 // Annotations must include some data.
99 c->drawAnnotation(SkRect::MakeEmpty(), kEndPage, SkData::MakeWithCString("X"));
101 sk_sp<SkPicture> p = fPictureRecorder.finishRecordingAsPicture();
102 p->serialize(wStream, &fProcs);
107 void onAbort() override {
114 sk_sp<SkDocument> SkMakeMultiPictureDocument(SkWStream* wStream, const SkSerialProcs* procs,
115 std::function<void(const SkPicture*)> onEndPage) {
116 return sk_make_sp<MultiPictureDocument>(wStream, procs, onEndPage);
119 ////////////////////////////////////////////////////////////////////////////////
121 int SkMultiPictureDocumentReadPageCount(SkStreamSeekable* stream) {
126 const size_t size = sizeof(kMagic) - 1;
128 if (size != stream->read(buffer, size) || 0 != memcmp(kMagic, buffer, size)) {
132 uint32_t versionNumber;
133 if (!stream->readU32(&versionNumber) || versionNumber != kVersion) {
137 if (!stream->readU32(&pageCount) || pageCount > INT_MAX) {
140 // leave stream position right here.
141 return SkTo<int>(pageCount);
144 bool SkMultiPictureDocumentReadPageSizes(SkStreamSeekable* stream,
145 SkDocumentPage* dstArray,
147 if (!dstArray || dstArrayCount < 1) {
150 int pageCount = SkMultiPictureDocumentReadPageCount(stream);
151 if (pageCount < 1 || pageCount != dstArrayCount) {
154 for (int i = 0; i < pageCount; ++i) {
155 SkSize& s = dstArray[i].fSize;
156 if (sizeof(s) != stream->read(&s, sizeof(s))) {
160 // leave stream position right here.
165 struct PagerCanvas : public SkNWayCanvas {
166 SkPictureRecorder fRecorder;
167 SkDocumentPage* fDst;
170 PagerCanvas(SkISize wh, SkDocumentPage* dst, int count)
171 : SkNWayCanvas(wh.width(), wh.height()), fDst(dst), fCount(count) {
175 if (fIndex < fCount) {
176 SkRect bounds = SkRect::MakeSize(fDst[fIndex].fSize);
177 this->addCanvas(fRecorder.beginRecording(bounds));
180 void onDrawAnnotation(const SkRect& r, const char* key, SkData* d) override {
181 if (0 == strcmp(key, kEndPage)) {
183 if (fIndex < fCount) {
184 fDst[fIndex].fPicture = fRecorder.finishRecordingAsPicture();
189 this->SkNWayCanvas::onDrawAnnotation(r, key, d);
195 bool SkMultiPictureDocumentRead(SkStreamSeekable* stream,
196 SkDocumentPage* dstArray,
198 const SkDeserialProcs* procs) {
199 if (!SkMultiPictureDocumentReadPageSizes(stream, dstArray, dstArrayCount)) {
202 SkSize joined = {0.0f, 0.0f};
203 for (int i = 0; i < dstArrayCount; ++i) {
204 joined = SkSize{std::max(joined.width(), dstArray[i].fSize.width()),
205 std::max(joined.height(), dstArray[i].fSize.height())};
208 auto picture = SkPicture::MakeFromStream(stream, procs);
213 PagerCanvas canvas(joined.toCeil(), dstArray, dstArrayCount);
214 // Must call playback(), not drawPicture() to reach
215 // PagerCanvas::onDrawAnnotation().
216 picture->playback(&canvas);
217 if (canvas.fIndex != dstArrayCount) {
218 SkDEBUGF("Malformed SkMultiPictureDocument: canvas.fIndex=%d dstArrayCount=%d\n",
219 canvas.fIndex, dstArrayCount);