• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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