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