Linear-time implementation of willPlaybackBitmaps(), computed & cached
on construction in SkPicture. Unit test. Template trickery thanks to mtklein@. BUG=skia:2702 R=mtklein@google.com, reed@android.com, reed@google.com, tomhudson@google.com, mtklein, reed Author: tomhudson@chromium.org Review URL: https://codereview.chromium.org/366443002
This commit is contained in:
parent
2830632ce9
commit
d930511ee7
@ -151,6 +151,8 @@
|
|||||||
'<(skia_src_path)/core/SkRasterClip.cpp',
|
'<(skia_src_path)/core/SkRasterClip.cpp',
|
||||||
'<(skia_src_path)/core/SkRasterizer.cpp',
|
'<(skia_src_path)/core/SkRasterizer.cpp',
|
||||||
'<(skia_src_path)/core/SkReadBuffer.cpp',
|
'<(skia_src_path)/core/SkReadBuffer.cpp',
|
||||||
|
'<(skia_src_path)/core/SkRecordAnalysis.cpp',
|
||||||
|
'<(skia_src_path)/core/SkRecordAnalysis.h',
|
||||||
'<(skia_src_path)/core/SkRecordDraw.cpp',
|
'<(skia_src_path)/core/SkRecordDraw.cpp',
|
||||||
'<(skia_src_path)/core/SkRecordOpts.cpp',
|
'<(skia_src_path)/core/SkRecordOpts.cpp',
|
||||||
'<(skia_src_path)/core/SkRecorder.cpp',
|
'<(skia_src_path)/core/SkRecorder.cpp',
|
||||||
|
@ -313,6 +313,7 @@ private:
|
|||||||
|
|
||||||
SkPicture(int width, int height, SkRecord*); // Takes ownership.
|
SkPicture(int width, int height, SkRecord*); // Takes ownership.
|
||||||
SkAutoTDelete<SkRecord> fRecord;
|
SkAutoTDelete<SkRecord> fRecord;
|
||||||
|
bool fRecordWillPlayBackBitmaps; // TODO: const
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "SkDrawPictureCallback.h"
|
#include "SkDrawPictureCallback.h"
|
||||||
#include "SkPaintPriv.h"
|
#include "SkPaintPriv.h"
|
||||||
#include "SkPicture.h"
|
#include "SkPicture.h"
|
||||||
|
#include "SkRecordAnalysis.h"
|
||||||
#include "SkRegion.h"
|
#include "SkRegion.h"
|
||||||
#include "SkStream.h"
|
#include "SkStream.h"
|
||||||
#include "SkTDArray.h"
|
#include "SkTDArray.h"
|
||||||
@ -132,7 +133,8 @@ static void validateMatrix(const SkMatrix* matrix) {
|
|||||||
// fRecord OK
|
// fRecord OK
|
||||||
SkPicture::SkPicture()
|
SkPicture::SkPicture()
|
||||||
: fWidth(0)
|
: fWidth(0)
|
||||||
, fHeight(0) {
|
, fHeight(0)
|
||||||
|
, fRecordWillPlayBackBitmaps(false) {
|
||||||
this->needsNewGenID();
|
this->needsNewGenID();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +143,8 @@ SkPicture::SkPicture(int width, int height,
|
|||||||
const SkPictureRecord& record,
|
const SkPictureRecord& record,
|
||||||
bool deepCopyOps)
|
bool deepCopyOps)
|
||||||
: fWidth(width)
|
: fWidth(width)
|
||||||
, fHeight(height) {
|
, fHeight(height)
|
||||||
|
, fRecordWillPlayBackBitmaps(false) {
|
||||||
this->needsNewGenID();
|
this->needsNewGenID();
|
||||||
|
|
||||||
SkPictInfo info;
|
SkPictInfo info;
|
||||||
@ -170,6 +173,7 @@ SkPicture::SkPicture(const SkPicture& src) : INHERITED() {
|
|||||||
this->needsNewGenID();
|
this->needsNewGenID();
|
||||||
fWidth = src.fWidth;
|
fWidth = src.fWidth;
|
||||||
fHeight = src.fHeight;
|
fHeight = src.fHeight;
|
||||||
|
fRecordWillPlayBackBitmaps = src.fRecordWillPlayBackBitmaps;
|
||||||
|
|
||||||
if (NULL != src.fData.get()) {
|
if (NULL != src.fData.get()) {
|
||||||
fData.reset(SkNEW_ARGS(SkPictureData, (*src.fData)));
|
fData.reset(SkNEW_ARGS(SkPictureData, (*src.fData)));
|
||||||
@ -204,6 +208,7 @@ void SkPicture::clone(SkPicture* pictures, int count) const {
|
|||||||
clone->fWidth = fWidth;
|
clone->fWidth = fWidth;
|
||||||
clone->fHeight = fHeight;
|
clone->fHeight = fHeight;
|
||||||
clone->fData.reset(NULL);
|
clone->fData.reset(NULL);
|
||||||
|
clone->fRecordWillPlayBackBitmaps = fRecordWillPlayBackBitmaps;
|
||||||
|
|
||||||
/* We want to copy the src's playback. However, if that hasn't been built
|
/* We want to copy the src's playback. However, if that hasn't been built
|
||||||
yet, we need to fake a call to endRecording() without actually calling
|
yet, we need to fake a call to endRecording() without actually calling
|
||||||
@ -381,7 +386,8 @@ bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer& buffer, SkPictInfo* pInfo
|
|||||||
SkPicture::SkPicture(SkPictureData* data, int width, int height)
|
SkPicture::SkPicture(SkPictureData* data, int width, int height)
|
||||||
: fData(data)
|
: fData(data)
|
||||||
, fWidth(width)
|
, fWidth(width)
|
||||||
, fHeight(height) {
|
, fHeight(height)
|
||||||
|
, fRecordWillPlayBackBitmaps(false) {
|
||||||
this->needsNewGenID();
|
this->needsNewGenID();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -521,8 +527,11 @@ bool SkPicture::suitableForGpuRasterization(GrContext* context, const char **rea
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// fRecord TODO
|
// fRecord OK
|
||||||
bool SkPicture::willPlayBackBitmaps() const {
|
bool SkPicture::willPlayBackBitmaps() const {
|
||||||
|
if (fRecord.get()) {
|
||||||
|
return fRecordWillPlayBackBitmaps;
|
||||||
|
}
|
||||||
if (!fData.get()) {
|
if (!fData.get()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -563,6 +572,7 @@ uint32_t SkPicture::uniqueID() const {
|
|||||||
SkPicture::SkPicture(int width, int height, SkRecord* record)
|
SkPicture::SkPicture(int width, int height, SkRecord* record)
|
||||||
: fWidth(width)
|
: fWidth(width)
|
||||||
, fHeight(height)
|
, fHeight(height)
|
||||||
, fRecord(record) {
|
, fRecord(record)
|
||||||
|
, fRecordWillPlayBackBitmaps(SkRecordWillPlaybackBitmaps(*record)) {
|
||||||
this->needsNewGenID();
|
this->needsNewGenID();
|
||||||
}
|
}
|
||||||
|
66
src/core/SkRecordAnalysis.cpp
Normal file
66
src/core/SkRecordAnalysis.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#include "SkRecordAnalysis.h"
|
||||||
|
|
||||||
|
#include "SkShader.h"
|
||||||
|
#include "SkTLogic.h"
|
||||||
|
|
||||||
|
/** 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);
|
||||||
|
|
||||||
|
// 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; }
|
||||||
|
|
||||||
|
|
||||||
|
// 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 SkRecordWillPlaybackBitmaps(const SkRecord& record) {
|
||||||
|
BitmapTester tester;
|
||||||
|
for (unsigned i = 0; i < record.count(); i++) {
|
||||||
|
if (record.visit<bool>(i, tester)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
8
src/core/SkRecordAnalysis.h
Normal file
8
src/core/SkRecordAnalysis.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef SkRecordAnalysis_DEFINED
|
||||||
|
#define SkRecordAnalysis_DEFINED
|
||||||
|
|
||||||
|
#include "SkRecord.h"
|
||||||
|
|
||||||
|
bool SkRecordWillPlaybackBitmaps(const SkRecord& record);
|
||||||
|
|
||||||
|
#endif // SkRecordAnalysis_DEFINED
|
@ -7,7 +7,11 @@
|
|||||||
|
|
||||||
#include "Test.h"
|
#include "Test.h"
|
||||||
|
|
||||||
|
#include "SkBitmap.h"
|
||||||
|
#include "SkImageInfo.h"
|
||||||
|
#include "SkShader.h"
|
||||||
#include "SkRecord.h"
|
#include "SkRecord.h"
|
||||||
|
#include "SkRecordAnalysis.h"
|
||||||
#include "SkRecords.h"
|
#include "SkRecords.h"
|
||||||
|
|
||||||
// Sums the area of any DrawRect command it sees.
|
// Sums the area of any DrawRect command it sees.
|
||||||
@ -48,6 +52,8 @@ struct Stretch {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define APPEND(record, type, ...) SkNEW_PLACEMENT_ARGS(record.append<type>(), type, (__VA_ARGS__))
|
||||||
|
|
||||||
// Basic tests for the low-level SkRecord code.
|
// Basic tests for the low-level SkRecord code.
|
||||||
DEF_TEST(Record, r) {
|
DEF_TEST(Record, r) {
|
||||||
SkRecord record;
|
SkRecord record;
|
||||||
@ -55,7 +61,7 @@ DEF_TEST(Record, r) {
|
|||||||
// Add a simple DrawRect command.
|
// Add a simple DrawRect command.
|
||||||
SkRect rect = SkRect::MakeWH(10, 10);
|
SkRect rect = SkRect::MakeWH(10, 10);
|
||||||
SkPaint paint;
|
SkPaint paint;
|
||||||
SkNEW_PLACEMENT_ARGS(record.append<SkRecords::DrawRect>(), SkRecords::DrawRect, (paint, rect));
|
APPEND(record, SkRecords::DrawRect, paint, rect);
|
||||||
|
|
||||||
// Its area should be 100.
|
// Its area should be 100.
|
||||||
AreaSummer summer;
|
AreaSummer summer;
|
||||||
@ -70,3 +76,38 @@ DEF_TEST(Record, r) {
|
|||||||
summer.apply(record);
|
summer.apply(record);
|
||||||
REPORTER_ASSERT(r, summer.area() == 500);
|
REPORTER_ASSERT(r, summer.area() == 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEF_TEST(RecordAnalysis, r) {
|
||||||
|
SkRecord record;
|
||||||
|
|
||||||
|
SkRect rect = SkRect::MakeWH(10, 10);
|
||||||
|
SkPaint paint;
|
||||||
|
APPEND(record, SkRecords::DrawRect, paint, rect);
|
||||||
|
REPORTER_ASSERT(r, !SkRecordWillPlaybackBitmaps(record));
|
||||||
|
|
||||||
|
SkBitmap bitmap;
|
||||||
|
APPEND(record, SkRecords::DrawBitmap, &paint, bitmap, 0.0f, 0.0f);
|
||||||
|
REPORTER_ASSERT(r, SkRecordWillPlaybackBitmaps(record));
|
||||||
|
|
||||||
|
SkNEW_PLACEMENT_ARGS(record.replace<SkRecords::DrawRect>(1),
|
||||||
|
SkRecords::DrawRect, (paint, rect));
|
||||||
|
REPORTER_ASSERT(r, !SkRecordWillPlaybackBitmaps(record));
|
||||||
|
|
||||||
|
SkPaint paint2;
|
||||||
|
// CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
|
||||||
|
// gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
|
||||||
|
SkBitmap bitmap2;
|
||||||
|
bitmap2.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
|
||||||
|
bitmap2.eraseColor(SK_ColorBLUE);
|
||||||
|
*(bitmap2.getAddr32(0, 0)) = SK_ColorGREEN;
|
||||||
|
SkShader* shader = SkShader::CreateBitmapShader(bitmap2, SkShader::kClamp_TileMode,
|
||||||
|
SkShader::kClamp_TileMode);
|
||||||
|
paint2.setShader(shader);
|
||||||
|
REPORTER_ASSERT(r, shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType);
|
||||||
|
|
||||||
|
APPEND(record, SkRecords::DrawRect, paint2, rect);
|
||||||
|
REPORTER_ASSERT(r, SkRecordWillPlaybackBitmaps(record));
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef APPEND
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user