Home | History | Annotate | Download | only in utils
      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