Deduplicate typefaces across sub-pictures

Old flow to serialize a picture:
   1) serialize picture ops
   2) serialize all sub pictures recursively
   3) flatten the rest of this picture into a buffer, deduping flattenable factories and typefaces as we go
   4) serialize the factories and typefaces
   5) serialize the bytes from 3)

This allows the data in step 5) to refer to the deduplicated factories and typefaces from step 4).  But, each sub picture in step 2) is completely siloed, so they can't dedup with the parent picture or each other.

New flow:
   1) serialize picture ops
   2) flatten the rest of this picture into a buffer, deduping flattenable factories and typefaces as we go
   3) dummy-serialize sub pictures into /dev/null, with the effect of adding any new typefaces to our dedup set
   4) serialize the factories and typefaces
   5) serialize the bytes from 2)
   6) serialize all sub pictures recursively, with perfect deduplication because of step 3).

Now all typefaces in the top-level picture and all sub pictures recursively should end up deduplicated in the top-level typeface set.

Decoding changes are similar: we just thread through the top-level typefaces to the sub pictures.  What's convenient / surprising is that this new code correctly reads old pictures if we just have each picture prefer its local typeface set over the top-level one: old pictures always just use their own typefaces, and new pictures always use the top-level ones.

BUG=skia:4092

Review URL: https://codereview.chromium.org/1233953004
This commit is contained in:
mtklein 2015-08-18 08:29:59 -07:00 committed by Commit bot
parent a83593b88a
commit 0c263fa9f8
4 changed files with 95 additions and 43 deletions

View File

@ -18,7 +18,9 @@ class SkBitmap;
class SkCanvas;
class SkPictureData;
class SkPixelSerializer;
class SkRefCntSet;
class SkStream;
class SkTypefacePlayback;
class SkWStream;
struct SkPictInfo;
@ -163,6 +165,12 @@ private:
friend class SkEmptyPicture;
template <typename> friend class SkMiniPicture;
void serialize(SkWStream*, SkPixelSerializer*, SkRefCntSet* typefaces) const;
static SkPicture* CreateFromStream(SkStream*,
InstallPixelRefProc proc,
SkTypefacePlayback*);
friend class SkPictureData;
virtual int numSlowPaths() const = 0;
friend struct SkPathCounter;

View File

