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