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