SkPicture.cpp revision 81940de68893e6a643301f9930db630764729ea8
1 2/* 3 * Copyright 2007 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkPictureFlat.h" 11#include "SkPictureData.h" 12#include "SkPicturePlayback.h" 13#include "SkPictureRecord.h" 14#include "SkPictureRecorder.h" 15#include "SkPictureStateTree.h" 16 17#include "SkBitmapDevice.h" 18#include "SkCanvas.h" 19#include "SkChunkAlloc.h" 20#include "SkDrawPictureCallback.h" 21#include "SkPaintPriv.h" 22#include "SkPathEffect.h" 23#include "SkPicture.h" 24#include "SkRegion.h" 25#include "SkShader.h" 26#include "SkStream.h" 27#include "SkTDArray.h" 28#include "SkTLogic.h" 29#include "SkTSearch.h" 30#include "SkTime.h" 31 32#include "SkReader32.h" 33#include "SkWriter32.h" 34#include "SkRTree.h" 35#include "SkBBoxHierarchyRecord.h" 36 37#if SK_SUPPORT_GPU 38#include "GrContext.h" 39#endif 40 41#include "SkRecord.h" 42#include "SkRecordDraw.h" 43#include "SkRecordOpts.h" 44#include "SkRecorder.h" 45 46template <typename T> int SafeCount(const T* obj) { 47 return obj ? obj->count() : 0; 48} 49 50/////////////////////////////////////////////////////////////////////////////// 51 52namespace { 53 54// Some commands have a paint, some have an optional paint. Either way, get back a pointer. 55static const SkPaint* AsPtr(const SkPaint& p) { return &p; } 56static const SkPaint* AsPtr(const SkRecords::Optional<SkPaint>& p) { return p; } 57 58/** SkRecords visitor to determine whether an instance may require an 59 "external" bitmap to rasterize. May return false positives. 60 Does not return true for bitmap text. 61 62 Expected use is to determine whether images need to be decoded before 63 rasterizing a particular SkRecord. 64 */ 65struct BitmapTester { 66 // Helpers. These create HasMember_bitmap and HasMember_paint. 67 SK_CREATE_MEMBER_DETECTOR(bitmap); 68 SK_CREATE_MEMBER_DETECTOR(paint); 69 70 71 // Main entry for visitor: 72 // If the command is a DrawPicture, recurse. 73 // If the command has a bitmap directly, return true. 74 // If the command has a paint and the paint has a bitmap, return true. 75 // Otherwise, return false. 76 bool operator()(const SkRecords::DrawPicture& op) { return op.picture->willPlayBackBitmaps(); } 77 78 template <typename T> 79 bool operator()(const T& r) { return CheckBitmap(r); } 80 81 82 // If the command has a bitmap, of course we're going to play back bitmaps. 83 template <typename T> 84 static SK_WHEN(HasMember_bitmap<T>, bool) CheckBitmap(const T&) { return true; } 85 86 // If not, look for one in its paint (if it has a paint). 87 template <typename T> 88 static SK_WHEN(!HasMember_bitmap<T>, bool) CheckBitmap(const T& r) { return CheckPaint(r); } 89 90 // If we have a paint, dig down into the effects looking for a bitmap. 91 template <typename T> 92 static SK_WHEN(HasMember_paint<T>, bool) CheckPaint(const T& r) { 93 const SkPaint* paint = AsPtr(r.paint); 94 if (paint) { 95 const SkShader* shader = paint->getShader(); 96 if (shader && 97 shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) { 98 return true; 99 } 100 } 101 return false; 102 } 103 104 // If we don't have a paint, that non-paint has no bitmap. 105 template <typename T> 106 static SK_WHEN(!HasMember_paint<T>, bool) CheckPaint(const T&) { return false; } 107}; 108 109bool WillPlaybackBitmaps(const SkRecord& record) { 110 BitmapTester tester; 111 for (unsigned i = 0; i < record.count(); i++) { 112 if (record.visit<bool>(i, tester)) { 113 return true; 114 } 115 } 116 return false; 117} 118 119// SkRecord visitor to find recorded text. 120struct TextHunter { 121 // All ops with text have that text as a char array member named "text". 122 SK_CREATE_MEMBER_DETECTOR(text); 123 bool operator()(const SkRecords::DrawPicture& op) { return op.picture->hasText(); } 124 template <typename T> SK_WHEN(HasMember_text<T>, bool) operator()(const T&) { return true; } 125 template <typename T> SK_WHEN(!HasMember_text<T>, bool) operator()(const T&) { return false; } 126}; 127 128} // namespace 129 130/** SkRecords visitor to determine heuristically whether or not a SkPicture 131 will be performant when rasterized on the GPU. 132 */ 133struct SkPicture::PathCounter { 134 SK_CREATE_MEMBER_DETECTOR(paint); 135 136 PathCounter() 137 : numPaintWithPathEffectUses (0) 138 , numFastPathDashEffects (0) 139 , numAAConcavePaths (0) 140 , numAAHairlineConcavePaths (0) { 141 } 142 143 // Recurse into nested pictures. 144 void operator()(const SkRecords::DrawPicture& op) { 145 // If you're not also SkRecord-backed, tough luck. Get on the bandwagon. 146 if (op.picture->fRecord.get() == NULL) { 147 return; 148 } 149 const SkRecord& nested = *op.picture->fRecord; 150 for (unsigned i = 0; i < nested.count(); i++) { 151 nested.visit<void>(i, *this); 152 } 153 } 154 155 void checkPaint(const SkPaint* paint) { 156 if (paint && paint->getPathEffect()) { 157 numPaintWithPathEffectUses++; 158 } 159 } 160 161 void operator()(const SkRecords::DrawPoints& op) { 162 this->checkPaint(&op.paint); 163 const SkPathEffect* effect = op.paint.getPathEffect(); 164 if (effect) { 165 SkPathEffect::DashInfo info; 166 SkPathEffect::DashType dashType = effect->asADash(&info); 167 if (2 == op.count && SkPaint::kRound_Cap != op.paint.getStrokeCap() && 168 SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) { 169 numFastPathDashEffects++; 170 } 171 } 172 } 173 174 void operator()(const SkRecords::DrawPath& op) { 175 this->checkPaint(&op.paint); 176 if (op.paint.isAntiAlias() && !op.path.isConvex()) { 177 numAAConcavePaths++; 178 179 if (SkPaint::kStroke_Style == op.paint.getStyle() && 180 0 == op.paint.getStrokeWidth()) { 181 numAAHairlineConcavePaths++; 182 } 183 } 184 } 185 186 template <typename T> 187 SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) { 188 this->checkPaint(AsPtr(op.paint)); 189 } 190 191 template <typename T> 192 SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { /* do nothing */ } 193 194 int numPaintWithPathEffectUses; 195 int numFastPathDashEffects; 196 int numAAConcavePaths; 197 int numAAHairlineConcavePaths; 198}; 199 200SkPicture::Analysis::Analysis(const SkRecord& record) { 201 fWillPlaybackBitmaps = WillPlaybackBitmaps(record); 202 203 PathCounter counter; 204 for (unsigned i = 0; i < record.count(); i++) { 205 record.visit<void>(i, counter); 206 } 207 fNumPaintWithPathEffectUses = counter.numPaintWithPathEffectUses; 208 fNumFastPathDashEffects = counter.numFastPathDashEffects; 209 fNumAAConcavePaths = counter.numAAConcavePaths; 210 fNumAAHairlineConcavePaths = counter.numAAHairlineConcavePaths; 211 212 fHasText = false; 213 TextHunter text; 214 for (unsigned i = 0; i < record.count(); i++) { 215 if (record.visit<bool>(i, text)) { 216 fHasText = true; 217 break; 218 } 219 } 220} 221 222bool SkPicture::Analysis::suitableForGpuRasterization(const char** reason, 223 int sampleCount) const { 224 // TODO: the heuristic used here needs to be refined 225 static const int kNumPaintWithPathEffectsUsesTol = 1; 226 static const int kNumAAConcavePathsTol = 5; 227 228 int numNonDashedPathEffects = fNumPaintWithPathEffectUses - 229 fNumFastPathDashEffects; 230 bool suitableForDash = (0 == fNumPaintWithPathEffectUses) || 231 (numNonDashedPathEffects < kNumPaintWithPathEffectsUsesTol 232 && 0 == sampleCount); 233 234 bool ret = suitableForDash && 235 (fNumAAConcavePaths - fNumAAHairlineConcavePaths) 236 < kNumAAConcavePathsTol; 237 238 if (!ret && reason) { 239 if (!suitableForDash) { 240 if (0 != sampleCount) { 241 *reason = "Can't use multisample on dash effect."; 242 } else { 243 *reason = "Too many non dashed path effects."; 244 } 245 } else if ((fNumAAConcavePaths - fNumAAHairlineConcavePaths) 246 >= kNumAAConcavePathsTol) 247 *reason = "Too many anti-aliased concave paths."; 248 else 249 *reason = "Unknown reason for GPU unsuitability."; 250 } 251 return ret; 252} 253 254/////////////////////////////////////////////////////////////////////////////// 255 256// fRecord OK 257SkPicture::SkPicture(SkScalar width, SkScalar height, 258 const SkPictureRecord& record, 259 bool deepCopyOps) 260 : fCullWidth(width) 261 , fCullHeight(height) 262 , fAnalysis() { 263 this->needsNewGenID(); 264 265 SkPictInfo info; 266 this->createHeader(&info); 267 fData.reset(SkNEW_ARGS(SkPictureData, (record, info, deepCopyOps))); 268} 269 270// Create an SkPictureData-backed SkPicture from an SkRecord. 271// This for compatibility with serialization code only. This is not cheap. 272static SkPicture* backport(const SkRecord& src, const SkRect& cullRect) { 273 SkPictureRecorder recorder; 274 SkRecordDraw(src, 275 recorder.DEPRECATED_beginRecording(cullRect.width(), cullRect.height()), 276 NULL/*bbh*/, NULL/*callback*/); 277 return recorder.endRecording(); 278} 279 280// fRecord OK 281SkPicture::~SkPicture() { 282 this->callDeletionListeners(); 283} 284 285// fRecord OK 286#ifdef SK_SUPPORT_LEGACY_PICTURE_CLONE 287SkPicture* SkPicture::clone() const { 288 return SkRef(const_cast<SkPicture*>(this)); 289} 290#endif//SK_SUPPORT_LEGACY_PICTURE_CLONE 291 292// fRecord OK 293void SkPicture::EXPERIMENTAL_addAccelData(const SkPicture::AccelData* data) const { 294 fAccelData.reset(SkRef(data)); 295} 296 297// fRecord OK 298const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData( 299 SkPicture::AccelData::Key key) const { 300 if (fAccelData.get() && fAccelData->getKey() == key) { 301 return fAccelData.get(); 302 } 303 return NULL; 304} 305 306// fRecord OK 307SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() { 308 static int32_t gNextID = 0; 309 310 int32_t id = sk_atomic_inc(&gNextID); 311 if (id >= 1 << (8 * sizeof(Domain))) { 312 SK_CRASH(); 313 } 314 315 return static_cast<Domain>(id); 316} 317 318/////////////////////////////////////////////////////////////////////////////// 319 320uint32_t SkPicture::OperationList::offset(int index) const { 321 SkASSERT(index < fOps.count()); 322 return ((SkPictureStateTree::Draw*)fOps[index])->fOffset; 323} 324 325const SkMatrix& SkPicture::OperationList::matrix(int index) const { 326 SkASSERT(index < fOps.count()); 327 return *((SkPictureStateTree::Draw*)fOps[index])->fMatrix; 328} 329 330// fRecord OK 331void SkPicture::playback(SkCanvas* canvas, SkDrawPictureCallback* callback) const { 332 SkASSERT(canvas); 333 SkASSERT(fData.get() || fRecord.get()); 334 335 // If the query contains the whole picture, don't bother with the BBH. 336 SkRect clipBounds = { 0, 0, 0, 0 }; 337 (void)canvas->getClipBounds(&clipBounds); 338 const bool useBBH = !clipBounds.contains(this->cullRect()); 339 340 if (fData.get()) { 341 SkPicturePlayback playback(this); 342 playback.setUseBBH(useBBH); 343 playback.draw(canvas, callback); 344 } 345 if (fRecord.get()) { 346 SkRecordDraw(*fRecord, canvas, useBBH ? fBBH.get() : NULL, callback); 347 } 348} 349 350/////////////////////////////////////////////////////////////////////////////// 351 352#include "SkStream.h" 353 354static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' }; 355 356// fRecord OK 357bool SkPicture::IsValidPictInfo(const SkPictInfo& info) { 358 if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) { 359 return false; 360 } 361 362 if (info.fVersion < MIN_PICTURE_VERSION || 363 info.fVersion > CURRENT_PICTURE_VERSION) { 364 return false; 365 } 366 367 return true; 368} 369 370// fRecord OK 371bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) { 372 if (NULL == stream) { 373 return false; 374 } 375 376 // Check magic bytes. 377 SkPictInfo info; 378 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); 379 380 if (!stream->read(&info.fMagic, sizeof(kMagic))) { 381 return false; 382 } 383 384 info.fVersion = stream->readU32(); 385 386#ifndef V35_COMPATIBILITY_CODE 387 if (info.fVersion < 35) { 388 info.fCullRect.fLeft = 0; 389 info.fCullRect.fTop = 0; 390 info.fCullRect.fRight = SkIntToScalar(stream->readU32()); 391 info.fCullRect.fBottom = SkIntToScalar(stream->readU32()); 392 } else { 393#endif 394 info.fCullRect.fLeft = stream->readScalar(); 395 info.fCullRect.fTop = stream->readScalar(); 396 info.fCullRect.fRight = stream->readScalar(); 397 info.fCullRect.fBottom = stream->readScalar(); 398#ifndef V35_COMPATIBILITY_CODE 399 } 400#endif 401 402 info.fFlags = stream->readU32(); 403 404 if (!IsValidPictInfo(info)) { 405 return false; 406 } 407 408 if (pInfo != NULL) { 409 *pInfo = info; 410 } 411 return true; 412} 413 414// fRecord OK 415bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) { 416 // Check magic bytes. 417 SkPictInfo info; 418 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); 419 420 if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) { 421 return false; 422 } 423 424 info.fVersion = buffer->readUInt(); 425 426#ifndef V35_COMPATIBILITY_CODE 427 if (info.fVersion < 35) { 428 info.fCullRect.fLeft = 0; 429 info.fCullRect.fTop = 0; 430 info.fCullRect.fRight = SkIntToScalar(buffer->readUInt()); 431 info.fCullRect.fBottom = SkIntToScalar(buffer->readUInt()); 432 } else { 433#endif 434 buffer->readRect(&info.fCullRect); 435#ifndef V35_COMPATIBILITY_CODE 436 } 437#endif 438 439 info.fFlags = buffer->readUInt(); 440 441 if (!IsValidPictInfo(info)) { 442 return false; 443 } 444 445 if (pInfo != NULL) { 446 *pInfo = info; 447 } 448 return true; 449} 450 451// fRecord OK 452SkPicture::SkPicture(SkPictureData* data, SkScalar width, SkScalar height) 453 : fData(data) 454 , fCullWidth(width) 455 , fCullHeight(height) 456 , fAnalysis() { 457 this->needsNewGenID(); 458} 459 460SkPicture* SkPicture::Forwardport(const SkPicture& src) { 461 SkAutoTDelete<SkRecord> record(SkNEW(SkRecord)); 462 SkRecorder canvas(record.get(), src.cullRect().width(), src.cullRect().height()); 463 src.playback(&canvas); 464 return SkNEW_ARGS(SkPicture, (src.cullRect().width(), src.cullRect().height(), 465 record.detach(), NULL/*bbh*/)); 466} 467 468// fRecord OK 469SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) { 470 SkPictInfo info; 471 472 if (!InternalOnly_StreamIsSKP(stream, &info)) { 473 return NULL; 474 } 475 476 // Check to see if there is a playback to recreate. 477 if (stream->readBool()) { 478 SkPictureData* data = SkPictureData::CreateFromStream(stream, info, proc); 479 if (NULL == data) { 480 return NULL; 481 } 482 const SkPicture src(data, info.fCullRect.width(), info.fCullRect.height()); 483 return Forwardport(src); 484 } 485 486 return NULL; 487} 488 489// fRecord OK 490SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) { 491 SkPictInfo info; 492 493 if (!InternalOnly_BufferIsSKP(&buffer, &info)) { 494 return NULL; 495 } 496 497 // Check to see if there is a playback to recreate. 498 if (buffer.readBool()) { 499 SkPictureData* data = SkPictureData::CreateFromBuffer(buffer, info); 500 if (NULL == data) { 501 return NULL; 502 } 503 const SkPicture src(data, info.fCullRect.width(), info.fCullRect.height()); 504 return Forwardport(src); 505 } 506 507 return NULL; 508} 509 510// fRecord OK 511void SkPicture::createHeader(SkPictInfo* info) const { 512 // Copy magic bytes at the beginning of the header 513 SkASSERT(sizeof(kMagic) == 8); 514 SkASSERT(sizeof(kMagic) == sizeof(info->fMagic)); 515 memcpy(info->fMagic, kMagic, sizeof(kMagic)); 516 517 // Set picture info after magic bytes in the header 518 info->fVersion = CURRENT_PICTURE_VERSION; 519 info->fCullRect = this->cullRect(); 520 info->fFlags = SkPictInfo::kCrossProcess_Flag; 521 // TODO: remove this flag, since we're always float (now) 522 info->fFlags |= SkPictInfo::kScalarIsFloat_Flag; 523 524 if (8 == sizeof(void*)) { 525 info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag; 526 } 527} 528 529// fRecord OK 530void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const { 531 const SkPictureData* data = fData.get(); 532 533 // If we're a new-format picture, backport to old format for serialization. 534 SkAutoTDelete<SkPicture> oldFormat; 535 if (NULL == data && fRecord.get()) { 536 oldFormat.reset(backport(*fRecord, this->cullRect())); 537 data = oldFormat->fData.get(); 538 SkASSERT(data); 539 } 540 541 SkPictInfo info; 542 this->createHeader(&info); 543 SkASSERT(sizeof(SkPictInfo) == 32); 544 stream->write(&info, sizeof(info)); 545 546 if (data) { 547 stream->writeBool(true); 548 data->serialize(stream, encoder); 549 } else { 550 stream->writeBool(false); 551 } 552} 553 554// fRecord OK 555void SkPicture::flatten(SkWriteBuffer& buffer) const { 556 const SkPictureData* data = fData.get(); 557 558 // If we're a new-format picture, backport to old format for serialization. 559 SkAutoTDelete<SkPicture> oldFormat; 560 if (NULL == data && fRecord.get()) { 561 oldFormat.reset(backport(*fRecord, this->cullRect())); 562 data = oldFormat->fData.get(); 563 SkASSERT(data); 564 } 565 566 SkPictInfo info; 567 this->createHeader(&info); 568 buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic)); 569 buffer.writeUInt(info.fVersion); 570 buffer.writeRect(info.fCullRect); 571 buffer.writeUInt(info.fFlags); 572 573 if (data) { 574 buffer.writeBool(true); 575 data->flatten(buffer); 576 } else { 577 buffer.writeBool(false); 578 } 579} 580 581#if SK_SUPPORT_GPU 582// fRecord OK 583bool SkPicture::suitableForGpuRasterization(GrContext* context, const char **reason) const { 584 if (fRecord.get()) { 585 return fAnalysis.suitableForGpuRasterization(reason, 0); 586 } 587 if (NULL == fData.get()) { 588 if (reason) { 589 *reason = "Missing internal data."; 590 } 591 return false; 592 } 593 594 return fData->suitableForGpuRasterization(context, reason); 595} 596#endif 597 598// fRecord OK 599bool SkPicture::hasText() const { 600 if (fRecord.get()) { 601 return fAnalysis.fHasText; 602 } 603 if (fData.get()) { 604 return fData->hasText(); 605 } 606 SkFAIL("Unreachable"); 607 return false; 608} 609 610// fRecord OK 611bool SkPicture::willPlayBackBitmaps() const { 612 if (fRecord.get()) { 613 return fAnalysis.fWillPlaybackBitmaps; 614 } 615 if (fData.get()) { 616 return fData->containsBitmaps(); 617 } 618 SkFAIL("Unreachable"); 619 return false; 620} 621 622// fRecord OK 623static int32_t next_picture_generation_id() { 624 static int32_t gPictureGenerationID = 0; 625 // do a loop in case our global wraps around, as we never want to 626 // return a 0 627 int32_t genID; 628 do { 629 genID = sk_atomic_inc(&gPictureGenerationID) + 1; 630 } while (SK_InvalidGenID == genID); 631 return genID; 632} 633 634// fRecord OK 635uint32_t SkPicture::uniqueID() const { 636 if (SK_InvalidGenID == fUniqueID) { 637 fUniqueID = next_picture_generation_id(); 638 } 639 return fUniqueID; 640} 641 642 643static SkRecord* optimized(SkRecord* r) { 644#ifdef SK_PICTURE_OPTIMIZE_SK_RECORD 645 SkRecordOptimize(r); 646#endif 647 return r; 648} 649 650// fRecord OK 651SkPicture::SkPicture(SkScalar width, SkScalar height, SkRecord* record, SkBBoxHierarchy* bbh) 652 : fCullWidth(width) 653 , fCullHeight(height) 654 , fRecord(optimized(record)) 655 , fBBH(SkSafeRef(bbh)) 656 , fAnalysis(*fRecord) { 657 // TODO: delay as much of this work until just before first playback? 658 if (fBBH.get()) { 659 SkRecordFillBounds(*fRecord, fBBH.get()); 660 } 661 this->needsNewGenID(); 662} 663 664// Note that we are assuming that this entry point will only be called from 665// one thread. Currently the only client of this method is 666// SkGpuDevice::EXPERIMENTAL_optimize which should be only called from a single 667// thread. 668void SkPicture::addDeletionListener(DeletionListener* listener) const { 669 SkASSERT(listener); 670 671 *fDeletionListeners.append() = SkRef(listener); 672} 673 674void SkPicture::callDeletionListeners() { 675 for (int i = 0; i < fDeletionListeners.count(); ++i) { 676 fDeletionListeners[i]->onDeletion(this->uniqueID()); 677 } 678 679 fDeletionListeners.unrefAll(); 680} 681 682// fRecord OK 683int SkPicture::approximateOpCount() const { 684 SkASSERT(fRecord.get() || fData.get()); 685 if (fRecord.get()) { 686 return fRecord->count(); 687 } 688 if (fData.get()) { 689 return fData->opCount(); 690 } 691 return 0; 692} 693