Flutter Engine
The Flutter Engine
SkPictureData.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2011 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
9
26#include "src/core/SkTHash.h"
30
31#include <cstring>
32#include <utility>
33
34using namespace skia_private;
35
36template <typename T> int SafeCount(const T* obj) {
37 return obj ? obj->size() : 0;
38}
39
41 : fInfo(info) {}
42
43void SkPictureData::initForPlayback() const {
44 // ensure that the paths bounds are pre-computed
45 for (int i = 0; i < fPaths.size(); i++) {
46 fPaths[i].updateBoundsCache();
47 }
48}
49
51 const SkPictInfo& info)
52 : fPictures(record.getPictures())
53 , fDrawables(record.getDrawables())
54 , fTextBlobs(record.getTextBlobs())
55 , fVertices(record.getVertices())
56 , fImages(record.getImages())
57 , fSlugs(record.getSlugs())
58 , fInfo(info) {
59
60 fOpData = record.opData();
61
62 fPaints = record.fPaints;
63
64 fPaths.reset(record.fPaths.count());
65 record.fPaths.foreach([this](const SkPath& path, int n) {
66 // These indices are logically 1-based, but we need to serialize them
67 // 0-based to keep the deserializing SkPictureData::getPath() working.
68 fPaths[n-1] = path;
69 });
70
71 this->initForPlayback();
72}
73
74///////////////////////////////////////////////////////////////////////////////
75///////////////////////////////////////////////////////////////////////////////
76
78 size_t size = 4; // for 'count'
79
80 for (int i = 0; i < count; i++) {
81 const char* name = SkFlattenable::FactoryToName(array[i]);
82 if (nullptr == name || 0 == *name) {
84 } else {
85 size_t len = strlen(name);
87 size += len;
88 }
89 }
90
91 return size;
92}
93
94static void write_tag_size(SkWriteBuffer& buffer, uint32_t tag, size_t size) {
95 buffer.writeUInt(tag);
96 buffer.writeUInt(SkToU32(size));
97}
98
99static void write_tag_size(SkWStream* stream, uint32_t tag, size_t size) {
100 stream->write32(tag);
101 stream->write32(SkToU32(size));
102}
103
104void SkPictureData::WriteFactories(SkWStream* stream, const SkFactorySet& rec) {
105 int count = rec.count();
106
109 rec.copyToArray(array);
110
111 size_t size = compute_chunk_size(array, count);
112
113 // TODO: write_tag_size should really take a size_t
115 SkDEBUGCODE(size_t start = stream->bytesWritten());
116 stream->write32(count);
117
118 for (int i = 0; i < count; i++) {
119 const char* name = SkFlattenable::FactoryToName(array[i]);
120 if (nullptr == name || 0 == *name) {
121 stream->writePackedUInt(0);
122 } else {
123 size_t len = strlen(name);
124 stream->writePackedUInt(len);
125 stream->write(name, len);
126 }
127 }
128
129 SkASSERT(size == (stream->bytesWritten() - start));
130}
131
132void SkPictureData::WriteTypefaces(SkWStream* stream, const SkRefCntSet& rec,
133 const SkSerialProcs& procs) {
134 int count = rec.count();
135
137
139 SkTypeface** array = (SkTypeface**)storage.get();
140 rec.copyToArray((SkRefCnt**)array);
141
142 for (int i = 0; i < count; i++) {
143 SkTypeface* tf = array[i];
144 if (procs.fTypefaceProc) {
145 auto data = procs.fTypefaceProc(tf, procs.fTypefaceCtx);
146 if (data) {
147 stream->write(data->data(), data->size());
148 continue;
149 }
150 }
151 // With the default serialization and deserialization behavior,
152 // kIncludeDataIfLocal does not always work because there is no default
153 // fontmgr to pass into SkTypeface::MakeDeserialize, so there is no
154 // fontmgr to find a font given the descriptor only.
156 }
157}
158
159void SkPictureData::flattenToBuffer(SkWriteBuffer& buffer, bool textBlobsOnly) const {
160 if (!textBlobsOnly) {
161 int numPaints = fPaints.size();
162 if (numPaints > 0) {
164 for (const SkPaint& paint : fPaints) {
165 buffer.writePaint(paint);
166 }
167 }
168
169 int numPaths = fPaths.size();
170 if (numPaths > 0) {
172 buffer.writeInt(numPaths);
173 for (const SkPath& path : fPaths) {
174 buffer.writePath(path);
175 }
176 }
177 }
178
179 if (!fTextBlobs.empty()) {
181 for (const auto& blob : fTextBlobs) {
183 }
184 }
185
186 if (!textBlobsOnly) {
188 for (const auto& slug : fSlugs) {
189 slug->doFlatten(buffer);
190 }
191 }
192
193 if (!textBlobsOnly) {
194 if (!fVertices.empty()) {
196 for (const auto& vert : fVertices) {
197 vert->priv().encode(buffer);
198 }
199 }
200
201 if (!fImages.empty()) {
203 for (const auto& img : fImages) {
204 buffer.writeImage(img.get());
205 }
206 }
207 }
208}
209
210// SkPictureData::serialize() will write out paints, and then write out an array of typefaces
211// (unique set). However, paint's serializer will respect SerialProcs, which can cause us to
212// call that custom typefaceproc on *every* typeface, not just on the unique ones. To avoid this,
213// we ignore the custom proc (here) when we serialize the paints, and then do respect it when
214// we serialize the typefaces.
216 SkSerialProcs newProcs = procs;
217 newProcs.fTypefaceProc = nullptr;
218 newProcs.fTypefaceCtx = nullptr;
219 return newProcs;
220}
221
222// topLevelTypeFaceSet is null only on the top level call.
223// This method is called recursively on every subpicture in two passes.
224// textBlobsOnly serves to indicate that we are on the first pass and skip as much work as
225// possible that is not relevant to collecting text blobs in topLevelTypeFaceSet
226// TODO(nifong): dedupe typefaces and all other shared resources in a faster and more readable way.
228 SkRefCntSet* topLevelTypeFaceSet, bool textBlobsOnly) const {
229 // This can happen at pretty much any time, so might as well do it first.
231 stream->write(fOpData->bytes(), fOpData->size());
232
233 // We serialize all typefaces into the typeface section of the top-level picture.
234 SkRefCntSet localTypefaceSet;
235 SkRefCntSet* typefaceSet = topLevelTypeFaceSet ? topLevelTypeFaceSet : &localTypefaceSet;
236
237 // We delay serializing the bulk of our data until after we've serialized
238 // factories and typefaces by first serializing to an in-memory write buffer.
239 SkFactorySet factSet; // buffer refs factSet, so factSet must come first.
241 buffer.setFactoryRecorder(sk_ref_sp(&factSet));
242 buffer.setTypefaceRecorder(sk_ref_sp(typefaceSet));
243 this->flattenToBuffer(buffer, textBlobsOnly);
244
245 // Pretend to serialize our sub-pictures for the side effect of filling typefaceSet
246 // with typefaces from sub-pictures.
247 struct DevNull: public SkWStream {
248 DevNull() : fBytesWritten(0) {}
249 size_t fBytesWritten;
250 bool write(const void*, size_t size) override { fBytesWritten += size; return true; }
251 size_t bytesWritten() const override { return fBytesWritten; }
252 } devnull;
253 for (const auto& pic : fPictures) {
254 pic->serialize(&devnull, nullptr, typefaceSet, /*textBlobsOnly=*/ true);
255 }
256 if (textBlobsOnly) { return; } // return early from fake serialize
257
258 // We need to write factories before we write the buffer.
259 // We need to write typefaces before we write the buffer or any sub-picture.
260 WriteFactories(stream, factSet);
261 // Pass the original typefaceproc (if any) now that we're ready to actually serialize the
262 // typefaces. We skipped this proc before, when we were serializing paints, so that the
263 // paints would just write indices into our typeface set.
264 WriteTypefaces(stream, *typefaceSet, procs);
265
266 // Write the buffer.
268 buffer.writeToStream(stream);
269
270 // Write sub-pictures by calling serialize again.
271 if (!fPictures.empty()) {
272 write_tag_size(stream, SK_PICT_PICTURE_TAG, fPictures.size());
273 for (const auto& pic : fPictures) {
274 pic->serialize(stream, &procs, typefaceSet, /*textBlobsOnly=*/ false);
275 }
276 }
277
278 stream->write32(SK_PICT_EOF_TAG);
279}
280
283 buffer.writeByteArray(fOpData->bytes(), fOpData->size());
284
285 if (!fPictures.empty()) {
287 for (const auto& pic : fPictures) {
289 }
290 }
291
292 if (!fDrawables.empty()) {
294 for (const auto& draw : fDrawables) {
295 buffer.writeFlattenable(draw.get());
296 }
297 }
298
299 // Write this picture playback's data into a writebuffer
300 this->flattenToBuffer(buffer, false);
301 buffer.write32(SK_PICT_EOF_TAG);
302}
303
304///////////////////////////////////////////////////////////////////////////////
305
306bool SkPictureData::parseStreamTag(SkStream* stream,
307 uint32_t tag,
308 uint32_t size,
309 const SkDeserialProcs& procs,
310 SkTypefacePlayback* topLevelTFPlayback,
311 int recursionLimit) {
312 switch (tag) {
314 SkASSERT(nullptr == fOpData);
316 if (!fOpData) {
317 return false;
318 }
319 break;
320 case SK_PICT_FACTORY_TAG: {
321 if (!stream->readU32(&size)) { return false; }
323 return false;
324 }
325 fFactoryPlayback = std::make_unique<SkFactoryPlayback>(size);
326 for (size_t i = 0; i < size; i++) {
327 SkString str;
328 size_t len;
329 if (!stream->readPackedUInt(&len)) { return false; }
331 return false;
332 }
333 str.resize(len);
334 if (stream->read(str.data(), len) != len) {
335 return false;
336 }
337 fFactoryPlayback->base()[i] = SkFlattenable::NameToFactory(str.c_str());
338 }
339 } break;
342 return false;
343 }
344 fTFPlayback.setCount(size);
345 for (uint32_t i = 0; i < size; ++i) {
346 if (stream->isAtEnd()) {
347 return false;
348 }
350 if (procs.fTypefaceProc) {
351 tf = procs.fTypefaceProc(&stream, sizeof(stream), procs.fTypefaceCtx);
352 }
353 else {
354 tf = SkTypeface::MakeDeserialize(stream, nullptr);
355 }
356 if (!tf) { // failed to deserialize
357 // fTFPlayback asserts it never has a null, so we plop in
358 // a default here.
360 }
361 fTFPlayback[i] = std::move(tf);
362 }
363 } break;
364 case SK_PICT_PICTURE_TAG: {
365 SkASSERT(fPictures.empty());
367 return false;
368 }
369 fPictures.reserve_exact(SkToInt(size));
370
371 for (uint32_t i = 0; i < size; i++) {
372 auto pic = SkPicture::MakeFromStreamPriv(stream, &procs,
373 topLevelTFPlayback, recursionLimit - 1);
374 if (!pic) {
375 return false;
376 }
377 fPictures.push_back(std::move(pic));
378 }
379 } break;
382 return false;
383 }
384 SkAutoMalloc storage(size);
385 if (stream->read(storage.get(), size) != size) {
386 return false;
387 }
388
389 SkReadBuffer buffer(storage.get(), size);
390 buffer.setVersion(fInfo.getVersion());
391
392 if (!fFactoryPlayback) {
393 return false;
394 }
395 fFactoryPlayback->setupBuffer(buffer);
396 buffer.setDeserialProcs(procs);
397
398 if (fTFPlayback.count() > 0) {
399 // .skp files <= v43 have typefaces serialized with each sub picture.
400 fTFPlayback.setupBuffer(buffer);
401 } else {
402 // Newer .skp files serialize all typefaces with the top picture.
403 topLevelTFPlayback->setupBuffer(buffer);
404 }
405
406 while (!buffer.eof() && buffer.isValid()) {
407 tag = buffer.readUInt();
408 size = buffer.readUInt();
409 this->parseBufferTag(buffer, tag, size);
410 }
411 if (!buffer.isValid()) {
412 return false;
413 }
414 } break;
415 }
416 return true; // success
417}
418
420 return buffer.readImage();
421}
422
425}
426
427// We need two types 'cause SkDrawable is const-variant.
428template <typename T, typename U>
430 TArray<sk_sp<T>>& array, sk_sp<U> (*factory)(SkReadBuffer&)) {
431 if (!buffer.validate(array.empty() && SkTFitsIn<int>(inCount))) {
432 return false;
433 }
434 if (0 == inCount) {
435 return true;
436 }
437
438 for (uint32_t i = 0; i < inCount; ++i) {
439 auto obj = factory(buffer);
440
441 if (!buffer.validate(obj != nullptr)) {
442 array.clear();
443 return false;
444 }
445
446 array.push_back(std::move(obj));
447 }
448
449 return true;
450}
451
452void SkPictureData::parseBufferTag(SkReadBuffer& buffer, uint32_t tag, uint32_t size) {
453 switch (tag) {
455 if (!buffer.validate(SkTFitsIn<int>(size))) {
456 return;
457 }
458 const int count = SkToInt(size);
459
460 for (int i = 0; i < count; ++i) {
461 fPaints.push_back(buffer.readPaint());
462 if (!buffer.isValid()) {
463 return;
464 }
465 }
466 } break;
468 if (size > 0) {
469 const int count = buffer.readInt();
470 if (!buffer.validate(count >= 0)) {
471 return;
472 }
473 for (int i = 0; i < count; i++) {
474 buffer.readPath(&fPaths.push_back());
475 if (!buffer.isValid()) {
476 return;
477 }
478 }
479 } break;
482 break;
485 break;
488 break;
491 break;
492 case SK_PICT_READER_TAG: {
493 // Preflight check that we can initialize all data from the buffer
494 // before allocating it.
495 if (!buffer.validateCanReadN<uint8_t>(size)) {
496 return;
497 }
499 if (!buffer.readByteArray(data->writable_data(), size) ||
500 !buffer.validate(nullptr == fOpData)) {
501 return;
502 }
503 SkASSERT(nullptr == fOpData);
504 fOpData = std::move(data);
505 } break;
508 break;
511 break;
512 default:
513 buffer.validate(false); // The tag was invalid.
514 break;
515 }
516}
517
519 const SkPictInfo& info,
520 const SkDeserialProcs& procs,
521 SkTypefacePlayback* topLevelTFPlayback,
522 int recursionLimit) {
523 std::unique_ptr<SkPictureData> data(new SkPictureData(info));
524 if (!topLevelTFPlayback) {
525 topLevelTFPlayback = &data->fTFPlayback;
526 }
527
528 if (!data->parseStream(stream, procs, topLevelTFPlayback, recursionLimit)) {
529 return nullptr;
530 }
531 return data.release();
532}
533
535 const SkPictInfo& info) {
536 std::unique_ptr<SkPictureData> data(new SkPictureData(info));
537 buffer.setVersion(info.getVersion());
538
539 if (!data->parseBuffer(buffer)) {
540 return nullptr;
541 }
542 return data.release();
543}
544
546 const SkDeserialProcs& procs,
547 SkTypefacePlayback* topLevelTFPlayback,
548 int recursionLimit) {
549 for (;;) {
550 uint32_t tag;
551 if (!stream->readU32(&tag)) { return false; }
552 if (SK_PICT_EOF_TAG == tag) {
553 break;
554 }
555
556 uint32_t size;
557 if (!stream->readU32(&size)) { return false; }
558 if (!this->parseStreamTag(stream, tag, size, procs, topLevelTFPlayback, recursionLimit)) {
559 return false; // we're invalid
560 }
561 }
562 return true;
563}
564
566 while (buffer.isValid()) {
567 uint32_t tag = buffer.readUInt();
568 if (SK_PICT_EOF_TAG == tag) {
569 break;
570 }
571 this->parseBufferTag(buffer, tag, buffer.readUInt());
572 }
573
574 // Check that we encountered required tags
575 if (!buffer.validate(this->opData() != nullptr)) {
576 // If we didn't build any opData, we are invalid. Even an EmptyPicture allocates the
577 // SkData for the ops (though its length may be zero).
578 return false;
579 }
580 return true;
581}
582
584 int index = reader->readInt();
585 if (index == 0) {
586 return nullptr; // recorder wrote a zero for no paint (likely drawimage)
587 }
588 return reader->validate(index > 0 && index <= fPaints.size()) ?
589 &fPaints[index - 1] : nullptr;
590}
591
593 const SkPaint* paint = this->optionalPaint(reader);
594 if (reader->validate(paint != nullptr)) {
595 return *paint;
596 }
597 static const SkPaint& stub = *(new SkPaint);
598 return stub;
599}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
int count
Definition: FontMgrTest.cpp:50
#define SkASSERT(cond)
Definition: SkAssert.h:116
static SkSerialProcs skip_typeface_proc(const SkSerialProcs &procs)
static void write_tag_size(SkWriteBuffer &buffer, uint32_t tag, size_t size)
bool new_array_from_buffer(SkReadBuffer &buffer, uint32_t inCount, TArray< sk_sp< T > > &array, sk_sp< U >(*factory)(SkReadBuffer &))
static sk_sp< SkImage > create_image_from_buffer(SkReadBuffer &buffer)
int SafeCount(const T *obj)
static sk_sp< SkDrawable > create_drawable_from_buffer(SkReadBuffer &buffer)
static size_t compute_chunk_size(SkFlattenable::Factory *array, int count)
#define SK_PICT_PICTURE_TAG
Definition: SkPictureData.h:64
#define SK_PICT_TEXTBLOB_BUFFER_TAG
Definition: SkPictureData.h:72
#define SK_PICT_EOF_TAG
Definition: SkPictureData.h:78
#define SK_PICT_PATH_BUFFER_TAG
Definition: SkPictureData.h:71
#define SK_PICT_FACTORY_TAG
Definition: SkPictureData.h:62
#define SK_PICT_IMAGE_BUFFER_TAG
Definition: SkPictureData.h:75
#define SK_PICT_VERTICES_BUFFER_TAG
Definition: SkPictureData.h:74
#define SK_PICT_BUFFER_SIZE_TAG
Definition: SkPictureData.h:68
#define SK_PICT_SLUG_BUFFER_TAG
Definition: SkPictureData.h:73
#define SK_PICT_DRAWABLE_TAG
Definition: SkPictureData.h:65
#define SK_PICT_PAINT_BUFFER_TAG
Definition: SkPictureData.h:70
#define SK_PICT_READER_TAG
Definition: SkPictureData.h:61
#define SK_PICT_TYPEFACE_TAG
Definition: SkPictureData.h:63
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
bool StreamRemainingLengthIsBelow(SkStream *stream, size_t len)
Definition: SkStream.cpp:976
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
constexpr int SkToInt(S x)
Definition: SkTo.h:29
constexpr uint32_t SkToU32(S x)
Definition: SkTo.h:26
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition: aaclip.cpp:27
const uint8_t * bytes() const
Definition: SkData.h:43
static sk_sp< SkData > MakeUninitialized(size_t length)
Definition: SkData.cpp:116
static sk_sp< SkData > MakeFromStream(SkStream *, size_t size)
Definition: SkData.cpp:208
size_t size() const
Definition: SkData.h:30
sk_sp< SkFlattenable >(* Factory)(SkReadBuffer &)
Definition: SkFlattenable.h:41
static const char * FactoryToName(Factory)
static Factory NameToFactory(const char name[])
Definition: SkPath.h:59
bool parseBuffer(SkReadBuffer &buffer)
static SkPictureData * CreateFromStream(SkStream *, const SkPictInfo &, const SkDeserialProcs &, SkTypefacePlayback *, int recursionLimit)
static SkPictureData * CreateFromBuffer(SkReadBuffer &, const SkPictInfo &)
bool parseStream(SkStream *, const SkDeserialProcs &, SkTypefacePlayback *, int recursionLimit)
const SkPaint * optionalPaint(SkReadBuffer *reader) const
const SkPaint & requiredPaint(SkReadBuffer *reader) const
void flatten(SkWriteBuffer &) const
SkPictureData(const SkPictureRecord &record, const SkPictInfo &)
void serialize(SkWStream *, const SkSerialProcs &, SkRefCntSet *, bool textBlobsOnly=false) const
const SkPictInfo & info() const
static void Flatten(const sk_sp< const SkPicture >, SkWriteBuffer &buffer)
Definition: SkPicture.cpp:314
static sk_sp< SkPicture > MakeFromBuffer(SkReadBuffer &buffer)
Definition: SkPicture.cpp:213
sk_sp< SkData > opData() const
int count() const
Definition: SkPtrRecorder.h:45
bool validate(bool isValid)
Definition: SkReadBuffer.h:191
int32_t readInt()
const char * data() const
Definition: SkString.h:132
void resize(size_t len)
Definition: SkString.cpp:374
const char * c_str() const
Definition: SkString.h:133
void copyToArray(T *array) const
static void Flatten(const SkTextBlob &, SkWriteBuffer &)
Definition: SkTextBlob.cpp:663
static sk_sp< SkTextBlob > MakeFromBuffer(SkReadBuffer &)
Definition: SkTextBlob.cpp:705
void setupBuffer(SkReadBuffer &buffer) const
size_t count() const
void setCount(size_t count)
void serialize(SkWStream *, SerializeBehavior=SerializeBehavior::kIncludeDataIfLocal) const
Definition: SkTypeface.cpp:202
static sk_sp< SkTypeface > MakeDeserialize(SkStream *, sk_sp< SkFontMgr > lastResortMgr)
Definition: SkTypeface.cpp:241
static sk_sp< SkTypeface > MakeEmpty()
Definition: SkTypeface.cpp:145
static sk_sp< SkVertices > Decode(SkReadBuffer &)
Definition: SkVertices.cpp:267
static int SizeOfPackedUInt(size_t value)
Definition: SkStream.cpp:113
T * get() const
Definition: SkRefCnt.h:303
bool empty() const
Definition: SkTArray.h:199
void reset(int n)
Definition: SkTArray.h:144
int size() const
Definition: SkTArray.h:421
void reserve_exact(int n)
Definition: SkTArray.h:181
void foreach(Fn &&fn)
Definition: SkTHash.h:521
int count() const
Definition: SkTHash.h:471
static sk_sp< Slug > MakeFromBuffer(SkReadBuffer &buffer)
const Paint & paint
Definition: color_source.cc:38
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
#define T
Definition: precompiler.cc:65
void write(SkWStream *wStream, const T &text)
Definition: skqp.cpp:188
SkDeserialTypefaceProc fTypefaceProc
uint32_t getVersion() const
Definition: SkPictureData.h:43
void * fTypefaceCtx
Definition: SkSerialProcs.h:94
SkSerialTypefaceProc fTypefaceProc
Definition: SkSerialProcs.h:93
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63