blob: 4251442fcf60d2b4b9575e69e533aa7e20cc8713 [file] [log] [blame]
halcanary45420a92016-06-02 12:41:14 -07001/*
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 "SkMultiPictureDocument.h"
9#include "SkMultiPictureDocumentPriv.h"
Hal Canary45cde312017-04-03 16:06:42 -040010#include "SkNWayCanvas.h"
halcanary45420a92016-06-02 12:41:14 -070011#include "SkPicture.h"
12#include "SkPictureRecorder.h"
13#include "SkStream.h"
halcanaryc966ef92016-08-23 09:15:04 -070014#include "SkTArray.h"
halcanary45420a92016-06-02 12:41:14 -070015
Hal Canary45cde312017-04-03 16:06:42 -040016#include <limits.h>
17
halcanary45420a92016-06-02 12:41:14 -070018/*
19 File format:
20 BEGINNING_OF_FILE:
21 kMagic
halcanaryc966ef92016-08-23 09:15:04 -070022 uint32_t version_number (==2)
halcanary45420a92016-06-02 12:41:14 -070023 uint32_t page_count
24 {
halcanary45420a92016-06-02 12:41:14 -070025 float sizeX
26 float sizeY
27 } * page_count
halcanary45420a92016-06-02 12:41:14 -070028 skp file
halcanary45420a92016-06-02 12:41:14 -070029*/
30
31namespace {
Hal Canary45cde312017-04-03 16:06:42 -040032// The unique file signature for this file type.
33static constexpr char kMagic[] = "Skia Multi-Picture Doc\n\n";
34
35static constexpr char kEndPage[] = "SkMultiPictureEndPage";
36
37const uint32_t kVersion = 2;
38
39static SkSize join(const SkTArray<SkSize>& sizes) {
40 SkSize joined = SkSize::Make(0, 0);
41 for (SkSize s : sizes) {
42 joined = SkSize::Make(SkTMax(joined.width(), s.width()),
43 SkTMax(joined.height(), s.height()));
44 }
45 return joined;
46}
47
halcanary45420a92016-06-02 12:41:14 -070048static SkCanvas* trim(SkCanvas* canvas,
49 SkScalar w, SkScalar h,
50 const SkRect& trimBox) {
51 // Only trim if necessary.
52 if (trimBox != SkRect::MakeWH(w, h)) {
53 // All SkDocument implementations implement trimBox using a
54 // clip+translate.
55 canvas->clipRect(trimBox);
56 canvas->translate(trimBox.x(), trimBox.y());
57 }
58 return canvas;
59}
60
halcanary45420a92016-06-02 12:41:14 -070061struct MultiPictureDocument final : public SkDocument {
62 SkPictureRecorder fPictureRecorder;
halcanary59d64022016-06-14 11:06:37 -070063 SkSize fCurrentPageSize;
halcanaryc966ef92016-08-23 09:15:04 -070064 SkTArray<sk_sp<SkPicture>> fPages;
65 SkTArray<SkSize> fSizes;
halcanary45420a92016-06-02 12:41:14 -070066 MultiPictureDocument(SkWStream* s, void (*d)(SkWStream*, bool))
67 : SkDocument(s, d) {}
Brian Salomond3b65972017-03-22 12:05:03 -040068 ~MultiPictureDocument() override { this->close(); }
halcanary45420a92016-06-02 12:41:14 -070069
70 SkCanvas* onBeginPage(SkScalar w, SkScalar h, const SkRect& c) override {
halcanary59d64022016-06-14 11:06:37 -070071 fCurrentPageSize.set(w, h);
halcanary45420a92016-06-02 12:41:14 -070072 return trim(fPictureRecorder.beginRecording(w, h), w, h, c);
73 }
74 void onEndPage() override {
halcanaryc966ef92016-08-23 09:15:04 -070075 fSizes.push_back(fCurrentPageSize);
76 fPages.push_back(fPictureRecorder.finishRecordingAsPicture());
halcanary45420a92016-06-02 12:41:14 -070077 }
reedd14df7c2016-09-22 14:12:46 -070078 void onClose(SkWStream* wStream) override {
halcanary45420a92016-06-02 12:41:14 -070079 SkASSERT(wStream);
80 SkASSERT(wStream->bytesWritten() == 0);
Hal Canary45cde312017-04-03 16:06:42 -040081 wStream->writeText(kMagic);
82 wStream->write32(kVersion);
reedd14df7c2016-09-22 14:12:46 -070083 wStream->write32(SkToU32(fPages.count()));
halcanaryc966ef92016-08-23 09:15:04 -070084 for (SkSize s : fSizes) {
reedd14df7c2016-09-22 14:12:46 -070085 wStream->write(&s, sizeof(s));
halcanary45420a92016-06-02 12:41:14 -070086 }
Hal Canary45cde312017-04-03 16:06:42 -040087 SkSize bigsize = join(fSizes);
halcanaryc966ef92016-08-23 09:15:04 -070088 SkCanvas* c = fPictureRecorder.beginRecording(SkRect::MakeSize(bigsize));
89 for (const sk_sp<SkPicture>& page : fPages) {
90 c->drawPicture(page);
Hal Canary45cde312017-04-03 16:06:42 -040091 c->drawAnnotation(SkRect::MakeEmpty(), kEndPage, nullptr);
halcanary45420a92016-06-02 12:41:14 -070092 }
halcanaryc966ef92016-08-23 09:15:04 -070093 sk_sp<SkPicture> p = fPictureRecorder.finishRecordingAsPicture();
94 p->serialize(wStream);
95 fPages.reset();
96 fSizes.reset();
reedd14df7c2016-09-22 14:12:46 -070097 return;
halcanary45420a92016-06-02 12:41:14 -070098 }
halcanaryc966ef92016-08-23 09:15:04 -070099 void onAbort() override {
100 fPages.reset();
101 fSizes.reset();
102 }
halcanary45420a92016-06-02 12:41:14 -0700103};
104}
105
106sk_sp<SkDocument> SkMakeMultiPictureDocument(SkWStream* wStream) {
107 return sk_make_sp<MultiPictureDocument>(wStream, nullptr);
108}
Hal Canary45cde312017-04-03 16:06:42 -0400109
110////////////////////////////////////////////////////////////////////////////////
111
112int SkMultiPictureDocumentReadPageCount(SkStreamSeekable* stream) {
113 if (!stream) {
114 return 0;
115 }
116 stream->seek(0);
117 const size_t size = sizeof(kMagic) - 1;
118 char buffer[size];
119 if (size != stream->read(buffer, size) || 0 != memcmp(kMagic, buffer, size)) {
120 stream = nullptr;
121 return 0;
122 }
123 uint32_t versionNumber = stream->readU32();
124 if (versionNumber != kVersion) {
125 return 0;
126 }
127 uint32_t pageCount = stream->readU32();
128 if (pageCount > INT_MAX) {
129 return 0;
130 }
131 // leave stream position right here.
132 return (int)pageCount;
133}
134
135bool SkMultiPictureDocumentReadPageSizes(SkStreamSeekable* stream,
136 SkDocumentPage* dstArray,
137 int dstArrayCount) {
138 if (!dstArray || dstArrayCount < 1) {
139 return false;
140 }
141 int pageCount = SkMultiPictureDocumentReadPageCount(stream);
142 if (pageCount < 1 || pageCount != dstArrayCount) {
143 return false;
144 }
145 for (int i = 0; i < pageCount; ++i) {
146 SkSize& s = dstArray[i].fSize;
147 if (sizeof(s) != stream->read(&s, sizeof(s))) {
148 return false;
149 }
150 }
151 // leave stream position right here.
152 return true;
153}
154
155namespace {
156struct PagerCanvas : public SkNWayCanvas {
157 SkPictureRecorder fRecorder;
158 SkDocumentPage* fDst;
159 int fCount;
160 int fIndex = 0;
161 PagerCanvas(SkISize wh, SkDocumentPage* dst, int count)
162 : SkNWayCanvas(wh.width(), wh.height()), fDst(dst), fCount(count) {
163 this->nextCanvas();
164 }
165 void nextCanvas() {
166 if (fIndex < fCount) {
167 SkRect bounds = SkRect::MakeSize(fDst[fIndex].fSize);
168 this->addCanvas(fRecorder.beginRecording(bounds));
169 }
170 }
171 void onDrawAnnotation(const SkRect& r, const char* key, SkData* d) override {
172 if (0 == strcmp(key, kEndPage)) {
173 this->removeAll();
174 if (fIndex < fCount) {
175 fDst[fIndex].fPicture = fRecorder.finishRecordingAsPicture();
176 ++fIndex;
177 }
178 this->nextCanvas();
179 } else {
180 this->SkNWayCanvas::onDrawAnnotation(r, key, d);
181 }
182 }
183};
184} // namespace
185
186bool SkMultiPictureDocumentRead(SkStreamSeekable* stream,
187 SkDocumentPage* dstArray,
188 int dstArrayCount) {
189 if (!SkMultiPictureDocumentReadPageSizes(stream, dstArray, dstArrayCount)) {
190 return false;
191 }
192 SkSize joined = SkSize::Make(0.0f, 0.0f);
193 for (int i = 0; i < dstArrayCount; ++i) {
194 joined = SkSize::Make(SkTMax(joined.width(), dstArray[i].fSize.width()),
195 SkTMax(joined.height(), dstArray[i].fSize.height()));
196 }
197
198 auto picture = SkPicture::MakeFromStream(stream);
199
200 PagerCanvas canvas(joined.toCeil(), dstArray, dstArrayCount);
201 // Must call playback(), not drawPicture() to reach
202 // PagerCanvas::onDrawAnnotation().
203 picture->playback(&canvas);
204 if (canvas.fIndex != dstArrayCount) {
205 SkDEBUGF(("Malformed SkMultiPictureDocument\n"));
206 }
207 return true;
208}