1/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "src/utils/SkMultiPictureDocument.h"
9
10#include "include/core/SkPicture.h"
11#include "include/core/SkPictureRecorder.h"
12#include "include/core/SkSerialProcs.h"
13#include "include/core/SkStream.h"
14#include "include/private/SkTArray.h"
15#include "include/private/SkTo.h"
16#include "include/utils/SkNWayCanvas.h"
17#include "src/utils/SkMultiPictureDocumentPriv.h"
18
19#include <limits.h>
20
21/*
22 File format:
23 BEGINNING_OF_FILE:
24 kMagic
25 uint32_t version_number (==2)
26 uint32_t page_count
27 {
28 float sizeX
29 float sizeY
30 } * page_count
31 skp file
32*/
33
34namespace {
35// The unique file signature for this file type.
36static constexpr char kMagic[] = "Skia Multi-Picture Doc\n\n";
37
38static constexpr char kEndPage[] = "SkMultiPictureEndPage";
39
40const uint32_t kVersion = 2;
41
42static SkSize join(const SkTArray<SkSize>& sizes) {
43 SkSize joined = {0, 0};
44 for (SkSize s : sizes) {
45 joined = SkSize{std::max(joined.width(), s.width()), std::max(joined.height(), s.height())};
46 }
47 return joined;
48}
49
50struct MultiPictureDocument final : public SkDocument {
51 const SkSerialProcs fProcs;
52 SkPictureRecorder fPictureRecorder;
53 SkSize fCurrentPageSize;
54 SkTArray<sk_sp<SkPicture>> fPages;
55 SkTArray<SkSize> fSizes;
56 MultiPictureDocument(SkWStream* s, const SkSerialProcs* procs)
57 : SkDocument(s)
58 , fProcs(procs ? *procs : SkSerialProcs())
59 {}
60 ~MultiPictureDocument() override { this->close(); }
61
62 SkCanvas* onBeginPage(SkScalar w, SkScalar h) override {
63 fCurrentPageSize.set(w, h);
64 return fPictureRecorder.beginRecording(w, h);
65 }
66 void onEndPage() override {
67 fSizes.push_back(fCurrentPageSize);
68 fPages.push_back(fPictureRecorder.finishRecordingAsPicture());
69 }
70 void onClose(SkWStream* wStream) override {
71 SkASSERT(wStream);
72 SkASSERT(wStream->bytesWritten() == 0);
73 wStream->writeText(kMagic);
74 wStream->write32(kVersion);
75 wStream->write32(SkToU32(fPages.count()));
76 for (SkSize s : fSizes) {
77 wStream->write(&s, sizeof(s));
78 }
79 SkSize bigsize = join(fSizes);
80 SkCanvas* c = fPictureRecorder.beginRecording(SkRect::MakeSize(bigsize));
81 for (const sk_sp<SkPicture>& page : fPages) {
82 c->drawPicture(page);
83 // Annotations must include some data.
84 c->drawAnnotation(SkRect::MakeEmpty(), kEndPage, SkData::MakeWithCString("X"));
85 }
86 sk_sp<SkPicture> p = fPictureRecorder.finishRecordingAsPicture();
87 p->serialize(wStream, &fProcs);
88 fPages.reset();
89 fSizes.reset();
90 return;
91 }
92 void onAbort() override {
93 fPages.reset();
94 fSizes.reset();
95 }
96};
97}
98
99sk_sp<SkDocument> SkMakeMultiPictureDocument(SkWStream* wStream, const SkSerialProcs* procs) {
100 return sk_make_sp<MultiPictureDocument>(wStream, procs);
101}
102
103////////////////////////////////////////////////////////////////////////////////
104
105int SkMultiPictureDocumentReadPageCount(SkStreamSeekable* stream) {
106 if (!stream) {
107 return 0;
108 }
109 stream->seek(0);
110 const size_t size = sizeof(kMagic) - 1;
111 char buffer[size];
112 if (size != stream->read(buffer, size) || 0 != memcmp(kMagic, buffer, size)) {
113 stream = nullptr;
114 return 0;
115 }
116 uint32_t versionNumber;
117 if (!stream->readU32(&versionNumber) || versionNumber != kVersion) {
118 return 0;
119 }
120 uint32_t pageCount;
121 if (!stream->readU32(&pageCount) || pageCount > INT_MAX) {
122 return 0;
123 }
124 // leave stream position right here.
125 return SkTo<int>(pageCount);
126}
127
128bool SkMultiPictureDocumentReadPageSizes(SkStreamSeekable* stream,
129 SkDocumentPage* dstArray,
130 int dstArrayCount) {
131 if (!dstArray || dstArrayCount < 1) {
132 return false;
133 }
134 int pageCount = SkMultiPictureDocumentReadPageCount(stream);
135 if (pageCount < 1 || pageCount != dstArrayCount) {
136 return false;
137 }
138 for (int i = 0; i < pageCount; ++i) {
139 SkSize& s = dstArray[i].fSize;
140 if (sizeof(s) != stream->read(&s, sizeof(s))) {
141 return false;
142 }
143 }
144 // leave stream position right here.
145 return true;
146}
147
148namespace {
149struct PagerCanvas : public SkNWayCanvas {
150 SkPictureRecorder fRecorder;
151 SkDocumentPage* fDst;
152 int fCount;
153 int fIndex = 0;
154 PagerCanvas(SkISize wh, SkDocumentPage* dst, int count)
155 : SkNWayCanvas(wh.width(), wh.height()), fDst(dst), fCount(count) {
156 this->nextCanvas();
157 }
158 void nextCanvas() {
159 if (fIndex < fCount) {
160 SkRect bounds = SkRect::MakeSize(fDst[fIndex].fSize);
161 this->addCanvas(fRecorder.beginRecording(bounds));
162 }
163 }
164 void onDrawAnnotation(const SkRect& r, const char* key, SkData* d) override {
165 if (0 == strcmp(key, kEndPage)) {
166 this->removeAll();
167 if (fIndex < fCount) {
168 fDst[fIndex].fPicture = fRecorder.finishRecordingAsPicture();
169 ++fIndex;
170 }
171 this->nextCanvas();
172 } else {
173 this->SkNWayCanvas::onDrawAnnotation(r, key, d);
174 }
175 }
176};
177} // namespace
178
179bool SkMultiPictureDocumentRead(SkStreamSeekable* stream,
180 SkDocumentPage* dstArray,
181 int dstArrayCount,
182 const SkDeserialProcs* procs) {
183 if (!SkMultiPictureDocumentReadPageSizes(stream, dstArray, dstArrayCount)) {
184 return false;
185 }
186 SkSize joined = {0.0f, 0.0f};
187 for (int i = 0; i < dstArrayCount; ++i) {
188 joined = SkSize{std::max(joined.width(), dstArray[i].fSize.width()),
189 std::max(joined.height(), dstArray[i].fSize.height())};
190 }
191
192 auto picture = SkPicture::MakeFromStream(stream, procs);
193
194 PagerCanvas canvas(joined.toCeil(), dstArray, dstArrayCount);
195 // Must call playback(), not drawPicture() to reach
196 // PagerCanvas::onDrawAnnotation().
197 picture->playback(&canvas);
198 if (canvas.fIndex != dstArrayCount) {
199 SkDEBUGF("Malformed SkMultiPictureDocument: canvas.fIndex=%d dstArrayCount=%d\n",
200 canvas.fIndex, dstArrayCount);
201 }
202 return true;
203}
204