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