SkMultiSKP: version 2
Measurable size improvement.
BUG=skia:5370
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2255333003
Review-Url: https://codereview.chromium.org/2255333003
diff --git a/src/utils/SkMultiPictureDocument.cpp b/src/utils/SkMultiPictureDocument.cpp
index 214e6ad..c40f1c9 100644
--- a/src/utils/SkMultiPictureDocument.cpp
+++ b/src/utils/SkMultiPictureDocument.cpp
@@ -5,33 +5,24 @@
* found in the LICENSE file.
*/
-#include <vector>
-
#include "SkMultiPictureDocument.h"
#include "SkMultiPictureDocumentPriv.h"
#include "SkPicture.h"
#include "SkPictureRecorder.h"
#include "SkStream.h"
+#include "SkTArray.h"
/*
File format:
BEGINNING_OF_FILE:
kMagic
- uint32_t version_number
+ uint32_t version_number (==2)
uint32_t page_count
{
- uint64_t offset
float sizeX
float sizeY
} * page_count
- FIRST_OFFSET:
skp file
- SECOND_OFFSET:
- skp file
- ...
- LAST_OFFSET:
- skp file
- "\nEndOfMultiPicture\n"
*/
namespace {
@@ -48,30 +39,11 @@
return canvas;
}
-struct NullWStream : public SkWStream {
- NullWStream() : fN(0) {}
- bool write(const void*, size_t n) override {
- fN += n;
- return true;
- }
- size_t bytesWritten() const override { return fN; }
- size_t fN;
-};
-
-struct Page {
- Page(SkSize s, sk_sp<SkPicture> c) : fSize(s), fContent(std::move(c)) {}
- Page(Page&&) = default;
- Page(const Page&) = default;
- Page& operator=(const Page&) = default;
- Page& operator=(Page&&) = default;
- SkSize fSize;
- sk_sp<SkPicture> fContent;
-};
-
struct MultiPictureDocument final : public SkDocument {
SkPictureRecorder fPictureRecorder;
SkSize fCurrentPageSize;
- std::vector<Page> fPages;
+ SkTArray<sk_sp<SkPicture>> fPages;
+ SkTArray<SkSize> fSizes;
MultiPictureDocument(SkWStream* s, void (*d)(SkWStream*, bool))
: SkDocument(s, d) {}
~MultiPictureDocument() { this->close(); }
@@ -81,35 +53,37 @@
return trim(fPictureRecorder.beginRecording(w, h), w, h, c);
}
void onEndPage() override {
- fPages.emplace_back(fCurrentPageSize,
- fPictureRecorder.finishRecordingAsPicture());
+ fSizes.push_back(fCurrentPageSize);
+ fPages.push_back(fPictureRecorder.finishRecordingAsPicture());
}
bool onClose(SkWStream* wStream) override {
SkASSERT(wStream);
SkASSERT(wStream->bytesWritten() == 0);
bool good = true;
good &= wStream->writeText(SkMultiPictureDocumentProtocol::kMagic);
- good &= wStream->write32(SkToU32(1)); // version
- good &= wStream->write32(SkToU32(fPages.size()));
- uint64_t offset = wStream->bytesWritten();
- offset += fPages.size() * sizeof(SkMultiPictureDocumentProtocol::Entry);
- for (const auto& page : fPages) {
- SkMultiPictureDocumentProtocol::Entry entry{
- offset, page.fSize.width(), page.fSize.height()};
- good &= wStream->write(&entry, sizeof(entry));
- NullWStream buffer;
- page.fContent->serialize(&buffer);
- offset += buffer.bytesWritten();
+ good &= wStream->write32(SkMultiPictureDocumentProtocol::kVersion);
+ good &= wStream->write32(SkToU32(fPages.count()));
+ for (SkSize s : fSizes) {
+ good &= wStream->write(&s, sizeof(s));
}
- for (const auto& page : fPages) {
- page.fContent->serialize(wStream);
+ SkSize bigsize = SkMultiPictureDocumentProtocol::Join(fSizes);
+ SkCanvas* c = fPictureRecorder.beginRecording(SkRect::MakeSize(bigsize));
+ for (const sk_sp<SkPicture>& page : fPages) {
+ c->drawPicture(page);
+ c->drawAnnotation(SkRect::MakeEmpty(),
+ SkMultiPictureDocumentProtocol::kEndPage,
+ nullptr);
}
- SkASSERT(wStream->bytesWritten() == offset);
- good &= wStream->writeText("\nEndOfMultiPicture\n");
- fPages.clear();
+ sk_sp<SkPicture> p = fPictureRecorder.finishRecordingAsPicture();
+ p->serialize(wStream);
+ fPages.reset();
+ fSizes.reset();
return good;
}
- void onAbort() override { fPages.clear(); }
+ void onAbort() override {
+ fPages.reset();
+ fSizes.reset();
+ }
};
}
diff --git a/src/utils/SkMultiPictureDocument.h b/src/utils/SkMultiPictureDocument.h
index 1da105e..ac78260 100644
--- a/src/utils/SkMultiPictureDocument.h
+++ b/src/utils/SkMultiPictureDocument.h
@@ -7,6 +7,38 @@
#ifndef SkMultiPictureDocument_DEFINED
#define SkMultiPictureDocument_DEFINED
+/*
+ This format is not intended to be used in production.
+
+ For clients looking for a way to represent a document in memory,
+
+ struct Doc {
+ std::vector<sk_sp<SkPicture>> fPages;
+ std::vector<SkSize> fPageSizes;
+ };
+
+ or
+
+ struct Page {
+ sk_sp<SkPicture> fPage;
+ SkSize fPageSize;
+ };
+ std::vector<Page> pages;
+
+ would work much better.
+
+ Multi-SkPicture (MSKP) files are still useful for debugging and
+ testing.
+
+ The downsides of this format are currently:
+ - no way to extract a single page; must read the entire file at once.
+ - must use `dm` to convert to another format before passing into
+ standard skp tools.
+ - `dm` can extract the first page to skp, but no others.
+
+ TODO(halcanary): replace with somthing that addresses these issues.
+ */
+
#include "SkDocument.h"
/** Writes into an experimental, undocumented file format that is
diff --git a/src/utils/SkMultiPictureDocumentPriv.h b/src/utils/SkMultiPictureDocumentPriv.h
index 124dad7..6d5ab47 100644
--- a/src/utils/SkMultiPictureDocumentPriv.h
+++ b/src/utils/SkMultiPictureDocumentPriv.h
@@ -8,16 +8,25 @@
#ifndef SkMultiPictureDocumentPriv_DEFINED
#define SkMultiPictureDocumentPriv_DEFINED
-#include "stdint.h"
+#include "SkTArray.h"
+#include "SkSize.h"
namespace SkMultiPictureDocumentProtocol {
static constexpr char kMagic[] = "Skia Multi-Picture Doc\n\n";
-struct Entry {
- uint64_t offset;
- float sizeX;
- float sizeY;
-};
+static constexpr char kEndPage[] = "SkMultiPictureEndPage";
+
+const uint32_t kVersion = 2;
+
+inline SkSize Join(const SkTArray<SkSize>& sizes) {
+ SkSize joined = SkSize::Make(0, 0);
+ for (SkSize s : sizes) {
+ joined = SkSize::Make(SkTMax(joined.width(), s.width()),
+ SkTMax(joined.height(), s.height()));
+ }
+ return joined;
+}
+
}
#endif // SkMultiPictureDocumentPriv_DEFINED
diff --git a/src/utils/SkMultiPictureDocumentReader.cpp b/src/utils/SkMultiPictureDocumentReader.cpp
index 6bc77bf..3924f3e 100644
--- a/src/utils/SkMultiPictureDocumentReader.cpp
+++ b/src/utils/SkMultiPictureDocumentReader.cpp
@@ -9,6 +9,8 @@
#include "SkMultiPictureDocumentReader.h"
#include "SkPicture.h"
#include "SkStream.h"
+#include "SkPictureRecorder.h"
+#include "SkNWayCanvas.h"
bool SkMultiPictureDocumentReader::init(SkStreamSeekable* stream) {
if (!stream) {
@@ -24,26 +26,68 @@
}
bool good = true;
uint32_t versionNumber = stream->readU32();
- if (versionNumber != 1) {
+ if (versionNumber != SkMultiPictureDocumentProtocol::kVersion) {
return false;
}
uint32_t pageCount = stream->readU32();
fSizes.reset(pageCount);
- fOffsets.reset(pageCount);
for (uint32_t i = 0; i < pageCount; ++i) {
- SkMultiPictureDocumentProtocol::Entry entry;
- good &= sizeof(entry) == stream->read(&entry, sizeof(entry));
- fSizes[i] = SkSize::Make(entry.sizeX, entry.sizeY);
- good &= SkTFitsIn<size_t>(entry.offset);
- fOffsets[i] = static_cast<size_t>(entry.offset);
+ SkSize size;
+ good &= sizeof(size) == stream->read(&size, sizeof(size));
+ fSizes[i] = size;
}
+ fOffset = stream->getPosition();
return good;
}
+namespace {
+struct PagerCanvas : public SkNWayCanvas {
+ SkPictureRecorder fRecorder;
+ const SkTArray<SkSize>* fSizes;
+ SkTArray<sk_sp<SkPicture>>* fDest;
+ PagerCanvas(SkISize wh,
+ const SkTArray<SkSize>* s,
+ SkTArray<sk_sp<SkPicture>>* d)
+ : SkNWayCanvas(wh.width(), wh.height()), fSizes(s), fDest(d) {
+ this->nextCanvas();
+ }
+ void nextCanvas() {
+ int i = fDest->count();
+ if (i < fSizes->count()) {
+ SkRect bounds = SkRect::MakeSize((*fSizes)[i]);
+ this->addCanvas(fRecorder.beginRecording(bounds));
+ }
+ }
+ void onDrawAnnotation(const SkRect& r, const char* key, SkData* d) override {
+ if (0 == strcmp(key, SkMultiPictureDocumentProtocol::kEndPage)) {
+ this->removeAll();
+ if (fRecorder.getRecordingCanvas()) {
+ fDest->emplace_back(fRecorder.finishRecordingAsPicture());
+ }
+ this->nextCanvas();
+ } else {
+ this->SkNWayCanvas::onDrawAnnotation(r, key, d);
+ }
+ }
+};
+} // namespace
+
sk_sp<SkPicture> SkMultiPictureDocumentReader::readPage(SkStreamSeekable* stream,
int pageNumber) const {
SkASSERT(pageNumber >= 0);
- SkASSERT(pageNumber < fOffsets.count());
- SkAssertResult(stream->seek(fOffsets[pageNumber]));
- return SkPicture::MakeFromStream(stream);
+ SkASSERT(pageNumber < fSizes.count());
+ if (0 == fPages.count()) {
+ stream->seek(fOffset); // jump to beginning of skp
+ auto picture = SkPicture::MakeFromStream(stream);
+ SkISize size = SkMultiPictureDocumentProtocol::Join(fSizes).toCeil();
+ PagerCanvas canvas(size, &fSizes, &this->fPages);
+ // Must call playback(), not drawPicture() to reach
+ // PagerCanvas::onDrawAnnotation().
+ picture->playback(&canvas);
+ if (fPages.count() != fSizes.count()) {
+ SkDEBUGF(("Malformed SkMultiPictureDocument\n"));
+ }
+ }
+ // Allow for malformed document.
+ return pageNumber < fPages.count() ? fPages[pageNumber] : nullptr;
}
diff --git a/src/utils/SkMultiPictureDocumentReader.h b/src/utils/SkMultiPictureDocumentReader.h
index 8e0a630..e0473a6 100644
--- a/src/utils/SkMultiPictureDocumentReader.h
+++ b/src/utils/SkMultiPictureDocumentReader.h
@@ -22,10 +22,10 @@
/** Return to factory settings. */
void reset() {
fSizes.reset();
- fOffsets.reset();
+ fPages.reset();
}
- /** Call this after calling init() */
+ /** Call this after calling init() (otherwise you'll always get zero). */
int pageCount() const { return fSizes.count(); }
/** Deserialize a page from the stream. Call init() first. The
@@ -39,7 +39,8 @@
private:
SkTArray<SkSize> fSizes;
- SkTArray<size_t> fOffsets;
+ size_t fOffset;
+ mutable SkTArray<sk_sp<SkPicture>> fPages;
};
#endif // SkMultiPictureDocumentReader_DEFINED