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 
34 namespace {
35 // The unique file signature for this file type.
36 static constexpr char kMagic[] = "Skia Multi-Picture Doc\n\n";
37 
38 static constexpr char kEndPage[] = "SkMultiPictureEndPage";
39 
40 const uint32_t kVersion = 2;
41 
join(const SkTArray<SkSize> & sizes)42 static SkSize join(const SkTArray<SkSize>& sizes) {
43     SkSize joined = {0, 0};
44     for (SkSize s : sizes) {
45         joined = SkSize{SkTMax(joined.width(), s.width()), SkTMax(joined.height(), s.height())};
46     }
47     return joined;
48 }
49 
50 struct MultiPictureDocument final : public SkDocument {
51     const SkSerialProcs fProcs;
52     SkPictureRecorder fPictureRecorder;
53     SkSize fCurrentPageSize;
54     SkTArray<sk_sp<SkPicture>> fPages;
55     SkTArray<SkSize> fSizes;
MultiPictureDocument__anond539ca2d0111::MultiPictureDocument56     MultiPictureDocument(SkWStream* s, const SkSerialProcs* procs)
57         : SkDocument(s)
58         , fProcs(procs ? *procs : SkSerialProcs())
59     {}
~MultiPictureDocument__anond539ca2d0111::MultiPictureDocument60     ~MultiPictureDocument() override { this->close(); }
61 
onBeginPage__anond539ca2d0111::MultiPictureDocument62     SkCanvas* onBeginPage(SkScalar w, SkScalar h) override {
63         fCurrentPageSize.set(w, h);
64         return fPictureRecorder.beginRecording(w, h);
65     }
onEndPage__anond539ca2d0111::MultiPictureDocument66     void onEndPage() override {
67         fSizes.push_back(fCurrentPageSize);
68         fPages.push_back(fPictureRecorder.finishRecordingAsPicture());
69     }
onClose__anond539ca2d0111::MultiPictureDocument70     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     }
onAbort__anond539ca2d0111::MultiPictureDocument92     void onAbort() override {
93         fPages.reset();
94         fSizes.reset();
95     }
96 };
97 }
98 
SkMakeMultiPictureDocument(SkWStream * wStream,const SkSerialProcs * procs)99 sk_sp<SkDocument> SkMakeMultiPictureDocument(SkWStream* wStream, const SkSerialProcs* procs) {
100     return sk_make_sp<MultiPictureDocument>(wStream, procs);
101 }
102 
103 ////////////////////////////////////////////////////////////////////////////////
104 
SkMultiPictureDocumentReadPageCount(SkStreamSeekable * stream)105 int 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 
SkMultiPictureDocumentReadPageSizes(SkStreamSeekable * stream,SkDocumentPage * dstArray,int dstArrayCount)128 bool 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 
148 namespace {
149 struct PagerCanvas : public SkNWayCanvas {
150     SkPictureRecorder fRecorder;
151     SkDocumentPage* fDst;
152     int fCount;
153     int fIndex = 0;
PagerCanvas__anond539ca2d0211::PagerCanvas154     PagerCanvas(SkISize wh, SkDocumentPage* dst, int count)
155             : SkNWayCanvas(wh.width(), wh.height()), fDst(dst), fCount(count) {
156         this->nextCanvas();
157     }
nextCanvas__anond539ca2d0211::PagerCanvas158     void nextCanvas() {
159         if (fIndex < fCount) {
160             SkRect bounds = SkRect::MakeSize(fDst[fIndex].fSize);
161             this->addCanvas(fRecorder.beginRecording(bounds));
162         }
163     }
onDrawAnnotation__anond539ca2d0211::PagerCanvas164     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 
SkMultiPictureDocumentRead(SkStreamSeekable * stream,SkDocumentPage * dstArray,int dstArrayCount,const SkDeserialProcs * procs)179 bool 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{SkTMax(joined.width(), dstArray[i].fSize.width()),
189                         SkTMax(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