blob: 9868ca26920f69592c17f7a71091ef8a32b42331 [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) {
Hal Canaryfafe1352017-04-11 12:12:02 -040040 SkSize joined = {0, 0};
Hal Canary45cde312017-04-03 16:06:42 -040041 for (SkSize s : sizes) {
Hal Canaryfafe1352017-04-11 12:12:02 -040042 joined = SkSize{SkTMax(joined.width(), s.width()), SkTMax(joined.height(), s.height())};
Hal Canary45cde312017-04-03 16:06:42 -040043 }
44 return joined;
45}
46
halcanary45420a92016-06-02 12:41:14 -070047static SkCanvas* trim(SkCanvas* canvas,
48 SkScalar w, SkScalar h,
49 const SkRect& trimBox) {
50 // Only trim if necessary.
51 if (trimBox != SkRect::MakeWH(w, h)) {
52 // All SkDocument implementations implement trimBox using a
53 // clip+translate.
54 canvas->clipRect(trimBox);
55 canvas->translate(trimBox.x(), trimBox.y());
56 }
57 return canvas;
58}
59
halcanary45420a92016-06-02 12:41:14 -070060struct MultiPictureDocument final : public SkDocument {
61 SkPictureRecorder fPictureRecorder;
halcanary59d64022016-06-14 11:06:37 -070062 SkSize fCurrentPageSize;
halcanaryc966ef92016-08-23 09:15:04 -070063 SkTArray<sk_sp<SkPicture>> fPages;
64 SkTArray<SkSize> fSizes;
halcanary45420a92016-06-02 12:41:14 -070065 MultiPictureDocument(SkWStream* s, void (*d)(SkWStream*, bool))
66 : SkDocument(s, d) {}
Brian Salomond3b65972017-03-22 12:05:03 -040067 ~MultiPictureDocument() override { this->close(); }
halcanary45420a92016-06-02 12:41:14 -070068
69 SkCanvas* onBeginPage(SkScalar w, SkScalar h, const SkRect& c) override {
halcanary59d64022016-06-14 11:06:37 -070070 fCurrentPageSize.set(w, h);
halcanary45420a92016-06-02 12:41:14 -070071 return trim(fPictureRecorder.beginRecording(w, h), w, h, c);
72 }
73 void onEndPage() override {
halcanaryc966ef92016-08-23 09:15:04 -070074 fSizes.push_back(fCurrentPageSize);
75 fPages.push_back(fPictureRecorder.finishRecordingAsPicture());
halcanary45420a92016-06-02 12:41:14 -070076 }
reedd14df7c2016-09-22 14:12:46 -070077 void onClose(SkWStream* wStream) override {
halcanary45420a92016-06-02 12:41:14 -070078 SkASSERT(wStream);
79 SkASSERT(wStream->bytesWritten() == 0);
Hal Canary45cde312017-04-03 16:06:42 -040080 wStream->writeText(kMagic);
81 wStream->write32(kVersion);
reedd14df7c2016-09-22 14:12:46 -070082 wStream->write32(SkToU32(fPages.count()));
halcanaryc966ef92016-08-23 09:15:04 -070083 for (SkSize s : fSizes) {
reedd14df7c2016-09-22 14:12:46 -070084 wStream->write(&s, sizeof(s));
halcanary45420a92016-06-02 12:41:14 -070085 }
Hal Canary45cde312017-04-03 16:06:42 -040086 SkSize bigsize = join(fSizes);
halcanaryc966ef92016-08-23 09:15:04 -070087 SkCanvas* c = fPictureRecorder.beginRecording(SkRect::MakeSize(bigsize));
88 for (const sk_sp<SkPicture>& page : fPages) {
89 c->drawPicture(page);
Hal Canary45cde312017-04-03 16:06:42 -040090 c->drawAnnotation(SkRect::MakeEmpty(), kEndPage, nullptr);
halcanary45420a92016-06-02 12:41:14 -070091 }
halcanaryc966ef92016-08-23 09:15:04 -070092 sk_sp<SkPicture> p = fPictureRecorder.finishRecordingAsPicture();
93 p->serialize(wStream);
94 fPages.reset();
95 fSizes.reset();
reedd14df7c2016-09-22 14:12:46 -070096 return;
halcanary45420a92016-06-02 12:41:14 -070097 }
halcanaryc966ef92016-08-23 09:15:04 -070098 void onAbort() override {
99 fPages.reset();
100 fSizes.reset();
101 }
halcanary45420a92016-06-02 12:41:14 -0700102};
103}
104
105sk_sp<SkDocument> SkMakeMultiPictureDocument(SkWStream* wStream) {
106 return sk_make_sp<MultiPictureDocument>(wStream, nullptr);
107}
Hal Canary45cde312017-04-03 16:06:42 -0400108
109////////////////////////////////////////////////////////////////////////////////
110
111int SkMultiPictureDocumentReadPageCount(SkStreamSeekable* stream) {
112 if (!stream) {
113 return 0;
114 }
115 stream->seek(0);
116 const size_t size = sizeof(kMagic) - 1;
117 char buffer[size];
118 if (size != stream->read(buffer, size) || 0 != memcmp(kMagic, buffer, size)) {
119 stream = nullptr;
120 return 0;
121 }
122 uint32_t versionNumber = stream->readU32();
123 if (versionNumber != kVersion) {
124 return 0;
125 }
126 uint32_t pageCount = stream->readU32();
127 if (pageCount > INT_MAX) {
128 return 0;
129 }
130 // leave stream position right here.
131 return (int)pageCount;
132}
133
134bool SkMultiPictureDocumentReadPageSizes(SkStreamSeekable* stream,
135 SkDocumentPage* dstArray,
136 int dstArrayCount) {
137 if (!dstArray || dstArrayCount < 1) {
138 return false;
139 }
140 int pageCount = SkMultiPictureDocumentReadPageCount(stream);
141 if (pageCount < 1 || pageCount != dstArrayCount) {
142 return false;
143 }
144 for (int i = 0; i < pageCount; ++i) {
145 SkSize& s = dstArray[i].fSize;
146 if (sizeof(s) != stream->read(&s, sizeof(s))) {
147 return false;
148 }
149 }
150 // leave stream position right here.
151 return true;
152}
153
154namespace {
155struct PagerCanvas : public SkNWayCanvas {
156 SkPictureRecorder fRecorder;
157 SkDocumentPage* fDst;
158 int fCount;
159 int fIndex = 0;
160 PagerCanvas(SkISize wh, SkDocumentPage* dst, int count)
161 : SkNWayCanvas(wh.width(), wh.height()), fDst(dst), fCount(count) {
162 this->nextCanvas();
163 }
164 void nextCanvas() {
165 if (fIndex < fCount) {
166 SkRect bounds = SkRect::MakeSize(fDst[fIndex].fSize);
167 this->addCanvas(fRecorder.beginRecording(bounds));
168 }
169 }
170 void onDrawAnnotation(const SkRect& r, const char* key, SkData* d) override {
171 if (0 == strcmp(key, kEndPage)) {
172 this->removeAll();
173 if (fIndex < fCount) {
174 fDst[fIndex].fPicture = fRecorder.finishRecordingAsPicture();
175 ++fIndex;
176 }
177 this->nextCanvas();
178 } else {
179 this->SkNWayCanvas::onDrawAnnotation(r, key, d);
180 }
181 }
182};
183} // namespace
184
185bool SkMultiPictureDocumentRead(SkStreamSeekable* stream,
186 SkDocumentPage* dstArray,
187 int dstArrayCount) {
188 if (!SkMultiPictureDocumentReadPageSizes(stream, dstArray, dstArrayCount)) {
189 return false;
190 }
Hal Canaryfafe1352017-04-11 12:12:02 -0400191 SkSize joined = {0.0f, 0.0f};
Hal Canary45cde312017-04-03 16:06:42 -0400192 for (int i = 0; i < dstArrayCount; ++i) {
Hal Canaryfafe1352017-04-11 12:12:02 -0400193 joined = SkSize{SkTMax(joined.width(), dstArray[i].fSize.width()),
194 SkTMax(joined.height(), dstArray[i].fSize.height())};
Hal Canary45cde312017-04-03 16:06:42 -0400195 }
196
197 auto picture = SkPicture::MakeFromStream(stream);
198
199 PagerCanvas canvas(joined.toCeil(), dstArray, dstArrayCount);
200 // Must call playback(), not drawPicture() to reach
201 // PagerCanvas::onDrawAnnotation().
202 picture->playback(&canvas);
203 if (canvas.fIndex != dstArrayCount) {
204 SkDEBUGF(("Malformed SkMultiPictureDocument\n"));
205 }
206 return true;
207}