Deliberately call typefaceproc only once per face in pictures
Before this CL, the new test would write out 2 copies of each typeface, since each one is referenced twice in the picture. Bug: skia: Change-Id: I6ebfe6b5ea0bb0cca2869ef0cccad21a51e48411 Reviewed-on: https://skia-review.googlesource.com/151667 Auto-Submit: Mike Reed <reed@google.com> Reviewed-by: Ben Wagner <bungeman@google.com> Commit-Queue: Mike Reed <reed@google.com>
This commit is contained in:
parent
9d1c88d991
commit
32ade4cedf
@ -121,7 +121,8 @@ void SkPictureData::WriteFactories(SkWStream* stream, const SkFactorySet& rec) {
|
||||
SkASSERT(size == (stream->bytesWritten() - start));
|
||||
}
|
||||
|
||||
void SkPictureData::WriteTypefaces(SkWStream* stream, const SkRefCntSet& rec) {
|
||||
void SkPictureData::WriteTypefaces(SkWStream* stream, const SkRefCntSet& rec,
|
||||
const SkSerialProcs& procs) {
|
||||
int count = rec.count();
|
||||
|
||||
write_tag_size(stream, SK_PICT_TYPEFACE_TAG, count);
|
||||
@ -131,6 +132,14 @@ void SkPictureData::WriteTypefaces(SkWStream* stream, const SkRefCntSet& rec) {
|
||||
rec.copyToArray((SkRefCnt**)array);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
SkTypeface* tf = array[i];
|
||||
if (procs.fTypefaceProc) {
|
||||
auto data = procs.fTypefaceProc(tf, procs.fTypefaceCtx);
|
||||
if (data) {
|
||||
stream->write(data->data(), data->size());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
array[i]->serialize(stream);
|
||||
}
|
||||
}
|
||||
@ -175,6 +184,18 @@ void SkPictureData::flattenToBuffer(SkWriteBuffer& buffer) const {
|
||||
}
|
||||
}
|
||||
|
||||
// SkPictureData::serialize() will write out paints, and then write out an array of typefaces
|
||||
// (unique set). However, paint's serializer will respect SerialProcs, which can cause us to
|
||||
// call that custom typefaceproc on *every* typeface, not just on the unique ones. To avoid this,
|
||||
// we ignore the custom proc (here) when we serialize the paints, and then do respect it when
|
||||
// we serialize the typefaces.
|
||||
static SkSerialProcs skip_typeface_proc(const SkSerialProcs& procs) {
|
||||
SkSerialProcs newProcs = procs;
|
||||
newProcs.fTypefaceProc = nullptr;
|
||||
newProcs.fTypefaceCtx = nullptr;
|
||||
return newProcs;
|
||||
}
|
||||
|
||||
void SkPictureData::serialize(SkWStream* stream, const SkSerialProcs& procs,
|
||||
SkRefCntSet* topLevelTypeFaceSet) const {
|
||||
// This can happen at pretty much any time, so might as well do it first.
|
||||
@ -190,7 +211,7 @@ void SkPictureData::serialize(SkWStream* stream, const SkSerialProcs& procs,
|
||||
SkFactorySet factSet; // buffer refs factSet, so factSet must come first.
|
||||
SkBinaryWriteBuffer buffer;
|
||||
buffer.setFactoryRecorder(sk_ref_sp(&factSet));
|
||||
buffer.setSerialProcs(procs);
|
||||
buffer.setSerialProcs(skip_typeface_proc(procs));
|
||||
buffer.setTypefaceRecorder(sk_ref_sp(typefaceSet));
|
||||
this->flattenToBuffer(buffer);
|
||||
|
||||
@ -210,7 +231,10 @@ void SkPictureData::serialize(SkWStream* stream, const SkSerialProcs& procs,
|
||||
// We need to write typefaces before we write the buffer or any sub-picture.
|
||||
WriteFactories(stream, factSet);
|
||||
if (typefaceSet == &localTypefaceSet) {
|
||||
WriteTypefaces(stream, *typefaceSet);
|
||||
// Pass the original typefaceproc (if any) now that we're ready to actually serialize the
|
||||
// typefaces. We skipped this proc before, when we were serializing paints, so that the
|
||||
// paints would just write indices into our typeface set.
|
||||
WriteTypefaces(stream, *typefaceSet, procs);
|
||||
}
|
||||
|
||||
// Write the buffer.
|
||||
|
@ -162,7 +162,7 @@ private:
|
||||
const SkPictInfo fInfo;
|
||||
|
||||
static void WriteFactories(SkWStream* stream, const SkFactorySet& rec);
|
||||
static void WriteTypefaces(SkWStream* stream, const SkRefCntSet& rec);
|
||||
static void WriteTypefaces(SkWStream* stream, const SkRefCntSet& rec, const SkSerialProcs&);
|
||||
|
||||
void initForPlayback() const;
|
||||
};
|
||||
|
@ -178,3 +178,46 @@ DEF_TEST(serial_procs_picture, reporter) {
|
||||
test_pictures(reporter, p0, 1, true);
|
||||
}
|
||||
|
||||
static sk_sp<SkPicture> make_picture(sk_sp<SkTypeface> tf0, sk_sp<SkTypeface> tf1) {
|
||||
SkPictureRecorder rec;
|
||||
SkCanvas* canvas = rec.beginRecording(100, 100);
|
||||
SkPaint paint;
|
||||
paint.setTypeface(tf0); canvas->drawText("hello", 5, 0, 0, paint);
|
||||
paint.setTypeface(tf1); canvas->drawText("hello", 5, 0, 0, paint);
|
||||
paint.setTypeface(tf0); canvas->drawText("hello", 5, 0, 0, paint);
|
||||
paint.setTypeface(tf1); canvas->drawText("hello", 5, 0, 0, paint);
|
||||
return rec.finishRecordingAsPicture();
|
||||
}
|
||||
|
||||
DEF_TEST(serial_typeface, reporter) {
|
||||
auto tf0 = MakeResourceAsTypeface("fonts/hintgasp.ttf");
|
||||
auto tf1 = MakeResourceAsTypeface("fonts/Roboto2-Regular_NoEmbed.ttf");
|
||||
if (!tf0 || !tf1 || tf0.get() == tf1.get()) {
|
||||
return; // need two different typefaces for this test to make sense.
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
REPORTER_ASSERT(reporter, tf0->getRefCnt() == 1);
|
||||
REPORTER_ASSERT(reporter, tf1->getRefCnt() == 1);
|
||||
#endif
|
||||
auto pic = make_picture(tf0, tf1);
|
||||
#ifdef SK_DEBUG
|
||||
// picture should add 2 more references to each typeface
|
||||
REPORTER_ASSERT(reporter, tf0->getRefCnt() == 3);
|
||||
REPORTER_ASSERT(reporter, tf1->getRefCnt() == 3);
|
||||
#endif
|
||||
|
||||
int counter = 0;
|
||||
SkSerialProcs procs;
|
||||
procs.fTypefaceProc = [](SkTypeface* tf, void* ctx) -> sk_sp<SkData> {
|
||||
*(int*)ctx += 1;
|
||||
return nullptr;
|
||||
};
|
||||
procs.fTypefaceCtx = &counter;
|
||||
auto data = pic->serialize(&procs);
|
||||
|
||||
// The picture has 2 references to each typeface, but we want the serialized picture to
|
||||
// only have written the data 1 time per typeface.
|
||||
REPORTER_ASSERT(reporter, counter == 2);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user