| Index: src/core/SkPicture.cpp
|
| diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp
|
| index 8825123336c14d472217c4934b0cf2cbe4bdbf04..13964065402aa022443ebe49e165d4eeca965da0 100644
|
| --- a/src/core/SkPicture.cpp
|
| +++ b/src/core/SkPicture.cpp
|
| @@ -19,11 +19,13 @@
|
| #include "SkChunkAlloc.h"
|
| #include "SkDrawPictureCallback.h"
|
| #include "SkPaintPriv.h"
|
| +#include "SkPathEffect.h"
|
| #include "SkPicture.h"
|
| -#include "SkRecordAnalysis.h"
|
| #include "SkRegion.h"
|
| +#include "SkShader.h"
|
| #include "SkStream.h"
|
| #include "SkTDArray.h"
|
| +#include "SkTLogic.h"
|
| #include "SkTSearch.h"
|
| #include "SkTime.h"
|
|
|
| @@ -46,12 +48,184 @@ template <typename T> int SafeCount(const T* obj) {
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
|
|
| +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);
|
| +
|
| +
|
| + // Main entry for visitor:
|
| + // 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.
|
| + 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;
|
| +}
|
| +
|
| +/** SkRecords visitor to determine heuristically whether or not a SkPicture
|
| + will be performant when rasterized on the GPU.
|
| + */
|
| +struct PathCounter {
|
| + SK_CREATE_MEMBER_DETECTOR(paint);
|
| +
|
| + PathCounter()
|
| + : numPaintWithPathEffectUses (0)
|
| + , numFastPathDashEffects (0)
|
| + , numAAConcavePaths (0)
|
| + , numAAHairlineConcavePaths (0) {
|
| + }
|
| +
|
| + void checkPaint(const SkPaint* paint) {
|
| + if (paint && paint->getPathEffect()) {
|
| + numPaintWithPathEffectUses++;
|
| + }
|
| + }
|
| +
|
| + 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) {
|
| + numFastPathDashEffects++;
|
| + }
|
| + }
|
| + }
|
| +
|
| + void operator()(const SkRecords::DrawPath& op) {
|
| + this->checkPaint(&op.paint);
|
| + if (op.paint.isAntiAlias() && !op.path.isConvex()) {
|
| + numAAConcavePaths++;
|
| +
|
| + if (SkPaint::kStroke_Style == op.paint.getStyle() &&
|
| + 0 == op.paint.getStrokeWidth()) {
|
| + numAAHairlineConcavePaths++;
|
| + }
|
| + }
|
| + }
|
| +
|
| + 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 numPaintWithPathEffectUses;
|
| + int numFastPathDashEffects;
|
| + int numAAConcavePaths;
|
| + int numAAHairlineConcavePaths;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +void SkPicture::Analysis::init(const SkRecord& record) {
|
| +
|
| + fWillPlaybackBitmaps = WillPlaybackBitmaps(record);
|
| +
|
| + PathCounter counter;
|
| + for (unsigned i = 0; i < record.count(); i++) {
|
| + record.visit<void>(i, counter);
|
| + }
|
| + fNumPaintWithPathEffectUses = counter.numPaintWithPathEffectUses;
|
| + fNumFastPathDashEffects = counter.numFastPathDashEffects;
|
| + fNumAAConcavePaths = counter.numAAConcavePaths;
|
| + fNumAAHairlineConcavePaths = counter.numAAHairlineConcavePaths;
|
| +}
|
| +
|
| +bool SkPicture::Analysis::suitableForGpuRasterization(const char** reason,
|
| + int sampleCount) const {
|
| + // TODO: the heuristic used here needs to be refined
|
| + static const int kNumPaintWithPathEffectsUsesTol = 1;
|
| + static const int kNumAAConcavePathsTol = 5;
|
| +
|
| + int numNonDashedPathEffects = fNumPaintWithPathEffectUses -
|
| + fNumFastPathDashEffects;
|
| + bool suitableForDash = (0 == fNumPaintWithPathEffectUses) ||
|
| + (numNonDashedPathEffects < kNumPaintWithPathEffectsUsesTol
|
| + && 0 == sampleCount);
|
| +
|
| + bool ret = suitableForDash &&
|
| + (fNumAAConcavePaths - fNumAAHairlineConcavePaths)
|
| + < kNumAAConcavePathsTol;
|
| +
|
| + if (!ret && NULL != reason) {
|
| + if (!suitableForDash) {
|
| + if (0 != sampleCount) {
|
| + *reason = "Can't use multisample on dash effect.";
|
| + } else {
|
| + *reason = "Too many non dashed path effects.";
|
| + }
|
| + } else if ((fNumAAConcavePaths - fNumAAHairlineConcavePaths)
|
| + >= kNumAAConcavePathsTol)
|
| + *reason = "Too many anti-aliased concave paths.";
|
| + else
|
| + *reason = "Unknown reason for GPU unsuitability.";
|
| + }
|
| + return ret;
|
| +}
|
| +
|
| +///////////////////////////////////////////////////////////////////////////////
|
| +
|
| #ifdef SK_SUPPORT_LEGACY_DEFAULT_PICTURE_CTOR
|
| // fRecord OK
|
| SkPicture::SkPicture()
|
| : fWidth(0)
|
| - , fHeight(0)
|
| - , fRecordWillPlayBackBitmaps(false) {
|
| + , fHeight(0) {
|
| this->needsNewGenID();
|
| }
|
| #endif
|
| @@ -62,7 +236,7 @@ SkPicture::SkPicture(int width, int height,
|
| bool deepCopyOps)
|
| : fWidth(width)
|
| , fHeight(height)
|
| - , fRecordWillPlayBackBitmaps(false) {
|
| + , fAnalysis() {
|
| this->needsNewGenID();
|
|
|
| SkPictInfo info;
|
| @@ -137,7 +311,6 @@ SkPicture* SkPicture::clone() const {
|
| }
|
|
|
| SkPicture* clone = SkNEW_ARGS(SkPicture, (newData.detach(), fWidth, fHeight));
|
| - clone->fRecordWillPlayBackBitmaps = fRecordWillPlayBackBitmaps;
|
| clone->fUniqueID = this->uniqueID(); // need to call method to ensure != 0
|
|
|
| return clone;
|
| @@ -271,7 +444,7 @@ SkPicture::SkPicture(SkPictureData* data, int width, int height)
|
| : fData(data)
|
| , fWidth(width)
|
| , fHeight(height)
|
| - , fRecordWillPlayBackBitmaps(false) {
|
| + , fAnalysis() {
|
| this->needsNewGenID();
|
| }
|
|
|
| @@ -386,8 +559,11 @@ void SkPicture::flatten(SkWriteBuffer& buffer) const {
|
| }
|
|
|
| #if SK_SUPPORT_GPU
|
| -// fRecord TODO
|
| +// fRecord OK
|
| bool SkPicture::suitableForGpuRasterization(GrContext* context, const char **reason) const {
|
| + if (fRecord.get()) {
|
| + return fAnalysis.suitableForGpuRasterization(reason, 0);
|
| + }
|
| if (NULL == fData.get()) {
|
| if (NULL != reason) {
|
| *reason = "Missing internal data.";
|
| @@ -407,7 +583,7 @@ bool SkPicture::hasText() const {
|
| // fRecord OK
|
| bool SkPicture::willPlayBackBitmaps() const {
|
| if (fRecord.get()) {
|
| - return fRecordWillPlayBackBitmaps;
|
| + return fAnalysis.fWillPlaybackBitmaps;
|
| }
|
| if (!fData.get()) {
|
| return false;
|
| @@ -441,8 +617,10 @@ SkPicture::SkPicture(int width, int height, SkRecord* record, SkBBoxHierarchy* b
|
| , fHeight(height)
|
| , fRecord(record)
|
| , fBBH(SkSafeRef(bbh))
|
| - , fRecordWillPlayBackBitmaps(SkRecordWillPlaybackBitmaps(*record)) {
|
| + , fAnalysis() {
|
| // TODO: delay as much of this work until just before first playback?
|
| +
|
| + const_cast<Analysis*>(&fAnalysis)->init(*record);
|
| if (fBBH.get()) {
|
| SkRecordFillBounds(*record, fBBH.get());
|
| }
|
|
|