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 #include <new>
8 #include "SkImageGenerator.h"
9 #include "SkPictureData.h"
10 #include "SkPictureRecord.h"
11 #include "SkReadBuffer.h"
12 #include "SkTextBlob.h"
13 #include "SkTypeface.h"
14 #include "SkWriteBuffer.h"
15
16 #if SK_SUPPORT_GPU
17 #include "GrContext.h"
18 #endif
19
SafeCount(const T * obj)20 template <typename T> int SafeCount(const T* obj) {
21 return obj ? obj->count() : 0;
22 }
23
SkPictureData(const SkPictInfo & info)24 SkPictureData::SkPictureData(const SkPictInfo& info)
25 : fInfo(info) {
26 this->init();
27 }
28
initForPlayback() const29 void SkPictureData::initForPlayback() const {
30 // ensure that the paths bounds are pre-computed
31 for (int i = 0; i < fPaths.count(); i++) {
32 fPaths[i].updateBoundsCache();
33 }
34 }
35
SkPictureData(const SkPictureRecord & record,const SkPictInfo & info,bool deepCopyOps)36 SkPictureData::SkPictureData(const SkPictureRecord& record,
37 const SkPictInfo& info,
38 bool deepCopyOps)
39 : fInfo(info) {
40
41 this->init();
42
43 fOpData = record.opData(deepCopyOps);
44
45 fContentInfo.set(record.fContentInfo);
46
47 fBitmaps = record.fBitmaps;
48 fPaints = record.fPaints;
49
50 fPaths.reset(record.fPaths.count());
51 record.fPaths.foreach([this](const SkPath& path, int n) {
52 // These indices are logically 1-based, but we need to serialize them
53 // 0-based to keep the deserializing SkPictureData::getPath() working.
54 fPaths[n-1] = path;
55 });
56
57 this->initForPlayback();
58
59 const SkTDArray<const SkPicture* >& pictures = record.getPictureRefs();
60 fPictureCount = pictures.count();
61 if (fPictureCount > 0) {
62 fPictureRefs = new const SkPicture* [fPictureCount];
63 for (int i = 0; i < fPictureCount; i++) {
64 fPictureRefs[i] = pictures[i];
65 fPictureRefs[i]->ref();
66 }
67 }
68
69 // templatize to consolidate with similar picture logic?
70 const SkTDArray<const SkTextBlob*>& blobs = record.getTextBlobRefs();
71 fTextBlobCount = blobs.count();
72 if (fTextBlobCount > 0) {
73 fTextBlobRefs = new const SkTextBlob* [fTextBlobCount];
74 for (int i = 0; i < fTextBlobCount; ++i) {
75 fTextBlobRefs[i] = SkRef(blobs[i]);
76 }
77 }
78
79 const SkTDArray<const SkImage*>& imgs = record.getImageRefs();
80 fImageCount = imgs.count();
81 if (fImageCount > 0) {
82 fImageRefs = new const SkImage* [fImageCount];
83 for (int i = 0; i < fImageCount; ++i) {
84 fImageRefs[i] = SkRef(imgs[i]);
85 }
86 }
87 }
88
init()89 void SkPictureData::init() {
90 fPictureRefs = nullptr;
91 fPictureCount = 0;
92 fTextBlobRefs = nullptr;
93 fTextBlobCount = 0;
94 fImageRefs = nullptr;
95 fImageCount = 0;
96 fOpData = nullptr;
97 fFactoryPlayback = nullptr;
98 }
99
~SkPictureData()100 SkPictureData::~SkPictureData() {
101 SkSafeUnref(fOpData);
102
103 for (int i = 0; i < fPictureCount; i++) {
104 fPictureRefs[i]->unref();
105 }
106 delete[] fPictureRefs;
107
108 for (int i = 0; i < fTextBlobCount; i++) {
109 fTextBlobRefs[i]->unref();
110 }
111 delete[] fTextBlobRefs;
112
113 for (int i = 0; i < fImageCount; i++) {
114 fImageRefs[i]->unref();
115 }
116 delete[] fImageRefs;
117
118 delete fFactoryPlayback;
119 }
120
containsBitmaps() const121 bool SkPictureData::containsBitmaps() const {
122 if (fBitmaps.count() > 0 || fImageCount > 0) {
123 return true;
124 }
125 for (int i = 0; i < fPictureCount; ++i) {
126 if (fPictureRefs[i]->willPlayBackBitmaps()) {
127 return true;
128 }
129 }
130 return false;
131 }
132
133 ///////////////////////////////////////////////////////////////////////////////
134 ///////////////////////////////////////////////////////////////////////////////
135
136 #include "SkStream.h"
137
compute_chunk_size(SkFlattenable::Factory * array,int count)138 static size_t compute_chunk_size(SkFlattenable::Factory* array, int count) {
139 size_t size = 4; // for 'count'
140
141 for (int i = 0; i < count; i++) {
142 const char* name = SkFlattenable::FactoryToName(array[i]);
143 if (nullptr == name || 0 == *name) {
144 size += SkWStream::SizeOfPackedUInt(0);
145 } else {
146 size_t len = strlen(name);
147 size += SkWStream::SizeOfPackedUInt(len);
148 size += len;
149 }
150 }
151
152 return size;
153 }
154
write_tag_size(SkWriteBuffer & buffer,uint32_t tag,size_t size)155 static void write_tag_size(SkWriteBuffer& buffer, uint32_t tag, size_t size) {
156 buffer.writeUInt(tag);
157 buffer.writeUInt(SkToU32(size));
158 }
159
write_tag_size(SkWStream * stream,uint32_t tag,size_t size)160 static void write_tag_size(SkWStream* stream, uint32_t tag, size_t size) {
161 stream->write32(tag);
162 stream->write32(SkToU32(size));
163 }
164
WriteFactories(SkWStream * stream,const SkFactorySet & rec)165 void SkPictureData::WriteFactories(SkWStream* stream, const SkFactorySet& rec) {
166 int count = rec.count();
167
168 SkAutoSTMalloc<16, SkFlattenable::Factory> storage(count);
169 SkFlattenable::Factory* array = (SkFlattenable::Factory*)storage.get();
170 rec.copyToArray(array);
171
172 size_t size = compute_chunk_size(array, count);
173
174 // TODO: write_tag_size should really take a size_t
175 write_tag_size(stream, SK_PICT_FACTORY_TAG, (uint32_t) size);
176 SkDEBUGCODE(size_t start = stream->bytesWritten());
177 stream->write32(count);
178
179 for (int i = 0; i < count; i++) {
180 const char* name = SkFlattenable::FactoryToName(array[i]);
181 if (nullptr == name || 0 == *name) {
182 stream->writePackedUInt(0);
183 } else {
184 size_t len = strlen(name);
185 stream->writePackedUInt(len);
186 stream->write(name, len);
187 }
188 }
189
190 SkASSERT(size == (stream->bytesWritten() - start));
191 }
192
WriteTypefaces(SkWStream * stream,const SkRefCntSet & rec)193 void SkPictureData::WriteTypefaces(SkWStream* stream, const SkRefCntSet& rec) {
194 int count = rec.count();
195
196 write_tag_size(stream, SK_PICT_TYPEFACE_TAG, count);
197
198 SkAutoSTMalloc<16, SkTypeface*> storage(count);
199 SkTypeface** array = (SkTypeface**)storage.get();
200 rec.copyToArray((SkRefCnt**)array);
201
202 for (int i = 0; i < count; i++) {
203 array[i]->serialize(stream);
204 }
205 }
206
flattenToBuffer(SkWriteBuffer & buffer) const207 void SkPictureData::flattenToBuffer(SkWriteBuffer& buffer) const {
208 int i, n;
209
210 if ((n = fBitmaps.count()) > 0) {
211 write_tag_size(buffer, SK_PICT_BITMAP_BUFFER_TAG, n);
212 for (i = 0; i < n; i++) {
213 buffer.writeBitmap(fBitmaps[i]);
214 }
215 }
216
217 if ((n = fPaints.count()) > 0) {
218 write_tag_size(buffer, SK_PICT_PAINT_BUFFER_TAG, n);
219 for (i = 0; i < n; i++) {
220 buffer.writePaint(fPaints[i]);
221 }
222 }
223
224 if ((n = fPaths.count()) > 0) {
225 write_tag_size(buffer, SK_PICT_PATH_BUFFER_TAG, n);
226 buffer.writeInt(n);
227 for (int i = 0; i < n; i++) {
228 buffer.writePath(fPaths[i]);
229 }
230 }
231
232 if (fTextBlobCount > 0) {
233 write_tag_size(buffer, SK_PICT_TEXTBLOB_BUFFER_TAG, fTextBlobCount);
234 for (i = 0; i < fTextBlobCount; ++i) {
235 fTextBlobRefs[i]->flatten(buffer);
236 }
237 }
238
239 if (fImageCount > 0) {
240 write_tag_size(buffer, SK_PICT_IMAGE_BUFFER_TAG, fImageCount);
241 for (i = 0; i < fImageCount; ++i) {
242 buffer.writeImage(fImageRefs[i]);
243 }
244 }
245 }
246
serialize(SkWStream * stream,SkPixelSerializer * pixelSerializer,SkRefCntSet * topLevelTypeFaceSet) const247 void SkPictureData::serialize(SkWStream* stream,
248 SkPixelSerializer* pixelSerializer,
249 SkRefCntSet* topLevelTypeFaceSet) const {
250 // This can happen at pretty much any time, so might as well do it first.
251 write_tag_size(stream, SK_PICT_READER_TAG, fOpData->size());
252 stream->write(fOpData->bytes(), fOpData->size());
253
254 // We serialize all typefaces into the typeface section of the top-level picture.
255 SkRefCntSet localTypefaceSet;
256 SkRefCntSet* typefaceSet = topLevelTypeFaceSet ? topLevelTypeFaceSet : &localTypefaceSet;
257
258 // We delay serializing the bulk of our data until after we've serialized
259 // factories and typefaces by first serializing to an in-memory write buffer.
260 SkFactorySet factSet; // buffer refs factSet, so factSet must come first.
261 SkWriteBuffer buffer(SkWriteBuffer::kCrossProcess_Flag);
262 buffer.setFactoryRecorder(&factSet);
263 buffer.setPixelSerializer(pixelSerializer);
264 buffer.setTypefaceRecorder(typefaceSet);
265 this->flattenToBuffer(buffer);
266
267 // Dummy serialize our sub-pictures for the side effect of filling
268 // typefaceSet with typefaces from sub-pictures.
269 struct DevNull: public SkWStream {
270 DevNull() : fBytesWritten(0) {}
271 size_t fBytesWritten;
272 bool write(const void*, size_t size) override { fBytesWritten += size; return true; }
273 size_t bytesWritten() const override { return fBytesWritten; }
274 } devnull;
275 for (int i = 0; i < fPictureCount; i++) {
276 fPictureRefs[i]->serialize(&devnull, pixelSerializer, typefaceSet);
277 }
278
279 // We need to write factories before we write the buffer.
280 // We need to write typefaces before we write the buffer or any sub-picture.
281 WriteFactories(stream, factSet);
282 if (typefaceSet == &localTypefaceSet) {
283 WriteTypefaces(stream, *typefaceSet);
284 }
285
286 // Write the buffer.
287 write_tag_size(stream, SK_PICT_BUFFER_SIZE_TAG, buffer.bytesWritten());
288 buffer.writeToStream(stream);
289
290 // Write sub-pictures by calling serialize again.
291 if (fPictureCount > 0) {
292 write_tag_size(stream, SK_PICT_PICTURE_TAG, fPictureCount);
293 for (int i = 0; i < fPictureCount; i++) {
294 fPictureRefs[i]->serialize(stream, pixelSerializer, typefaceSet);
295 }
296 }
297
298 stream->write32(SK_PICT_EOF_TAG);
299 }
300
flatten(SkWriteBuffer & buffer) const301 void SkPictureData::flatten(SkWriteBuffer& buffer) const {
302 write_tag_size(buffer, SK_PICT_READER_TAG, fOpData->size());
303 buffer.writeByteArray(fOpData->bytes(), fOpData->size());
304
305 if (fPictureCount > 0) {
306 write_tag_size(buffer, SK_PICT_PICTURE_TAG, fPictureCount);
307 for (int i = 0; i < fPictureCount; i++) {
308 fPictureRefs[i]->flatten(buffer);
309 }
310 }
311
312 // Write this picture playback's data into a writebuffer
313 this->flattenToBuffer(buffer);
314 buffer.write32(SK_PICT_EOF_TAG);
315 }
316
317 ///////////////////////////////////////////////////////////////////////////////
318
319 /**
320 * Return the corresponding SkReadBuffer flags, given a set of
321 * SkPictInfo flags.
322 */
pictInfoFlagsToReadBufferFlags(uint32_t pictInfoFlags)323 static uint32_t pictInfoFlagsToReadBufferFlags(uint32_t pictInfoFlags) {
324 static const struct {
325 uint32_t fSrc;
326 uint32_t fDst;
327 } gSD[] = {
328 { SkPictInfo::kCrossProcess_Flag, SkReadBuffer::kCrossProcess_Flag },
329 { SkPictInfo::kScalarIsFloat_Flag, SkReadBuffer::kScalarIsFloat_Flag },
330 { SkPictInfo::kPtrIs64Bit_Flag, SkReadBuffer::kPtrIs64Bit_Flag },
331 };
332
333 uint32_t rbMask = 0;
334 for (size_t i = 0; i < SK_ARRAY_COUNT(gSD); ++i) {
335 if (pictInfoFlags & gSD[i].fSrc) {
336 rbMask |= gSD[i].fDst;
337 }
338 }
339 return rbMask;
340 }
341
parseStreamTag(SkStream * stream,uint32_t tag,uint32_t size,SkPicture::InstallPixelRefProc proc,SkTypefacePlayback * topLevelTFPlayback)342 bool SkPictureData::parseStreamTag(SkStream* stream,
343 uint32_t tag,
344 uint32_t size,
345 SkPicture::InstallPixelRefProc proc,
346 SkTypefacePlayback* topLevelTFPlayback) {
347 /*
348 * By the time we encounter BUFFER_SIZE_TAG, we need to have already seen
349 * its dependents: FACTORY_TAG and TYPEFACE_TAG. These two are not required
350 * but if they are present, they need to have been seen before the buffer.
351 *
352 * We assert that if/when we see either of these, that we have not yet seen
353 * the buffer tag, because if we have, then its too-late to deal with the
354 * factories or typefaces.
355 */
356 SkDEBUGCODE(bool haveBuffer = false;)
357
358 switch (tag) {
359 case SK_PICT_READER_TAG:
360 SkASSERT(nullptr == fOpData);
361 fOpData = SkData::NewFromStream(stream, size);
362 if (!fOpData) {
363 return false;
364 }
365 break;
366 case SK_PICT_FACTORY_TAG: {
367 SkASSERT(!haveBuffer);
368 size = stream->readU32();
369 fFactoryPlayback = new SkFactoryPlayback(size);
370 for (size_t i = 0; i < size; i++) {
371 SkString str;
372 const size_t len = stream->readPackedUInt();
373 str.resize(len);
374 if (stream->read(str.writable_str(), len) != len) {
375 return false;
376 }
377 fFactoryPlayback->base()[i] = SkFlattenable::NameToFactory(str.c_str());
378 }
379 } break;
380 case SK_PICT_TYPEFACE_TAG: {
381 SkASSERT(!haveBuffer);
382 const int count = SkToInt(size);
383 fTFPlayback.setCount(count);
384 for (int i = 0; i < count; i++) {
385 SkAutoTUnref<SkTypeface> tf(SkTypeface::Deserialize(stream));
386 if (!tf.get()) { // failed to deserialize
387 // fTFPlayback asserts it never has a null, so we plop in
388 // the default here.
389 tf.reset(SkTypeface::RefDefault());
390 }
391 fTFPlayback.set(i, tf);
392 }
393 } break;
394 case SK_PICT_PICTURE_TAG: {
395 fPictureCount = 0;
396 fPictureRefs = new const SkPicture* [size];
397 for (uint32_t i = 0; i < size; i++) {
398 fPictureRefs[i] = SkPicture::CreateFromStream(stream, proc, topLevelTFPlayback);
399 if (!fPictureRefs[i]) {
400 return false;
401 }
402 fPictureCount++;
403 }
404 } break;
405 case SK_PICT_BUFFER_SIZE_TAG: {
406 SkAutoMalloc storage(size);
407 if (stream->read(storage.get(), size) != size) {
408 return false;
409 }
410
411 /* Should we use SkValidatingReadBuffer instead? */
412 SkReadBuffer buffer(storage.get(), size);
413 buffer.setFlags(pictInfoFlagsToReadBufferFlags(fInfo.fFlags));
414 buffer.setVersion(fInfo.fVersion);
415
416 if (!fFactoryPlayback) {
417 return false;
418 }
419 fFactoryPlayback->setupBuffer(buffer);
420 buffer.setBitmapDecoder(proc);
421
422 if (fTFPlayback.count() > 0) {
423 // .skp files <= v43 have typefaces serialized with each sub picture.
424 fTFPlayback.setupBuffer(buffer);
425 } else {
426 // Newer .skp files serialize all typefaces with the top picture.
427 topLevelTFPlayback->setupBuffer(buffer);
428 }
429
430 while (!buffer.eof() && buffer.isValid()) {
431 tag = buffer.readUInt();
432 size = buffer.readUInt();
433 if (!this->parseBufferTag(buffer, tag, size)) {
434 return false;
435 }
436 }
437 if (!buffer.isValid()) {
438 return false;
439 }
440 SkDEBUGCODE(haveBuffer = true;)
441 } break;
442 }
443 return true; // success
444 }
445
create_image_from_buffer(SkReadBuffer & buffer)446 static const SkImage* create_image_from_buffer(SkReadBuffer& buffer) {
447 return buffer.readImage();
448 }
449
450 // Need a shallow wrapper to return const SkPicture* to match the other factories,
451 // as SkPicture::CreateFromBuffer() returns SkPicture*
create_picture_from_buffer(SkReadBuffer & buffer)452 static const SkPicture* create_picture_from_buffer(SkReadBuffer& buffer) {
453 return SkPicture::CreateFromBuffer(buffer);
454 }
455
456 template <typename T>
new_array_from_buffer(SkReadBuffer & buffer,uint32_t inCount,const T *** array,int * outCount,const T * (* factory)(SkReadBuffer &))457 bool new_array_from_buffer(SkReadBuffer& buffer, uint32_t inCount,
458 const T*** array, int* outCount, const T* (*factory)(SkReadBuffer&)) {
459 if (!buffer.validate((0 == *outCount) && (nullptr == *array))) {
460 return false;
461 }
462 if (0 == inCount) {
463 return true;
464 }
465 *outCount = inCount;
466 *array = new const T* [*outCount];
467 bool success = true;
468 int i = 0;
469 for (; i < *outCount; i++) {
470 (*array)[i] = factory(buffer);
471 if (nullptr == (*array)[i]) {
472 success = false;
473 break;
474 }
475 }
476 if (!success) {
477 // Delete all of the blobs that were already created (up to but excluding i):
478 for (int j = 0; j < i; j++) {
479 (*array)[j]->unref();
480 }
481 // Delete the array
482 delete[] * array;
483 *array = nullptr;
484 *outCount = 0;
485 return false;
486 }
487 return true;
488 }
489
parseBufferTag(SkReadBuffer & buffer,uint32_t tag,uint32_t size)490 bool SkPictureData::parseBufferTag(SkReadBuffer& buffer, uint32_t tag, uint32_t size) {
491 switch (tag) {
492 case SK_PICT_BITMAP_BUFFER_TAG: {
493 const int count = SkToInt(size);
494 fBitmaps.reset(count);
495 for (int i = 0; i < count; ++i) {
496 SkBitmap* bm = &fBitmaps[i];
497 if (buffer.readBitmap(bm)) {
498 bm->setImmutable();
499 } else {
500 return false;
501 }
502 }
503 } break;
504 case SK_PICT_PAINT_BUFFER_TAG: {
505 const int count = SkToInt(size);
506 fPaints.reset(count);
507 for (int i = 0; i < count; ++i) {
508 buffer.readPaint(&fPaints[i]);
509 }
510 } break;
511 case SK_PICT_PATH_BUFFER_TAG:
512 if (size > 0) {
513 const int count = buffer.readInt();
514 fPaths.reset(count);
515 for (int i = 0; i < count; i++) {
516 buffer.readPath(&fPaths[i]);
517 }
518 } break;
519 case SK_PICT_TEXTBLOB_BUFFER_TAG:
520 if (!new_array_from_buffer(buffer, size, &fTextBlobRefs, &fTextBlobCount,
521 SkTextBlob::CreateFromBuffer)) {
522 return false;
523 }
524 break;
525 case SK_PICT_IMAGE_BUFFER_TAG:
526 if (!new_array_from_buffer(buffer, size, &fImageRefs, &fImageCount,
527 create_image_from_buffer)) {
528 return false;
529 }
530 break;
531 case SK_PICT_READER_TAG: {
532 SkAutoDataUnref data(SkData::NewUninitialized(size));
533 if (!buffer.readByteArray(data->writable_data(), size) ||
534 !buffer.validate(nullptr == fOpData)) {
535 return false;
536 }
537 SkASSERT(nullptr == fOpData);
538 fOpData = data.detach();
539 } break;
540 case SK_PICT_PICTURE_TAG:
541 if (!new_array_from_buffer(buffer, size, &fPictureRefs, &fPictureCount,
542 create_picture_from_buffer)) {
543 return false;
544 }
545 break;
546 default:
547 // The tag was invalid.
548 return false;
549 }
550 return true; // success
551 }
552
CreateFromStream(SkStream * stream,const SkPictInfo & info,SkPicture::InstallPixelRefProc proc,SkTypefacePlayback * topLevelTFPlayback)553 SkPictureData* SkPictureData::CreateFromStream(SkStream* stream,
554 const SkPictInfo& info,
555 SkPicture::InstallPixelRefProc proc,
556 SkTypefacePlayback* topLevelTFPlayback) {
557 SkAutoTDelete<SkPictureData> data(new SkPictureData(info));
558 if (!topLevelTFPlayback) {
559 topLevelTFPlayback = &data->fTFPlayback;
560 }
561
562 if (!data->parseStream(stream, proc, topLevelTFPlayback)) {
563 return nullptr;
564 }
565 return data.detach();
566 }
567
CreateFromBuffer(SkReadBuffer & buffer,const SkPictInfo & info)568 SkPictureData* SkPictureData::CreateFromBuffer(SkReadBuffer& buffer,
569 const SkPictInfo& info) {
570 SkAutoTDelete<SkPictureData> data(new SkPictureData(info));
571 buffer.setVersion(info.fVersion);
572
573 if (!data->parseBuffer(buffer)) {
574 return nullptr;
575 }
576 return data.detach();
577 }
578
parseStream(SkStream * stream,SkPicture::InstallPixelRefProc proc,SkTypefacePlayback * topLevelTFPlayback)579 bool SkPictureData::parseStream(SkStream* stream,
580 SkPicture::InstallPixelRefProc proc,
581 SkTypefacePlayback* topLevelTFPlayback) {
582 for (;;) {
583 uint32_t tag = stream->readU32();
584 if (SK_PICT_EOF_TAG == tag) {
585 break;
586 }
587
588 uint32_t size = stream->readU32();
589 if (!this->parseStreamTag(stream, tag, size, proc, topLevelTFPlayback)) {
590 return false; // we're invalid
591 }
592 }
593 return true;
594 }
595
parseBuffer(SkReadBuffer & buffer)596 bool SkPictureData::parseBuffer(SkReadBuffer& buffer) {
597 for (;;) {
598 uint32_t tag = buffer.readUInt();
599 if (SK_PICT_EOF_TAG == tag) {
600 break;
601 }
602
603 uint32_t size = buffer.readUInt();
604 if (!this->parseBufferTag(buffer, tag, size)) {
605 return false; // we're invalid
606 }
607 }
608 return true;
609 }
610
611 ///////////////////////////////////////////////////////////////////////////////
612 ///////////////////////////////////////////////////////////////////////////////
613
614 #if SK_SUPPORT_GPU
suitableForGpuRasterization(GrContext * context,const char ** reason,int sampleCount) const615 bool SkPictureData::suitableForGpuRasterization(GrContext* context, const char **reason,
616 int sampleCount) const {
617 return fContentInfo.suitableForGpuRasterization(context, reason, sampleCount);
618 }
619
suitableForGpuRasterization(GrContext * context,const char ** reason,GrPixelConfig config,SkScalar dpi) const620 bool SkPictureData::suitableForGpuRasterization(GrContext* context, const char **reason,
621 GrPixelConfig config, SkScalar dpi) const {
622
623 if (context != nullptr) {
624 return this->suitableForGpuRasterization(context, reason,
625 context->getRecommendedSampleCount(config, dpi));
626 } else {
627 return this->suitableForGpuRasterization(nullptr, reason);
628 }
629 }
630
suitableForLayerOptimization() const631 bool SkPictureData::suitableForLayerOptimization() const {
632 return fContentInfo.numLayers() > 0;
633 }
634 #endif
635 ///////////////////////////////////////////////////////////////////////////////
636
637
638