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/SkRasterizer.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/SkRecordOpts.cpp',
|
||||
'<(skia_src_path)/core/SkRecorder.cpp',
|
||||
|
@ -313,6 +313,7 @@ private:
|
||||
|
||||
SkPicture(int width, int height, SkRecord*); // Takes ownership.
|
||||
SkAutoTDelete<SkRecord> fRecord;
|
||||
bool fRecordWillPlayBackBitmaps; // TODO: const
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "SkDrawPictureCallback.h"
|
||||
#include "SkPaintPriv.h"
|
||||
#include "SkPicture.h"
|
||||
#include "SkRecordAnalysis.h"
|
||||
#include "SkRegion.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkTDArray.h"
|
||||
@ -132,7 +133,8 @@ static void validateMatrix(const SkMatrix* matrix) {
|
||||
// fRecord OK
|
||||
SkPicture::SkPicture()
|
||||
: fWidth(0)
|
||||
, fHeight(0) {
|
||||
, fHeight(0)
|
||||
, fRecordWillPlayBackBitmaps(false) {
|
||||
this->needsNewGenID();
|
||||
}
|
||||
|
||||
@ -141,7 +143,8 @@ SkPicture::SkPicture(int width, int height,
|
||||
const SkPictureRecord& record,
|
||||
bool deepCopyOps)
|
||||
: fWidth(width)
|
||||
, fHeight(height) {
|
||||
, fHeight(height)
|
||||
, fRecordWillPlayBackBitmaps(false) {
|
||||
this->needsNewGenID();
|
||||
|
||||
SkPictInfo info;
|
||||
@ -170,6 +173,7 @@ SkPicture::SkPicture(const SkPicture& src) : INHERITED() {
|
||||
this->needsNewGenID();
|
||||
fWidth = src.fWidth;
|
||||
fHeight = src.fHeight;
|
||||
fRecordWillPlayBackBitmaps = src.fRecordWillPlayBackBitmaps;
|
||||
|
||||
if (NULL != src.fData.get()) {
|
||||
fData.reset(SkNEW_ARGS(SkPictureData, (*src.fData)));
|
||||
@ -204,6 +208,7 @@ void SkPicture::clone(SkPicture* pictures, int count) const {
|
||||
clone->fWidth = fWidth;
|
||||
clone->fHeight = fHeight;
|
||||
clone->fData.reset(NULL);
|
||||
clone->fRecordWillPlayBackBitmaps = fRecordWillPlayBackBitmaps;
|
||||
|
||||
/* 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
|
||||
@ -381,7 +386,8 @@ bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer& buffer, SkPictInfo* pInfo
|
||||
SkPicture::SkPicture(SkPictureData* data, int width, int height)
|
||||
: fData(data)
|
||||
, fWidth(width)
|
||||
, fHeight(height) {
|
||||
, fHeight(height)
|
||||
, fRecordWillPlayBackBitmaps(false) {
|
||||
this->needsNewGenID();
|
||||
}
|
||||
|
||||
@ -521,8 +527,11 @@ bool SkPicture::suitableForGpuRasterization(GrContext* context, const char **rea
|
||||
}
|
||||
#endif
|
||||
|
||||
// fRecord TODO
|
||||
// fRecord OK
|
||||
bool SkPicture::willPlayBackBitmaps() const {
|
||||
if (fRecord.get()) {
|
||||
return fRecordWillPlayBackBitmaps;
|
||||
}
|
||||
if (!fData.get()) {
|
||||
return false;
|
||||
}
|
||||
@ -563,6 +572,7 @@ uint32_t SkPicture::uniqueID() const {
|
||||
SkPicture::SkPicture(int width, int height, SkRecord* record)
|
||||
: fWidth(width)
|
||||
, fHeight(height)
|
||||
, fRecord(record) {
|
||||
, fRecord(record)
|
||||
, fRecordWillPlayBackBitmaps(SkRecordWillPlaybackBitmaps(*record)) {
|
||||
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 "SkBitmap.h"
|
||||
#include "SkImageInfo.h"
|
||||
#include "SkShader.h"
|
||||
#include "SkRecord.h"
|
||||
#include "SkRecordAnalysis.h"
|
||||
#include "SkRecords.h"
|
||||
|
||||
// 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.
|
||||
DEF_TEST(Record, r) {
|
||||
SkRecord record;
|
||||
@ -55,7 +61,7 @@ DEF_TEST(Record, r) {
|
||||
// Add a simple DrawRect command.
|
||||
SkRect rect = SkRect::MakeWH(10, 10);
|
||||
SkPaint paint;
|
||||
SkNEW_PLACEMENT_ARGS(record.append<SkRecords::DrawRect>(), SkRecords::DrawRect, (paint, rect));
|
||||
APPEND(record, SkRecords::DrawRect, paint, rect);
|
||||
|
||||
// Its area should be 100.
|
||||
AreaSummer summer;
|
||||
@ -70,3 +76,38 @@ DEF_TEST(Record, r) {
|
||||
summer.apply(record);
|
||||
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