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 #include "SkMultiPictureDocumentPriv.h" 10 #include "SkNWayCanvas.h" 11 #include "SkPicture.h" 12 #include "SkPictureRecorder.h" 13 #include "SkSerialProcs.h" 14 #include "SkStream.h" 15 #include "SkTArray.h" 16 17 #include <limits.h> 18 19 /* 20 File format: 21 BEGINNING_OF_FILE: 22 kMagic 23 uint32_t version_number (==2) 24 uint32_t page_count 25 { 26 float sizeX 27 float sizeY 28 } * page_count 29 skp file 30 */ 31 32 namespace { 33 // The unique file signature for this file type. 34 static constexpr char kMagic[] = "Skia Multi-Picture Doc\n\n"; 35 36 static constexpr char kEndPage[] = "SkMultiPictureEndPage"; 37 38 const uint32_t kVersion = 2; 39 40 static SkSize join(const SkTArray<SkSize>& sizes) { 41 SkSize joined = {0, 0}; 42 for (SkSize s : sizes) { 43 joined = SkSize{SkTMax(joined.width(), s.width()), SkTMax(joined.height(), s.height())}; 44 } 45 return joined; 46 } 47 48 struct MultiPictureDocument final : public SkDocument { 49 const SkSerialProcs fProcs; 50 SkPictureRecorder fPictureRecorder; 51 SkSize fCurrentPageSize; 52 SkTArray<sk_sp<SkPicture>> fPages; 53 SkTArray<SkSize> fSizes; 54 MultiPictureDocument(SkWStream* s, const SkSerialProcs* procs) 55 : SkDocument(s) 56 , fProcs(procs ? *procs : SkSerialProcs()) 57 {} 58 ~MultiPictureDocument() override { this->close(); } 59 60 SkCanvas* onBeginPage(SkScalar w, SkScalar h) override { 61 fCurrentPageSize.set(w, h); 62 return fPictureRecorder.beginRecording(w, h); 63 } 64 void onEndPage() override { 65 fSizes.push_back(fCurrentPageSize); 66 fPages.push_back(fPictureRecorder.finishRecordingAsPicture()); 67 } 68 void onClose(SkWStream* wStream) override { 69 SkASSERT(wStream); 70 SkASSERT(wStream->bytesWritten() == 0); 71 wStream->writeText(kMagic); 72 wStream->write32(kVersion); 73 wStream->write32(SkToU32(fPages.count())); 74 for (SkSize s : fSizes) { 75 wStream->write(&s, sizeof(s)); 76 } 77 SkSize bigsize = join(fSizes); 78 SkCanvas* c = fPictureRecorder.beginRecording(SkRect::MakeSize(bigsize)); 79 for (const sk_sp<SkPicture>& page : fPages) { 80 c->drawPicture(page); 81 c->drawAnnotation(SkRect::MakeEmpty(), kEndPage, nullptr); 82 } 83 sk_sp<SkPicture> p = fPictureRecorder.finishRecordingAsPicture(); 84 p->serialize(wStream, &fProcs); 85 fPages.reset(); 86 fSizes.reset(); 87 return; 88 } 89 void onAbort() override { 90 fPages.reset(); 91 fSizes.reset(); 92 } 93 }; 94 } 95 96 sk_sp<SkDocument> SkMakeMultiPictureDocument(SkWStream* wStream, const SkSerialProcs* procs) { 97 return sk_make_sp<MultiPictureDocument>(wStream, procs); 98 } 99 100 //////////////////////////////////////////////////////////////////////////////// 101 102 int 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 125 bool 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 145 namespace { 146 struct 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 176 bool SkMultiPictureDocumentRead(SkStreamSeekable* stream, 177 SkDocumentPage* dstArray, 178 int dstArrayCount, 179 const SkDeserialProcs* procs) { 180 if (!SkMultiPictureDocumentReadPageSizes(stream, dstArray, dstArrayCount)) { 181 return false; 182 } 183 SkSize joined = {0.0f, 0.0f}; 184 for (int i = 0; i < dstArrayCount; ++i) { 185 joined = SkSize{SkTMax(joined.width(), dstArray[i].fSize.width()), 186 SkTMax(joined.height(), dstArray[i].fSize.height())}; 187 } 188 189 auto picture = SkPicture::MakeFromStream(stream, procs); 190 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 } 200