@ -139,11 +139,18 @@ SkPicture* SkPicture::Forwardport(const SkPictInfo& info, const SkPictureData* d
}
SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
return CreateFromStream(stream, proc, nullptr);
}
SkPicture* SkPicture::CreateFromStream(SkStream* stream,
InstallPixelRefProc proc,
SkTypefacePlayback* typefaces) {
SkPictInfo info;
if (!InternalOnly_StreamIsSKP(stream, &info) || !stream->readBool()) {
return nullptr;
}
SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromStream(stream, info, proc));
SkAutoTDelete<SkPictureData> data(
SkPictureData::CreateFromStream(stream, info, proc, typefaces));
return Forwardport(info, data);
}
@ -166,13 +173,19 @@ SkPictureData* SkPicture::backport() const {
}
void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) const {
this->serialize(stream, pixelSerializer, nullptr);
}
void SkPicture::serialize(SkWStream* stream,
SkPixelSerializer* pixelSerializer,
SkRefCntSet* typefaceSet) const {
SkPictInfo info = this->createHeader();
SkAutoTDelete<SkPictureData> data(this->backport());
stream->write(&info, sizeof(info));
if (data) {
stream->writeBool(true);
data->serialize(stream, pixelSerializer);
data->serialize(stream, pixelSerializer, typefaceSet);
} else {
stream->writeBool(false);
}

View File

@ -68,7 +68,7 @@ SkPictureData::SkPictureData(const SkPictureRecord& record,
fTextBlobRefs[i] = SkRef(blobs[i]);
}
}
const SkTDArray<const SkImage*>& imgs = record.getImageRefs();
fImageCount = imgs.count();
if (fImageCount > 0) {
@ -102,12 +102,12 @@ SkPictureData::~SkPictureData() {
fTextBlobRefs[i]->unref();
}
SkDELETE_ARRAY(fTextBlobRefs);
for (int i = 0; i < fImageCount; i++) {
fImageRefs[i]->unref();
}
SkDELETE_ARRAY(fImageRefs);
SkDELETE(fFactoryPlayback);
}
@ -233,7 +233,7 @@ void SkPictureData::flattenToBuffer(SkWriteBuffer& buffer) const {
fTextBlobRefs[i]->flatten(buffer);
}
}
if (fImageCount > 0) {
write_tag_size(buffer, SK_PICT_IMAGE_BUFFER_TAG, fImageCount);
for (i = 0; i < fImageCount; ++i) {
@ -243,40 +243,56 @@ void SkPictureData::flattenToBuffer(SkWriteBuffer& buffer) const {
}
void SkPictureData::serialize(SkWStream* stream,
SkPixelSerializer* pixelSerializer) const {
SkPixelSerializer* pixelSerializer,
SkRefCntSet* topLevelTypeFaceSet) const {
// This can happen at pretty much any time, so might as well do it first.
write_tag_size(stream, SK_PICT_READER_TAG, fOpData->size());
stream->write(fOpData->bytes(), fOpData->size());
// We serialize all typefaces into the typeface section of the top-level picture.
SkRefCntSet localTypefaceSet;
SkRefCntSet* typefaceSet = topLevelTypeFaceSet ? topLevelTypeFaceSet : &localTypefaceSet;
// We delay serializing the bulk of our data until after we've serialized
// factories and typefaces by first serializing to an in-memory write buffer.
SkFactorySet factSet; // buffer refs factSet, so factSet must come first.
SkWriteBuffer buffer(SkWriteBuffer::kCrossProcess_Flag);
buffer.setFactoryRecorder(&factSet);
buffer.setPixelSerializer(pixelSerializer);
buffer.setTypefaceRecorder(typefaceSet);
this->flattenToBuffer(buffer);
// Dummy serialize our sub-pictures for the side effect of filling
// typefaceSet with typefaces from sub-pictures.
struct DevNull: public SkWStream {
DevNull() : fBytesWritten(0) {}
size_t fBytesWritten;
bool write(const void*, size_t size) override { fBytesWritten += size; return true; }
size_t bytesWritten() const override { return fBytesWritten; }
} devnull;
for (int i = 0; i < fPictureCount; i++) {
fPictureRefs[i]->serialize(&devnull, pixelSerializer, typefaceSet);
}
// We need to write factories before we write the buffer.
// We need to write typefaces before we write the buffer or any sub-picture.
WriteFactories(stream, factSet);
if (typefaceSet == &localTypefaceSet) {
WriteTypefaces(stream, *typefaceSet);
}
// Write the buffer.
write_tag_size(stream, SK_PICT_BUFFER_SIZE_TAG, buffer.bytesWritten());
buffer.writeToStream(stream);
// Write sub-pictures by calling serialize again.
if (fPictureCount > 0) {
write_tag_size(stream, SK_PICT_PICTURE_TAG, fPictureCount);
for (int i = 0; i < fPictureCount; i++) {
fPictureRefs[i]->serialize(stream, pixelSerializer);
fPictureRefs[i]->serialize(stream, pixelSerializer, typefaceSet);
}
}
// Write some of our data into a writebuffer, and then serialize that
// into our stream
{
SkRefCntSet typefaceSet;
SkFactorySet factSet;
SkWriteBuffer buffer(SkWriteBuffer::kCrossProcess_Flag);
buffer.setTypefaceRecorder(&typefaceSet);
buffer.setFactoryRecorder(&factSet);
buffer.setPixelSerializer(pixelSerializer);
this->flattenToBuffer(buffer);
// We have to write these two sets into the stream *before* we write
// the buffer, since parsing that buffer will require that we already
// have these sets available to use.
WriteFactories(stream, factSet);
WriteTypefaces(stream, typefaceSet);
write_tag_size(stream, SK_PICT_BUFFER_SIZE_TAG, buffer.bytesWritten());
buffer.writeToStream(stream);
}
stream->write32(SK_PICT_EOF_TAG);
}
@ -324,7 +340,8 @@ static uint32_t pictInfoFlagsToReadBufferFlags(uint32_t pictInfoFlags) {
bool SkPictureData::parseStreamTag(SkStream* stream,
uint32_t tag,
uint32_t size,
SkPicture::InstallPixelRefProc proc) {
SkPicture::InstallPixelRefProc proc,
SkTypefacePlayback* topLevelTFPlayback) {
/*
* By the time we encounter BUFFER_SIZE_TAG, we need to have already seen
* its dependents: FACTORY_TAG and TYPEFACE_TAG. These two are not required
@ -376,7 +393,7 @@ bool SkPictureData::parseStreamTag(SkStream* stream,
fPictureCount = 0;
fPictureRefs = SkNEW_ARRAY(const SkPicture*, size);
for (uint32_t i = 0; i < size; i++) {
fPictureRefs[i] = SkPicture::CreateFromStream(stream, proc);
fPictureRefs[i] = SkPicture::CreateFromStream(stream, proc, topLevelTFPlayback);
if (!fPictureRefs[i]) {
return false;
}
@ -395,9 +412,16 @@ bool SkPictureData::parseStreamTag(SkStream* stream,
buffer.setVersion(fInfo.fVersion);
fFactoryPlayback->setupBuffer(buffer);
fTFPlayback.setupBuffer(buffer);
buffer.setBitmapDecoder(proc);
if (fTFPlayback.count() > 0) {
// .skp files <= v43 have typefaces serialized with each sub picture.
fTFPlayback.setupBuffer(buffer);
} else {
// Newer .skp files serialize all typefaces with the top picture.
topLevelTFPlayback->setupBuffer(buffer);
}
while (!buffer.eof() && buffer.isValid()) {
tag = buffer.readUInt();
size = buffer.readUInt();
@ -539,10 +563,14 @@ bool SkPictureData::parseBufferTag(SkReadBuffer& buffer, uint32_t tag, uint32_t
SkPictureData* SkPictureData::CreateFromStream(SkStream* stream,
const SkPictInfo& info,
SkPicture::InstallPixelRefProc proc) {
SkPicture::InstallPixelRefProc proc,
SkTypefacePlayback* topLevelTFPlayback) {
SkAutoTDelete<SkPictureData> data(SkNEW_ARGS(SkPictureData, (info)));
if (!topLevelTFPlayback) {
topLevelTFPlayback = &data->fTFPlayback;
}
if (!data->parseStream(stream, proc)) {
if (!data->parseStream(stream, proc, topLevelTFPlayback)) {
return NULL;
}
return data.detach();
@ -560,7 +588,8 @@ SkPictureData* SkPictureData::CreateFromBuffer(SkReadBuffer& buffer,
}
bool SkPictureData::parseStream(SkStream* stream,
SkPicture::InstallPixelRefProc proc) {
SkPicture::InstallPixelRefProc proc,
SkTypefacePlayback* topLevelTFPlayback) {
for (;;) {
uint32_t tag = stream->readU32();
if (SK_PICT_EOF_TAG == tag) {
@ -568,7 +597,7 @@ bool SkPictureData::parseStream(SkStream* stream,
}
uint32_t size = stream->readU32();
if (!this->parseStreamTag(stream, tag, size, proc)) {
if (!this->parseStreamTag(stream, tag, size, proc, topLevelTFPlayback)) {
return false; // we're invalid
}
}

View File

@ -62,12 +62,13 @@ public:
// Does not affect ownership of SkStream.
static SkPictureData* CreateFromStream(SkStream*,
const SkPictInfo&,
SkPicture::InstallPixelRefProc);
SkPicture::InstallPixelRefProc,
SkTypefacePlayback*);
static SkPictureData* CreateFromBuffer(SkReadBuffer&, const SkPictInfo&);
virtual ~SkPictureData();
void serialize(SkWStream*, SkPixelSerializer*) const;
void serialize(SkWStream*, SkPixelSerializer*, SkRefCntSet*) const;
void flatten(SkWriteBuffer&) const;
bool containsBitmaps() const;
@ -82,7 +83,7 @@ protected:
explicit SkPictureData(const SkPictInfo& info);
// Does not affect ownership of SkStream.
bool parseStream(SkStream*, SkPicture::InstallPixelRefProc);
bool parseStream(SkStream*, SkPicture::InstallPixelRefProc, SkTypefacePlayback*);
bool parseBuffer(SkReadBuffer& buffer);
public:
@ -95,7 +96,7 @@ public:
const int index = reader->readInt();
return fImageRefs[index];
}
const SkPath& getPath(SkReader32* reader) const {
int index = reader->readInt() - 1;
return fPaths[index];
@ -144,7 +145,8 @@ private:
// these help us with reading/writing
// Does not affect ownership of SkStream.
bool parseStreamTag(SkStream*, uint32_t tag, uint32_t size, SkPicture::InstallPixelRefProc);
bool parseStreamTag(SkStream*, uint32_t tag, uint32_t size,
SkPicture::InstallPixelRefProc, SkTypefacePlayback*);
bool parseBufferTag(SkReadBuffer&, uint32_t tag, uint32_t size);
void flattenToBuffer(SkWriteBuffer&) const;