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