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
This commit is contained in:
parent
afdc177e77
commit
c966ef9f07
@ -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 @@ static SkCanvas* trim(SkCanvas* canvas,
|
||||
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 @@ struct MultiPictureDocument final : public SkDocument {
|
||||
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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 SkMultiPictureDocumentReader::init(SkStreamSeekable* stream) {
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -22,10 +22,10 @@ public:
|
||||
/** 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 @@ public:
|
||||
|
||||
private:
|
||||
SkTArray<SkSize> fSizes;
|
||||
SkTArray<size_t> fOffsets;
|
||||
size_t fOffset;
|
||||
mutable SkTArray<sk_sp<SkPicture>> fPages;
|
||||
};
|
||||
|
||||
#endif // SkMultiPictureDocumentReader_DEFINED
|
||||
|
Loading…
Reference in New Issue
Block a user