1/* 2 * Copyright 2007 The Android Open Source Project 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 8 9#include "SkPictureFlat.h" 10#include "SkPictureData.h" 11#include "SkPicturePlayback.h" 12#include "SkPictureRecord.h" 13#include "SkPictureRecorder.h" 14 15#include "SkAtomics.h" 16#include "SkBitmapDevice.h" 17#include "SkCanvas.h" 18#include "SkChunkAlloc.h" 19#include "SkMessageBus.h" 20#include "SkPaintPriv.h" 21#include "SkPathEffect.h" 22#include "SkPicture.h" 23#include "SkRegion.h" 24#include "SkShader.h" 25#include "SkStream.h" 26#include "SkTDArray.h" 27#include "SkTLogic.h" 28#include "SkTSearch.h" 29#include "SkTime.h" 30 31#include "SkReader32.h" 32#include "SkWriter32.h" 33#include "SkRTree.h" 34 35#if SK_SUPPORT_GPU 36#include "GrContext.h" 37#endif 38 39#include "SkRecord.h" 40#include "SkRecordDraw.h" 41#include "SkRecordOpts.h" 42#include "SkRecorder.h" 43 44DECLARE_SKMESSAGEBUS_MESSAGE(SkPicture::DeletionMessage); 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() : fNumSlowPathsAndDashEffects(0) {} 137 138 // Recurse into nested pictures. 139 void operator()(const SkRecords::DrawPicture& op) { 140 const SkPicture::Analysis& analysis = op.picture->analysis(); 141 fNumSlowPathsAndDashEffects += analysis.fNumSlowPathsAndDashEffects; 142 } 143 144 void checkPaint(const SkPaint* paint) { 145 if (paint && paint->getPathEffect()) { 146 // Initially assume it's slow. 147 fNumSlowPathsAndDashEffects++; 148 } 149 } 150 151 void operator()(const SkRecords::DrawPoints& op) { 152 this->checkPaint(&op.paint); 153 const SkPathEffect* effect = op.paint.getPathEffect(); 154 if (effect) { 155 SkPathEffect::DashInfo info; 156 SkPathEffect::DashType dashType = effect->asADash(&info); 157 if (2 == op.count && SkPaint::kRound_Cap != op.paint.getStrokeCap() && 158 SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) { 159 fNumSlowPathsAndDashEffects--; 160 } 161 } 162 } 163 164 void operator()(const SkRecords::DrawPath& op) { 165 this->checkPaint(&op.paint); 166 if (op.paint.isAntiAlias() && !op.path.isConvex()) { 167 SkPaint::Style paintStyle = op.paint.getStyle(); 168 const SkRect& pathBounds = op.path.getBounds(); 169 if (SkPaint::kStroke_Style == paintStyle && 170 0 == op.paint.getStrokeWidth()) { 171 // AA hairline concave path is not slow. 172 } else if (SkPaint::kFill_Style == paintStyle && pathBounds.width() < 64.f && 173 pathBounds.height() < 64.f && !op.path.isVolatile()) { 174 // AADF eligible concave path is not slow. 175 } else { 176 fNumSlowPathsAndDashEffects++; 177 } 178 } 179 } 180 181 template <typename T> 182 SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) { 183 this->checkPaint(AsPtr(op.paint)); 184 } 185 186 template <typename T> 187 SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { /* do nothing */ } 188 189 int fNumSlowPathsAndDashEffects; 190}; 191 192SkPicture::Analysis::Analysis(const SkRecord& record) { 193 fWillPlaybackBitmaps = WillPlaybackBitmaps(record); 194 195 PathCounter counter; 196 for (unsigned i = 0; i < record.count(); i++) { 197 record.visit<void>(i, counter); 198 } 199 fNumSlowPathsAndDashEffects = SkTMin<int>(counter.fNumSlowPathsAndDashEffects, 255); 200 201 fHasText = false; 202 TextHunter text; 203 for (unsigned i = 0; i < record.count(); i++) { 204 if (record.visit<bool>(i, text)) { 205 fHasText = true; 206 break; 207 } 208 } 209} 210 211bool SkPicture::Analysis::suitableForGpuRasterization(const char** reason, 212 int sampleCount) const { 213 // TODO: the heuristic used here needs to be refined 214 static const int kNumSlowPathsTol = 6; 215 216 bool ret = fNumSlowPathsAndDashEffects < kNumSlowPathsTol; 217 218 if (!ret && reason) { 219 *reason = "Too many slow paths (either concave or dashed)."; 220 } 221 return ret; 222} 223 224/////////////////////////////////////////////////////////////////////////////// 225 226int SkPicture::drawableCount() const { 227 return fDrawablePicts.get() ? fDrawablePicts->count() : 0; 228} 229 230SkPicture const* const* SkPicture::drawablePicts() const { 231 return fDrawablePicts.get() ? fDrawablePicts->begin() : NULL; 232} 233 234SkPicture::~SkPicture() { 235 // If the ID is still zero, no one has read it, so no need to send this message. 236 uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed); 237 if (id != 0) { 238 SkPicture::DeletionMessage msg; 239 msg.fUniqueID = id; 240 SkMessageBus<SkPicture::DeletionMessage>::Post(msg); 241 } 242} 243 244const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData( 245 SkPicture::AccelData::Key key) const { 246 if (fAccelData.get() && fAccelData->getKey() == key) { 247 return fAccelData.get(); 248 } 249 return NULL; 250} 251 252SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() { 253 static int32_t gNextID = 0; 254 255 int32_t id = sk_atomic_inc(&gNextID); 256 if (id >= 1 << (8 * sizeof(Domain))) { 257 SK_CRASH(); 258 } 259 260 return static_cast<Domain>(id); 261} 262 263/////////////////////////////////////////////////////////////////////////////// 264 265void SkPicture::playback(SkCanvas* canvas, AbortCallback* callback) const { 266 SkASSERT(canvas); 267 268 // If the query contains the whole picture, don't bother with the BBH. 269 SkRect clipBounds = { 0, 0, 0, 0 }; 270 (void)canvas->getClipBounds(&clipBounds); 271 const bool useBBH = !clipBounds.contains(this->cullRect()); 272 273 SkRecordDraw(*fRecord, canvas, this->drawablePicts(), NULL, this->drawableCount(), 274 useBBH ? fBBH.get() : NULL, callback); 275} 276 277/////////////////////////////////////////////////////////////////////////////// 278 279#include "SkStream.h" 280 281static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' }; 282 283bool SkPicture::IsValidPictInfo(const SkPictInfo& info) { 284 if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) { 285 return false; 286 } 287 288 if (info.fVersion < MIN_PICTURE_VERSION || 289 info.fVersion > CURRENT_PICTURE_VERSION) { 290 return false; 291 } 292 293 return true; 294} 295 296bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) { 297 if (NULL == stream) { 298 return false; 299 } 300 301 // Check magic bytes. 302 SkPictInfo info; 303 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); 304 305 if (!stream->read(&info.fMagic, sizeof(kMagic))) { 306 return false; 307 } 308 309 info.fVersion = stream->readU32(); 310 info.fCullRect.fLeft = stream->readScalar(); 311 info.fCullRect.fTop = stream->readScalar(); 312 info.fCullRect.fRight = stream->readScalar(); 313 info.fCullRect.fBottom = stream->readScalar(); 314 315 info.fFlags = stream->readU32(); 316 317 if (!IsValidPictInfo(info)) { 318 return false; 319 } 320 321 if (pInfo != NULL) { 322 *pInfo = info; 323 } 324 return true; 325} 326 327bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) { 328 // Check magic bytes. 329 SkPictInfo info; 330 SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); 331 332 if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) { 333 return false; 334 } 335 336 info.fVersion = buffer->readUInt(); 337 buffer->readRect(&info.fCullRect); 338 info.fFlags = buffer->readUInt(); 339 340 if (!IsValidPictInfo(info)) { 341 return false; 342 } 343 344 if (pInfo != NULL) { 345 *pInfo = info; 346 } 347 return true; 348} 349 350SkPicture* SkPicture::Forwardport(const SkPictInfo& info, const SkPictureData* data) { 351 if (!data) { 352 return NULL; 353 } 354 SkPicturePlayback playback(data); 355 SkPictureRecorder r; 356 playback.draw(r.beginRecording(SkScalarCeilToInt(info.fCullRect.width()), 357 SkScalarCeilToInt(info.fCullRect.height())), 358 NULL/*no callback*/); 359 return r.endRecording(); 360} 361 362SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) { 363 SkPictInfo info; 364 if (!InternalOnly_StreamIsSKP(stream, &info) || !stream->readBool()) { 365 return NULL; 366 } 367 SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromStream(stream, info, proc)); 368 return Forwardport(info, data); 369} 370 371SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) { 372 SkPictInfo info; 373 if (!InternalOnly_BufferIsSKP(&buffer, &info) || !buffer.readBool()) { 374 return NULL; 375 } 376 SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromBuffer(buffer, info)); 377 return Forwardport(info, data); 378} 379 380void SkPicture::createHeader(SkPictInfo* info) const { 381 // Copy magic bytes at the beginning of the header 382 SkASSERT(sizeof(kMagic) == 8); 383 SkASSERT(sizeof(kMagic) == sizeof(info->fMagic)); 384 memcpy(info->fMagic, kMagic, sizeof(kMagic)); 385 386 // Set picture info after magic bytes in the header 387 info->fVersion = CURRENT_PICTURE_VERSION; 388 info->fCullRect = this->cullRect(); 389 info->fFlags = SkPictInfo::kCrossProcess_Flag; 390 // TODO: remove this flag, since we're always float (now) 391 info->fFlags |= SkPictInfo::kScalarIsFloat_Flag; 392 393 if (8 == sizeof(void*)) { 394 info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag; 395 } 396} 397 398// This for compatibility with serialization code only. This is not cheap. 399SkPictureData* SkPicture::Backport(const SkRecord& src, const SkPictInfo& info, 400 SkPicture const* const drawablePicts[], int drawableCount) { 401 SkPictureRecord rec(SkISize::Make(info.fCullRect.width(), info.fCullRect.height()), 0/*flags*/); 402 rec.beginRecording(); 403 SkRecordDraw(src, &rec, drawablePicts, NULL, drawableCount, NULL/*bbh*/, NULL/*callback*/); 404 rec.endRecording(); 405 return SkNEW_ARGS(SkPictureData, (rec, info, false/*deep copy ops?*/)); 406} 407 408void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) const { 409 SkPictInfo info; 410 this->createHeader(&info); 411 SkAutoTDelete<SkPictureData> data(Backport(*fRecord, info, this->drawablePicts(), 412 this->drawableCount())); 413 414 stream->write(&info, sizeof(info)); 415 if (data) { 416 stream->writeBool(true); 417 data->serialize(stream, pixelSerializer); 418 } else { 419 stream->writeBool(false); 420 } 421} 422 423void SkPicture::flatten(SkWriteBuffer& buffer) const { 424 SkPictInfo info; 425 this->createHeader(&info); 426 SkAutoTDelete<SkPictureData> data(Backport(*fRecord, info, this->drawablePicts(), 427 this->drawableCount())); 428 429 buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic)); 430 buffer.writeUInt(info.fVersion); 431 buffer.writeRect(info.fCullRect); 432 buffer.writeUInt(info.fFlags); 433 if (data) { 434 buffer.writeBool(true); 435 data->flatten(buffer); 436 } else { 437 buffer.writeBool(false); 438 } 439} 440 441const SkPicture::Analysis& SkPicture::analysis() const { 442 auto create = [&](){ return SkNEW_ARGS(Analysis, (*fRecord)); }; 443 return *fAnalysis.get(create); 444} 445 446#if SK_SUPPORT_GPU 447bool SkPicture::suitableForGpuRasterization(GrContext*, const char **reason) const { 448 return this->analysis().suitableForGpuRasterization(reason, 0); 449} 450#endif 451 452bool SkPicture::hasText() const { return this->analysis().fHasText; } 453bool SkPicture::willPlayBackBitmaps() const { return this->analysis().fWillPlaybackBitmaps; } 454int SkPicture::approximateOpCount() const { return fRecord->count(); } 455 456SkPicture::SkPicture(const SkRect& cullRect, 457 SkRecord* record, 458 SnapshotArray* drawablePicts, 459 SkBBoxHierarchy* bbh, 460 AccelData* accelData, 461 size_t approxBytesUsedBySubPictures) 462 : fUniqueID(0) 463 , fCullRect(cullRect) 464 , fRecord(record) // Take ownership of caller's ref. 465 , fDrawablePicts(drawablePicts) // Take ownership. 466 , fBBH(bbh) // Take ownership of caller's ref. 467 , fAccelData(accelData) // Take ownership of caller's ref. 468 , fApproxBytesUsedBySubPictures(approxBytesUsedBySubPictures) 469{} 470 471 472static uint32_t gNextID = 1; 473uint32_t SkPicture::uniqueID() const { 474 uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed); 475 while (id == 0) { 476 uint32_t next = sk_atomic_fetch_add(&gNextID, 1u); 477 if (sk_atomic_compare_exchange(&fUniqueID, &id, next, 478 sk_memory_order_relaxed, 479 sk_memory_order_relaxed)) { 480 id = next; 481 } else { 482 // sk_atomic_compare_exchange replaced id with the current value of fUniqueID. 483 } 484 } 485 return id; 486} 487