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