xref: /aosp_15_r20/external/skia/src/utils/SkMultiPictureDocument.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 "include/docs/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 #include <utility>
31 
32 using namespace skia_private;
33 
34 /*
35   File format:
36       BEGINNING_OF_FILE:
37         kMagic
38         uint32_t version_number (==2)
39         uint32_t page_count
40         {
41           float sizeX
42           float sizeY
43         } * page_count
44         skp file
45 */
46 
47 namespace {
48 // The unique file signature for this file type.
49 static constexpr char kMagic[] = "Skia Multi-Picture Doc\n\n";
50 
51 static constexpr char kEndPage[] = "SkMultiPictureEndPage";
52 
53 const uint32_t kVersion = 2;
54 
join(const TArray<SkSize> & sizes)55 static SkSize join(const TArray<SkSize>& sizes) {
56     SkSize joined = {0, 0};
57     for (SkSize s : sizes) {
58         joined = SkSize{std::max(joined.width(), s.width()), std::max(joined.height(), s.height())};
59     }
60     return joined;
61 }
62 
63 struct MultiPictureDocument final : public SkDocument {
64     const SkSerialProcs fProcs;
65     SkPictureRecorder fPictureRecorder;
66     SkSize fCurrentPageSize;
67     TArray<sk_sp<SkPicture>> fPages;
68     TArray<SkSize> fSizes;
69     std::function<void(const SkPicture*)> fOnEndPage;
MultiPictureDocument__anon46d0710a0111::MultiPictureDocument70     MultiPictureDocument(SkWStream* s,
71                          const SkSerialProcs* procs,
72                          std::function<void(const SkPicture*)> onEndPage)
73             : SkDocument(s)
74             , fProcs(procs ? *procs : SkSerialProcs())
75             , fOnEndPage(std::move(onEndPage)) {}
76 
~MultiPictureDocument__anon46d0710a0111::MultiPictureDocument77     ~MultiPictureDocument() override { this->close(); }
78 
onBeginPage__anon46d0710a0111::MultiPictureDocument79     SkCanvas* onBeginPage(SkScalar w, SkScalar h) override {
80         fCurrentPageSize.set(w, h);
81         return fPictureRecorder.beginRecording(w, h);
82     }
onEndPage__anon46d0710a0111::MultiPictureDocument83     void onEndPage() override {
84         fSizes.push_back(fCurrentPageSize);
85         sk_sp<SkPicture> lastPage = fPictureRecorder.finishRecordingAsPicture();
86         fPages.push_back(lastPage);
87         if (fOnEndPage) {
88             fOnEndPage(lastPage.get());
89         }
90     }
onClose__anon46d0710a0111::MultiPictureDocument91     void onClose(SkWStream* wStream) override {
92         SkASSERT(wStream);
93         SkASSERT(wStream->bytesWritten() == 0);
94         wStream->writeText(kMagic);
95         wStream->write32(kVersion);
96         wStream->write32(SkToU32(fPages.size()));
97         for (SkSize s : fSizes) {
98             wStream->write(&s, sizeof(s));
99         }
100         SkSize bigsize = join(fSizes);
101         SkCanvas* c = fPictureRecorder.beginRecording(SkRect::MakeSize(bigsize));
102         for (const sk_sp<SkPicture>& page : fPages) {
103             c->drawPicture(page);
104             // Annotations must include some data.
105             c->drawAnnotation(SkRect::MakeEmpty(), kEndPage, SkData::MakeWithCString("X"));
106         }
107         sk_sp<SkPicture> p = fPictureRecorder.finishRecordingAsPicture();
108         p->serialize(wStream, &fProcs);
109         fPages.clear();
110         fSizes.clear();
111     }
onAbort__anon46d0710a0111::MultiPictureDocument112     void onAbort() override {
113         fPages.clear();
114         fSizes.clear();
115     }
116 };
117 
118 struct PagerCanvas : public SkNWayCanvas {
119     SkPictureRecorder fRecorder;
120     SkDocumentPage* fDst;
121     int fCount;
122     int fIndex = 0;
PagerCanvas__anon46d0710a0111::PagerCanvas123     PagerCanvas(SkISize wh, SkDocumentPage* dst, int count)
124             : SkNWayCanvas(wh.width(), wh.height()), fDst(dst), fCount(count) {
125         this->nextCanvas();
126     }
nextCanvas__anon46d0710a0111::PagerCanvas127     void nextCanvas() {
128         if (fIndex < fCount) {
129             SkRect bounds = SkRect::MakeSize(fDst[fIndex].fSize);
130             this->addCanvas(fRecorder.beginRecording(bounds));
131         }
132     }
onDrawAnnotation__anon46d0710a0111::PagerCanvas133     void onDrawAnnotation(const SkRect& r, const char* key, SkData* d) override {
134         if (0 == strcmp(key, kEndPage)) {
135             this->removeAll();
136             if (fIndex < fCount) {
137                 fDst[fIndex].fPicture = fRecorder.finishRecordingAsPicture();
138                 ++fIndex;
139             }
140             this->nextCanvas();
141         } else {
142             this->SkNWayCanvas::onDrawAnnotation(r, key, d);
143         }
144     }
145 };
146 
147 }  // namespace
148 
149 namespace SkMultiPictureDocument {
Make(SkWStream * dst,const SkSerialProcs * procs,std::function<void (const SkPicture *)> onEndPage)150 sk_sp<SkDocument> Make(SkWStream* dst,
151                        const SkSerialProcs* procs,
152                        std::function<void(const SkPicture*)> onEndPage) {
153     return sk_make_sp<MultiPictureDocument>(dst, procs, std::move(onEndPage));
154 }
155 
ReadPageCount(SkStreamSeekable * src)156 int ReadPageCount(SkStreamSeekable* src) {
157     if (!src) {
158         return 0;
159     }
160     src->seek(0);
161     const size_t size = sizeof(kMagic) - 1;
162     char buffer[size];
163     if (size != src->read(buffer, size) || 0 != memcmp(kMagic, buffer, size)) {
164         src = nullptr;
165         return 0;
166     }
167     uint32_t versionNumber;
168     if (!src->readU32(&versionNumber) || versionNumber != kVersion) {
169         return 0;
170     }
171     uint32_t pageCount;
172     if (!src->readU32(&pageCount) || pageCount > INT_MAX) {
173         return 0;
174     }
175     // leave stream position right here.
176     return SkTo<int>(pageCount);
177 }
178 
ReadPageSizes(SkStreamSeekable * stream,SkDocumentPage * dstArray,int dstArrayCount)179 bool ReadPageSizes(SkStreamSeekable* stream,
180                    SkDocumentPage* dstArray,
181                    int dstArrayCount) {
182     if (!dstArray || dstArrayCount < 1) {
183         return false;
184     }
185     int pageCount = ReadPageCount(stream);
186     if (pageCount < 1 || pageCount != dstArrayCount) {
187         return false;
188     }
189     for (int i = 0; i < pageCount; ++i) {
190         SkSize& s = dstArray[i].fSize;
191         if (sizeof(s) != stream->read(&s, sizeof(s))) {
192             return false;
193         }
194     }
195     // leave stream position right here.
196     return true;
197 }
198 
Read(SkStreamSeekable * src,SkDocumentPage * dstArray,int dstArrayCount,const SkDeserialProcs * procs)199 bool Read(SkStreamSeekable* src,
200           SkDocumentPage* dstArray,
201           int dstArrayCount,
202           const SkDeserialProcs* procs) {
203     if (!ReadPageSizes(src, dstArray, dstArrayCount)) {
204         return false;
205     }
206     SkSize joined = {0.0f, 0.0f};
207     for (int i = 0; i < dstArrayCount; ++i) {
208         joined = SkSize{std::max(joined.width(), dstArray[i].fSize.width()),
209                         std::max(joined.height(), dstArray[i].fSize.height())};
210     }
211 
212     auto picture = SkPicture::MakeFromStream(src, procs);
213     if (!picture) {
214         return false;
215     }
216 
217     PagerCanvas canvas(joined.toCeil(), dstArray, dstArrayCount);
218     // Must call playback(), not drawPicture() to reach
219     // PagerCanvas::onDrawAnnotation().
220     picture->playback(&canvas);
221     if (canvas.fIndex != dstArrayCount) {
222         SkDEBUGF("Malformed SkMultiPictureDocument: canvas.fIndex=%d dstArrayCount=%d\n",
223             canvas.fIndex, dstArrayCount);
224     }
225     return true;
226 }
227 }  // namespace SkMultiPictureDocument
228