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