Index: src/core/SkPicture.cpp |
diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp |
index 0145cbe5d7adab19f0b8602a1a5e0bb69c297c0f..6c1f6cb759ee5f9338877e2bf3ea5bb37dc03ae8 100644 |
--- a/src/core/SkPicture.cpp |
+++ b/src/core/SkPicture.cpp |
@@ -5,330 +5,107 @@ |
* found in the LICENSE file. |
*/ |
- |
-#include "SkPictureFlat.h" |
+#include "SkAtomics.h" |
+#include "SkMessageBus.h" |
+#include "SkPicture.h" |
#include "SkPictureData.h" |
#include "SkPicturePlayback.h" |
#include "SkPictureRecord.h" |
#include "SkPictureRecorder.h" |
-#include "SkAtomics.h" |
-#include "SkBitmapDevice.h" |
-#include "SkCanvas.h" |
-#include "SkChunkAlloc.h" |
-#include "SkMessageBus.h" |
-#include "SkPaintPriv.h" |
-#include "SkPathEffect.h" |
-#include "SkPicture.h" |
-#include "SkRegion.h" |
-#include "SkShader.h" |
-#include "SkStream.h" |
-#include "SkTDArray.h" |
-#include "SkTLogic.h" |
-#include "SkTSearch.h" |
-#include "SkTime.h" |
- |
-#include "SkReader32.h" |
-#include "SkWriter32.h" |
-#include "SkRTree.h" |
- |
-#if SK_SUPPORT_GPU |
-#include "GrContext.h" |
-#endif |
- |
-#include "SkRecord.h" |
-#include "SkRecordDraw.h" |
-#include "SkRecordOpts.h" |
-#include "SkRecorder.h" |
- |
DECLARE_SKMESSAGEBUS_MESSAGE(SkPicture::DeletionMessage); |
-template <typename T> int SafeCount(const T* obj) { |
- return obj ? obj->count() : 0; |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-namespace { |
- |
-// Some commands have a paint, some have an optional paint. Either way, get back a pointer. |
-static const SkPaint* AsPtr(const SkPaint& p) { return &p; } |
-static const SkPaint* AsPtr(const SkRecords::Optional<SkPaint>& p) { return p; } |
- |
-/** SkRecords visitor to determine whether an instance may require an |
- "external" bitmap to rasterize. May return false positives. |
- Does not return true for bitmap text. |
- |
- Expected use is to determine whether images need to be decoded before |
- rasterizing a particular SkRecord. |
- */ |
-struct BitmapTester { |
- // Helpers. These create HasMember_bitmap and HasMember_paint. |
- SK_CREATE_MEMBER_DETECTOR(bitmap); |
- SK_CREATE_MEMBER_DETECTOR(paint); |
- |
+/* SkPicture impl. This handles generic responsibilities like unique IDs and serialization. */ |
- // Main entry for visitor: |
- // If the command is a DrawPicture, recurse. |
- // If the command has a bitmap directly, return true. |
- // If the command has a paint and the paint has a bitmap, return true. |
- // Otherwise, return false. |
- bool operator()(const SkRecords::DrawPicture& op) { return op.picture->willPlayBackBitmaps(); } |
- |
- template <typename T> |
- bool operator()(const T& r) { return CheckBitmap(r); } |
- |
- |
- // If the command has a bitmap, of course we're going to play back bitmaps. |
- template <typename T> |
- static SK_WHEN(HasMember_bitmap<T>, bool) CheckBitmap(const T&) { return true; } |
- |
- // If not, look for one in its paint (if it has a paint). |
- template <typename T> |
- static SK_WHEN(!HasMember_bitmap<T>, bool) CheckBitmap(const T& r) { return CheckPaint(r); } |
- |
- // If we have a paint, dig down into the effects looking for a bitmap. |
- template <typename T> |
- static SK_WHEN(HasMember_paint<T>, bool) CheckPaint(const T& r) { |
- const SkPaint* paint = AsPtr(r.paint); |
- if (paint) { |
- const SkShader* shader = paint->getShader(); |
- if (shader && |
- shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) { |
- return true; |
- } |
- } |
- return false; |
- } |
- |
- // If we don't have a paint, that non-paint has no bitmap. |
- template <typename T> |
- static SK_WHEN(!HasMember_paint<T>, bool) CheckPaint(const T&) { return false; } |
-}; |
- |
-bool WillPlaybackBitmaps(const SkRecord& record) { |
- BitmapTester tester; |
- for (unsigned i = 0; i < record.count(); i++) { |
- if (record.visit<bool>(i, tester)) { |
- return true; |
- } |
- } |
- return false; |
-} |
- |
-// SkRecord visitor to find recorded text. |
-struct TextHunter { |
- // All ops with text have that text as a char array member named "text". |
- SK_CREATE_MEMBER_DETECTOR(text); |
- bool operator()(const SkRecords::DrawPicture& op) { return op.picture->hasText(); } |
- template <typename T> SK_WHEN(HasMember_text<T>, bool) operator()(const T&) { return true; } |
- template <typename T> SK_WHEN(!HasMember_text<T>, bool) operator()(const T&) { return false; } |
-}; |
- |
-} // namespace |
- |
-/** SkRecords visitor to determine heuristically whether or not a SkPicture |
- will be performant when rasterized on the GPU. |
- */ |
-struct SkPicture::PathCounter { |
- SK_CREATE_MEMBER_DETECTOR(paint); |
- |
- PathCounter() : fNumSlowPathsAndDashEffects(0) {} |
- |
- // Recurse into nested pictures. |
- void operator()(const SkRecords::DrawPicture& op) { |
- const SkPicture::Analysis& analysis = op.picture->analysis(); |
- fNumSlowPathsAndDashEffects += analysis.fNumSlowPathsAndDashEffects; |
- } |
- |
- void checkPaint(const SkPaint* paint) { |
- if (paint && paint->getPathEffect()) { |
- // Initially assume it's slow. |
- fNumSlowPathsAndDashEffects++; |
- } |
- } |
- |
- void operator()(const SkRecords::DrawPoints& op) { |
- this->checkPaint(&op.paint); |
- const SkPathEffect* effect = op.paint.getPathEffect(); |
- if (effect) { |
- SkPathEffect::DashInfo info; |
- SkPathEffect::DashType dashType = effect->asADash(&info); |
- if (2 == op.count && SkPaint::kRound_Cap != op.paint.getStrokeCap() && |
- SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) { |
- fNumSlowPathsAndDashEffects--; |
- } |
- } |
- } |
- |
- void operator()(const SkRecords::DrawPath& op) { |
- this->checkPaint(&op.paint); |
- if (op.paint.isAntiAlias() && !op.path.isConvex()) { |
- SkPaint::Style paintStyle = op.paint.getStyle(); |
- const SkRect& pathBounds = op.path.getBounds(); |
- if (SkPaint::kStroke_Style == paintStyle && |
- 0 == op.paint.getStrokeWidth()) { |
- // AA hairline concave path is not slow. |
- } else if (SkPaint::kFill_Style == paintStyle && pathBounds.width() < 64.f && |
- pathBounds.height() < 64.f && !op.path.isVolatile()) { |
- // AADF eligible concave path is not slow. |
- } else { |
- fNumSlowPathsAndDashEffects++; |
- } |
- } |
- } |
- |
- template <typename T> |
- SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) { |
- this->checkPaint(AsPtr(op.paint)); |
- } |
- |
- template <typename T> |
- SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { /* do nothing */ } |
- |
- int fNumSlowPathsAndDashEffects; |
-}; |
- |
-SkPicture::Analysis::Analysis(const SkRecord& record) { |
- fWillPlaybackBitmaps = WillPlaybackBitmaps(record); |
- |
- PathCounter counter; |
- for (unsigned i = 0; i < record.count(); i++) { |
- record.visit<void>(i, counter); |
- } |
- fNumSlowPathsAndDashEffects = SkTMin<int>(counter.fNumSlowPathsAndDashEffects, 255); |
- |
- fHasText = false; |
- TextHunter text; |
- for (unsigned i = 0; i < record.count(); i++) { |
- if (record.visit<bool>(i, text)) { |
- fHasText = true; |
- break; |
- } |
- } |
-} |
- |
-bool SkPicture::Analysis::suitableForGpuRasterization(const char** reason, |
- int sampleCount) const { |
- // TODO: the heuristic used here needs to be refined |
- static const int kNumSlowPathsTol = 6; |
- |
- bool ret = fNumSlowPathsAndDashEffects < kNumSlowPathsTol; |
- |
- if (!ret && reason) { |
- *reason = "Too many slow paths (either concave or dashed)."; |
- } |
- return ret; |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-int SkPicture::drawableCount() const { |
- return fDrawablePicts.get() ? fDrawablePicts->count() : 0; |
-} |
- |
-SkPicture const* const* SkPicture::drawablePicts() const { |
- return fDrawablePicts.get() ? fDrawablePicts->begin() : NULL; |
-} |
+SkPicture::SkPicture() : fUniqueID(0) {} |
SkPicture::~SkPicture() { |
+ // TODO: move this to ~SkBigPicture() only? |
+ |
// If the ID is still zero, no one has read it, so no need to send this message. |
uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed); |
if (id != 0) { |
- SkPicture::DeletionMessage msg; |
- msg.fUniqueID = id; |
+ SkPicture::DeletionMessage msg = { (int32_t)id }; |
SkMessageBus<SkPicture::DeletionMessage>::Post(msg); |
} |
} |
-const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData( |
- SkPicture::AccelData::Key key) const { |
- if (fAccelData.get() && fAccelData->getKey() == key) { |
- return fAccelData.get(); |
- } |
- return NULL; |
-} |
- |
-SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() { |
- static int32_t gNextID = 0; |
- |
- int32_t id = sk_atomic_inc(&gNextID); |
- if (id >= 1 << (8 * sizeof(Domain))) { |
- SK_CRASH(); |
+uint32_t SkPicture::uniqueID() const { |
+ static uint32_t gNextID = 1; |
+ uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed); |
+ while (id == 0) { |
+ uint32_t next = sk_atomic_fetch_add(&gNextID, 1u); |
+ if (sk_atomic_compare_exchange(&fUniqueID, &id, next, |
+ sk_memory_order_relaxed, |
+ sk_memory_order_relaxed)) { |
+ id = next; |
+ } else { |
+ // sk_atomic_compare_exchange replaced id with the current value of fUniqueID. |
+ } |
} |
- |
- return static_cast<Domain>(id); |
+ return id; |
} |
-/////////////////////////////////////////////////////////////////////////////// |
+static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' }; |
-void SkPicture::playback(SkCanvas* canvas, AbortCallback* callback) const { |
- SkASSERT(canvas); |
+SkPictInfo SkPicture::createHeader() const { |
+ SkPictInfo info; |
+ // Copy magic bytes at the beginning of the header |
+ static_assert(sizeof(kMagic) == 8, ""); |
+ static_assert(sizeof(kMagic) == sizeof(info.fMagic), ""); |
+ memcpy(info.fMagic, kMagic, sizeof(kMagic)); |
- // If the query contains the whole picture, don't bother with the BBH. |
- SkRect clipBounds = { 0, 0, 0, 0 }; |
- (void)canvas->getClipBounds(&clipBounds); |
- const bool useBBH = !clipBounds.contains(this->cullRect()); |
+ // Set picture info after magic bytes in the header |
+ info.fVersion = CURRENT_PICTURE_VERSION; |
+ info.fCullRect = this->cullRect(); |
+ info.fFlags = SkPictInfo::kCrossProcess_Flag; |
+ // TODO: remove this flag, since we're always float (now) |
+ info.fFlags |= SkPictInfo::kScalarIsFloat_Flag; |
- SkRecordDraw(*fRecord, canvas, this->drawablePicts(), NULL, this->drawableCount(), |
- useBBH ? fBBH.get() : NULL, callback); |
+ if (8 == sizeof(void*)) { |
+ info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag; |
+ } |
+ return info; |
} |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-#include "SkStream.h" |
- |
-static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' }; |
- |
bool SkPicture::IsValidPictInfo(const SkPictInfo& info) { |
if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) { |
return false; |
} |
- |
- if (info.fVersion < MIN_PICTURE_VERSION || |
- info.fVersion > CURRENT_PICTURE_VERSION) { |
+ if (info.fVersion < MIN_PICTURE_VERSION || info.fVersion > CURRENT_PICTURE_VERSION) { |
return false; |
} |
- |
return true; |
} |
bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) { |
- if (NULL == stream) { |
+ if (!stream) { |
return false; |
} |
- // Check magic bytes. |
SkPictInfo info; |
SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); |
- |
if (!stream->read(&info.fMagic, sizeof(kMagic))) { |
return false; |
} |
- info.fVersion = stream->readU32(); |
- info.fCullRect.fLeft = stream->readScalar(); |
- info.fCullRect.fTop = stream->readScalar(); |
- info.fCullRect.fRight = stream->readScalar(); |
+ info.fVersion = stream->readU32(); |
+ info.fCullRect.fLeft = stream->readScalar(); |
+ info.fCullRect.fTop = stream->readScalar(); |
+ info.fCullRect.fRight = stream->readScalar(); |
info.fCullRect.fBottom = stream->readScalar(); |
+ info.fFlags = stream->readU32(); |
- info.fFlags = stream->readU32(); |
- |
- if (!IsValidPictInfo(info)) { |
- return false; |
- } |
- |
- if (pInfo != NULL) { |
- *pInfo = info; |
+ if (IsValidPictInfo(info)) { |
+ if (pInfo) { *pInfo = info; } |
+ return true; |
} |
- return true; |
+ return false; |
} |
bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) { |
- // Check magic bytes. |
SkPictInfo info; |
SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); |
- |
if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) { |
return false; |
} |
@@ -337,32 +114,29 @@ bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo |
buffer->readRect(&info.fCullRect); |
info.fFlags = buffer->readUInt(); |
- if (!IsValidPictInfo(info)) { |
- return false; |
+ if (IsValidPictInfo(info)) { |
+ if (pInfo) { *pInfo = info; } |
+ return true; |
} |
- |
- if (pInfo != NULL) { |
- *pInfo = info; |
- } |
- return true; |
+ return false; |
} |
SkPicture* SkPicture::Forwardport(const SkPictInfo& info, const SkPictureData* data) { |
if (!data) { |
- return NULL; |
+ return nullptr; |
} |
SkPicturePlayback playback(data); |
SkPictureRecorder r; |
playback.draw(r.beginRecording(SkScalarCeilToInt(info.fCullRect.width()), |
SkScalarCeilToInt(info.fCullRect.height())), |
- NULL/*no callback*/); |
+ nullptr/*no callback*/); |
return r.endRecording(); |
} |
SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) { |
SkPictInfo info; |
if (!InternalOnly_StreamIsSKP(stream, &info) || !stream->readBool()) { |
- return NULL; |
+ return nullptr; |
} |
SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromStream(stream, info, proc)); |
return Forwardport(info, data); |
@@ -371,45 +145,24 @@ SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc pro |
SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) { |
SkPictInfo info; |
if (!InternalOnly_BufferIsSKP(&buffer, &info) || !buffer.readBool()) { |
- return NULL; |
+ return nullptr; |
} |
SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromBuffer(buffer, info)); |
return Forwardport(info, data); |
} |
-void SkPicture::createHeader(SkPictInfo* info) const { |
- // Copy magic bytes at the beginning of the header |
- SkASSERT(sizeof(kMagic) == 8); |
- SkASSERT(sizeof(kMagic) == sizeof(info->fMagic)); |
- memcpy(info->fMagic, kMagic, sizeof(kMagic)); |
- |
- // Set picture info after magic bytes in the header |
- info->fVersion = CURRENT_PICTURE_VERSION; |
- info->fCullRect = this->cullRect(); |
- info->fFlags = SkPictInfo::kCrossProcess_Flag; |
- // TODO: remove this flag, since we're always float (now) |
- info->fFlags |= SkPictInfo::kScalarIsFloat_Flag; |
- |
- if (8 == sizeof(void*)) { |
- info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag; |
- } |
-} |
- |
-// This for compatibility with serialization code only. This is not cheap. |
-SkPictureData* SkPicture::Backport(const SkRecord& src, const SkPictInfo& info, |
- SkPicture const* const drawablePicts[], int drawableCount) { |
+SkPictureData* SkPicture::backport() const { |
+ SkPictInfo info = this->createHeader(); |
SkPictureRecord rec(SkISize::Make(info.fCullRect.width(), info.fCullRect.height()), 0/*flags*/); |
rec.beginRecording(); |
- SkRecordDraw(src, &rec, drawablePicts, NULL, drawableCount, NULL/*bbh*/, NULL/*callback*/); |
+ this->playback(&rec); |
rec.endRecording(); |
return SkNEW_ARGS(SkPictureData, (rec, info, false/*deep copy ops?*/)); |
} |
void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) const { |
- SkPictInfo info; |
- this->createHeader(&info); |
- SkAutoTDelete<SkPictureData> data(Backport(*fRecord, info, this->drawablePicts(), |
- this->drawableCount())); |
+ SkPictInfo info = this->createHeader(); |
+ SkAutoTDelete<SkPictureData> data(this->backport()); |
stream->write(&info, sizeof(info)); |
if (data) { |
@@ -421,10 +174,8 @@ void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) |
} |
void SkPicture::flatten(SkWriteBuffer& buffer) const { |
- SkPictInfo info; |
- this->createHeader(&info); |
- SkAutoTDelete<SkPictureData> data(Backport(*fRecord, info, this->drawablePicts(), |
- this->drawableCount())); |
+ SkPictInfo info = this->createHeader(); |
+ SkAutoTDelete<SkPictureData> data(this->backport()); |
buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic)); |
buffer.writeUInt(info.fVersion); |
@@ -438,49 +189,10 @@ void SkPicture::flatten(SkWriteBuffer& buffer) const { |
} |
} |
-const SkPicture::Analysis& SkPicture::analysis() const { |
- auto create = [&](){ return SkNEW_ARGS(Analysis, (*fRecord)); }; |
- return *fAnalysis.get(create); |
-} |
- |
-#if SK_SUPPORT_GPU |
-bool SkPicture::suitableForGpuRasterization(GrContext*, const char **reason) const { |
- return this->analysis().suitableForGpuRasterization(reason, 0); |
-} |
-#endif |
- |
-bool SkPicture::hasText() const { return this->analysis().fHasText; } |
-bool SkPicture::willPlayBackBitmaps() const { return this->analysis().fWillPlaybackBitmaps; } |
-int SkPicture::approximateOpCount() const { return fRecord->count(); } |
- |
-SkPicture::SkPicture(const SkRect& cullRect, |
- SkRecord* record, |
- SnapshotArray* drawablePicts, |
- SkBBoxHierarchy* bbh, |
- AccelData* accelData, |
- size_t approxBytesUsedBySubPictures) |
- : fUniqueID(0) |
- , fCullRect(cullRect) |
- , fRecord(record) // Take ownership of caller's ref. |
- , fDrawablePicts(drawablePicts) // Take ownership. |
- , fBBH(bbh) // Take ownership of caller's ref. |
- , fAccelData(accelData) // Take ownership of caller's ref. |
- , fApproxBytesUsedBySubPictures(approxBytesUsedBySubPictures) |
-{} |
- |
- |
-static uint32_t gNextID = 1; |
-uint32_t SkPicture::uniqueID() const { |
- uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed); |
- while (id == 0) { |
- uint32_t next = sk_atomic_fetch_add(&gNextID, 1u); |
- if (sk_atomic_compare_exchange(&fUniqueID, &id, next, |
- sk_memory_order_relaxed, |
- sk_memory_order_relaxed)) { |
- id = next; |
- } else { |
- // sk_atomic_compare_exchange replaced id with the current value of fUniqueID. |
- } |
+bool SkPicture::suitableForGpuRasterization(GrContext*, const char** whyNot) const { |
+ if (this->numSlowPaths() > 5) { |
+ if (whyNot) { *whyNot = "Too many slow paths (either concave or dashed)."; } |
+ return false; |
} |
- return id; |
+ return true; |
} |