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