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 #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