blob: b09db06f11a9612cf8f21f6ac24e7d4f8f91208c [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"
Mike Reed47fdf6c2017-12-20 14:12:07 -050013#include "SkSerialProcs.h"
halcanary45420a92016-06-02 12:41:14 -070014#include "SkStream.h"
halcanaryc966ef92016-08-23 09:15:04 -070015#include "SkTArray.h"
halcanary45420a92016-06-02 12:41:14 -070016
Hal Canary45cde312017-04-03 16:06:42 -040017#include <limits.h>
18
halcanary45420a92016-06-02 12:41:14 -070019/*
20 File format:
21 BEGINNING_OF_FILE:
22 kMagic
halcanaryc966ef92016-08-23 09:15:04 -070023 uint32_t version_number (==2)
halcanary45420a92016-06-02 12:41:14 -070024 uint32_t page_count
25 {
halcanary45420a92016-06-02 12:41:14 -070026 float sizeX
27 float sizeY
28 } * page_count
halcanary45420a92016-06-02 12:41:14 -070029 skp file
halcanary45420a92016-06-02 12:41:14 -070030*/
31
32namespace {
Hal Canary45cde312017-04-03 16:06:42 -040033// The unique file signature for this file type.
34static constexpr char kMagic[] = "Skia Multi-Picture Doc\n\n";
35
36static constexpr char kEndPage[] = "SkMultiPictureEndPage";
37
38const uint32_t kVersion = 2;
39
40static SkSize join(const SkTArray<SkSize>& sizes) {
Hal Canaryfafe1352017-04-11 12:12:02 -040041 SkSize joined = {0, 0};
Hal Canary45cde312017-04-03 16:06:42 -040042 for (SkSize s : sizes) {
Hal Canaryfafe1352017-04-11 12:12:02 -040043 joined = SkSize{SkTMax(joined.width(), s.width()), SkTMax(joined.height(), s.height())};
Hal Canary45cde312017-04-03 16:06:42 -040044 }
45 return joined;
46}
47
halcanary45420a92016-06-02 12:41:14 -070048struct MultiPictureDocument final : public SkDocument {
Mike Reed47fdf6c2017-12-20 14:12:07 -050049 const SkSerialProcs fProcs;
halcanary45420a92016-06-02 12:41:14 -070050 SkPictureRecorder fPictureRecorder;
halcanary59d64022016-06-14 11:06:37 -070051 SkSize fCurrentPageSize;
halcanaryc966ef92016-08-23 09:15:04 -070052 SkTArray<sk_sp<SkPicture>> fPages;
53 SkTArray<SkSize> fSizes;
Hal Canaryc5980d02018-01-08 15:02:36 -050054 MultiPictureDocument(SkWStream* s, const SkSerialProcs* procs)
55 : SkDocument(s)
Mike Reed47fdf6c2017-12-20 14:12:07 -050056 , fProcs(procs ? *procs : SkSerialProcs())
57 {}
Brian Salomond3b65972017-03-22 12:05:03 -040058 ~MultiPictureDocument() override { this->close(); }
halcanary45420a92016-06-02 12:41:14 -070059
Hal Canaryb1de5f92017-07-01 22:17:15 -040060 SkCanvas* onBeginPage(SkScalar w, SkScalar h) override {
halcanary59d64022016-06-14 11:06:37 -070061 fCurrentPageSize.set(w, h);
Hal Canaryb1de5f92017-07-01 22:17:15 -040062 return fPictureRecorder.beginRecording(w, h);
halcanary45420a92016-06-02 12:41:14 -070063 }
64 void onEndPage() override {
halcanaryc966ef92016-08-23 09:15:04 -070065 fSizes.push_back(fCurrentPageSize);
66 fPages.push_back(fPictureRecorder.finishRecordingAsPicture());
halcanary45420a92016-06-02 12:41:14 -070067 }
reedd14df7c2016-09-22 14:12:46 -070068 void onClose(SkWStream* wStream) override {
halcanary45420a92016-06-02 12:41:14 -070069 SkASSERT(wStream);
70 SkASSERT(wStream->bytesWritten() == 0);
Hal Canary45cde312017-04-03 16:06:42 -040071 wStream->writeText(kMagic);
72 wStream->write32(kVersion);
reedd14df7c2016-09-22 14:12:46 -070073 wStream->write32(SkToU32(fPages.count()));
halcanaryc966ef92016-08-23 09:15:04 -070074 for (SkSize s : fSizes) {
reedd14df7c2016-09-22 14:12:46 -070075 wStream->write(&s, sizeof(s));
halcanary45420a92016-06-02 12:41:14 -070076 }
Hal Canary45cde312017-04-03 16:06:42 -040077 SkSize bigsize = join(fSizes);
halcanaryc966ef92016-08-23 09:15:04 -070078 SkCanvas* c = fPictureRecorder.beginRecording(SkRect::MakeSize(bigsize));
79 for (const sk_sp<SkPicture>& page : fPages) {
80 c->drawPicture(page);
Hal Canary45cde312017-04-03 16:06:42 -040081 c->drawAnnotation(SkRect::MakeEmpty(), kEndPage, nullptr);
halcanary45420a92016-06-02 12:41:14 -070082 }
halcanaryc966ef92016-08-23 09:15:04 -070083 sk_sp<SkPicture> p = fPictureRecorder.finishRecordingAsPicture();
Mike Reed47fdf6c2017-12-20 14:12:07 -050084 p->serialize(wStream, &fProcs);
halcanaryc966ef92016-08-23 09:15:04 -070085 fPages.reset();
86 fSizes.reset();
reedd14df7c2016-09-22 14:12:46 -070087 return;
halcanary45420a92016-06-02 12:41:14 -070088 }
halcanaryc966ef92016-08-23 09:15:04 -070089 void onAbort() override {
90 fPages.reset();
91 fSizes.reset();
92 }
halcanary45420a92016-06-02 12:41:14 -070093};
94}
95
Mike Reed47fdf6c2017-12-20 14:12:07 -050096sk_sp<SkDocument> SkMakeMultiPictureDocument(SkWStream* wStream, const SkSerialProcs* procs) {
Hal Canaryc5980d02018-01-08 15:02:36 -050097 return sk_make_sp<MultiPictureDocument>(wStream, procs);
halcanary45420a92016-06-02 12:41:14 -070098}
Hal Canary45cde312017-04-03 16:06:42 -040099
100////////////////////////////////////////////////////////////////////////////////
101
102int SkMultiPictureDocumentReadPageCount(SkStreamSeekable* stream) {
103 if (!stream) {
104 return 0;
105 }
106 stream->seek(0);
107 const size_t size = sizeof(kMagic) - 1;
108 char buffer[size];
109 if (size != stream->read(buffer, size) || 0 != memcmp(kMagic, buffer, size)) {
110 stream = nullptr;
111 return 0;
112 }
113 uint32_t versionNumber = stream->readU32();
114 if (versionNumber != kVersion) {
115 return 0;
116 }
117 uint32_t pageCount = stream->readU32();
118 if (pageCount > INT_MAX) {
119 return 0;
120 }
121 // leave stream position right here.
122 return (int)pageCount;
123}
124
125bool SkMultiPictureDocumentReadPageSizes(SkStreamSeekable* stream,
126 SkDocumentPage* dstArray,
127 int dstArrayCount) {
128 if (!dstArray || dstArrayCount < 1) {
129 return false;
130 }
131 int pageCount = SkMultiPictureDocumentReadPageCount(stream);
132 if (pageCount < 1 || pageCount != dstArrayCount) {
133 return false;
134 }
135 for (int i = 0; i < pageCount; ++i) {
136 SkSize& s = dstArray[i].fSize;
137 if (sizeof(s) != stream->read(&s, sizeof(s))) {
138 return false;
139 }
140 }
141 // leave stream position right here.
142 return true;
143}
144
145namespace {
146struct PagerCanvas : public SkNWayCanvas {
147 SkPictureRecorder fRecorder;
148 SkDocumentPage* fDst;
149 int fCount;
150 int fIndex = 0;
151 PagerCanvas(SkISize wh, SkDocumentPage* dst, int count)
152 : SkNWayCanvas(wh.width(), wh.height()), fDst(dst), fCount(count) {
153 this->nextCanvas();
154 }
155 void nextCanvas() {
156 if (fIndex < fCount) {
157 SkRect bounds = SkRect::MakeSize(fDst[fIndex].fSize);
158 this->addCanvas(fRecorder.beginRecording(bounds));
159 }
160 }
161 void onDrawAnnotation(const SkRect& r, const char* key, SkData* d) override {
162 if (0 == strcmp(key, kEndPage)) {
163 this->removeAll();
164 if (fIndex < fCount) {
165 fDst[fIndex].fPicture = fRecorder.finishRecordingAsPicture();
166 ++fIndex;
167 }
168 this->nextCanvas();
169 } else {
170 this->SkNWayCanvas::onDrawAnnotation(r, key, d);
171 }
172 }
173};
174} // namespace
175
176bool SkMultiPictureDocumentRead(SkStreamSeekable* stream,
177 SkDocumentPage* dstArray,
Mike Reed47fdf6c2017-12-20 14:12:07 -0500178 int dstArrayCount,
179 const SkDeserialProcs* procs) {
Hal Canary45cde312017-04-03 16:06:42 -0400180 if (!SkMultiPictureDocumentReadPageSizes(stream, dstArray, dstArrayCount)) {
181 return false;
182 }
Hal Canaryfafe1352017-04-11 12:12:02 -0400183 SkSize joined = {0.0f, 0.0f};
Hal Canary45cde312017-04-03 16:06:42 -0400184 for (int i = 0; i < dstArrayCount; ++i) {
Hal Canaryfafe1352017-04-11 12:12:02 -0400185 joined = SkSize{SkTMax(joined.width(), dstArray[i].fSize.width()),
186 SkTMax(joined.height(), dstArray[i].fSize.height())};
Hal Canary45cde312017-04-03 16:06:42 -0400187 }
188
Mike Reed47fdf6c2017-12-20 14:12:07 -0500189 auto picture = SkPicture::MakeFromStream(stream, procs);
Hal Canary45cde312017-04-03 16:06:42 -0400190
191 PagerCanvas canvas(joined.toCeil(), dstArray, dstArrayCount);
192 // Must call playback(), not drawPicture() to reach
193 // PagerCanvas::onDrawAnnotation().
194 picture->playback(&canvas);
195 if (canvas.fIndex != dstArrayCount) {
196 SkDEBUGF(("Malformed SkMultiPictureDocument\n"));
197 }
198 return true;
199}