Use an image generator to back SkPictureShader tiles.
To avoid lifetime issues for tiles backed by discardable memory, use an image generator to re-generate them on the fly. With this CL, we are now caching bitmap shaders wrapping discardable pixel ref bitmaps backed by picture image generators. (the CL also includes some minor/unrelated SkPictureShader cleanup) BUG=skia:3220 R=reed@google.com,halcanary@google.com Review URL: https://codereview.chromium.org/866773002
This commit is contained in:
parent
5b27b142f2
commit
4739955e98
@ -10,6 +10,7 @@
|
||||
#include "SkBitmap.h"
|
||||
#include "SkBitmapProcShader.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkImageGenerator.h"
|
||||
#include "SkMatrixUtils.h"
|
||||
#include "SkPicture.h"
|
||||
#include "SkReadBuffer.h"
|
||||
@ -22,6 +23,50 @@
|
||||
namespace {
|
||||
static unsigned gBitmapSkaderKeyNamespaceLabel;
|
||||
|
||||
class PictureImageGenerator : public SkImageGenerator {
|
||||
public:
|
||||
PictureImageGenerator(const SkPicture* picture,
|
||||
const SkRect& pictureTile,
|
||||
const SkISize& tileSize)
|
||||
: fPicture(SkRef(picture))
|
||||
, fPictureTile(pictureTile)
|
||||
, fRasterTileInfo(SkImageInfo::MakeN32Premul(tileSize)) {}
|
||||
|
||||
protected:
|
||||
virtual bool onGetInfo(SkImageInfo *info) SK_OVERRIDE {
|
||||
*info = fRasterTileInfo;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual Result onGetPixels(const SkImageInfo& info, void *pixels, size_t rowBytes,
|
||||
SkPMColor ctable[], int *ctableCount) SK_OVERRIDE {
|
||||
if (info != fRasterTileInfo || SkToBool(ctable) || SkToBool(ctableCount)) {
|
||||
return kInvalidConversion;
|
||||
}
|
||||
|
||||
SkSize tileScale = SkSize::Make(SkIntToScalar(info.width()) / fPictureTile.width(),
|
||||
SkIntToScalar(info.height()) / fPictureTile.height());
|
||||
SkBitmap tileBitmap;
|
||||
if (!tileBitmap.installPixels(info, pixels, rowBytes)) {
|
||||
return kInvalidParameters;
|
||||
}
|
||||
tileBitmap.eraseColor(SK_ColorTRANSPARENT);
|
||||
|
||||
// Always disable LCD text, since we can't assume our image will be opaque.
|
||||
SkCanvas tileCanvas(tileBitmap, SkSurfaceProps(0, kUnknown_SkPixelGeometry));
|
||||
tileCanvas.scale(tileScale.width(), tileScale.height());
|
||||
tileCanvas.translate(-fPictureTile.x(), -fPictureTile.y());
|
||||
tileCanvas.drawPicture(fPicture);
|
||||
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
private:
|
||||
SkAutoTUnref<const SkPicture> fPicture;
|
||||
const SkRect fPictureTile;
|
||||
const SkImageInfo fRasterTileInfo;
|
||||
};
|
||||
|
||||
struct BitmapShaderKey : public SkResourceCache::Key {
|
||||
public:
|
||||
BitmapShaderKey(uint32_t pictureID,
|
||||
@ -81,24 +126,12 @@ struct BitmapShaderRec : public SkResourceCache::Rec {
|
||||
|
||||
result->reset(SkRef(rec.fShader.get()));
|
||||
|
||||
SkBitmap tile;
|
||||
rec.fShader.get()->asABitmap(&tile, NULL, NULL);
|
||||
// FIXME: this doesn't protect the pixels from being discarded as soon as we unlock.
|
||||
// Should be handled via a pixel ref generator instead
|
||||
// (https://code.google.com/p/skia/issues/detail?id=3220).
|
||||
SkAutoLockPixels alp(tile, true);
|
||||
return tile.getPixels() != NULL;
|
||||
// The bitmap shader is backed by an image generator, thus it can always re-generate its
|
||||
// pixels if discarded.
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static bool cache_try_alloc_pixels(SkBitmap* bitmap) {
|
||||
SkBitmap::Allocator* allocator = SkResourceCache::GetAllocator();
|
||||
|
||||
return NULL != allocator
|
||||
? allocator->allocPixelRef(bitmap, NULL)
|
||||
: bitmap->tryAllocPixels();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SkPictureShader::SkPictureShader(const SkPicture* picture, TileMode tmx, TileMode tmy,
|
||||
@ -110,10 +143,6 @@ SkPictureShader::SkPictureShader(const SkPicture* picture, TileMode tmx, TileMod
|
||||
, fTmy(tmy) {
|
||||
}
|
||||
|
||||
SkPictureShader::~SkPictureShader() {
|
||||
fPicture->unref();
|
||||
}
|
||||
|
||||
SkPictureShader* SkPictureShader::Create(const SkPicture* picture, TileMode tmx, TileMode tmy,
|
||||
const SkMatrix* localMatrix, const SkRect* tile) {
|
||||
if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
|
||||
@ -188,18 +217,10 @@ SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatri
|
||||
|
||||
if (!SkResourceCache::Find(key, BitmapShaderRec::Visitor, &tileShader)) {
|
||||
SkBitmap bm;
|
||||
bm.setInfo(SkImageInfo::MakeN32Premul(tileSize));
|
||||
if (!cache_try_alloc_pixels(&bm)) {
|
||||
if (!SkInstallDiscardablePixelRef(SkNEW_ARGS(PictureImageGenerator,
|
||||
(fPicture, fTile, tileSize)), &bm)) {
|
||||
return NULL;
|
||||
}
|
||||
bm.eraseColor(SK_ColorTRANSPARENT);
|
||||
|
||||
// Always disable LCD text, since we can't assume our image will be opaque.
|
||||
SkCanvas canvas(bm, SkSurfaceProps(0, kUnknown_SkPixelGeometry));
|
||||
|
||||
canvas.scale(tileScale.width(), tileScale.height());
|
||||
canvas.translate(-fTile.x(), -fTile.y());
|
||||
canvas.drawPicture(fPicture);
|
||||
|
||||
SkMatrix shaderMatrix = this->getLocalMatrix();
|
||||
shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height());
|
||||
@ -280,10 +301,10 @@ void SkPictureShader::toString(SkString* str) const {
|
||||
};
|
||||
|
||||
str->appendf("PictureShader: [%f:%f:%f:%f] ",
|
||||
fPicture ? fPicture->cullRect().fLeft : 0,
|
||||
fPicture ? fPicture->cullRect().fTop : 0,
|
||||
fPicture ? fPicture->cullRect().fRight : 0,
|
||||
fPicture ? fPicture->cullRect().fBottom : 0);
|
||||
fPicture->cullRect().fLeft,
|
||||
fPicture->cullRect().fTop,
|
||||
fPicture->cullRect().fRight,
|
||||
fPicture->cullRect().fBottom);
|
||||
|
||||
str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]);
|
||||
|
||||
|
@ -23,7 +23,6 @@ class SkPictureShader : public SkShader {
|
||||
public:
|
||||
static SkPictureShader* Create(const SkPicture*, TileMode, TileMode, const SkMatrix*,
|
||||
const SkRect*);
|
||||
virtual ~SkPictureShader();
|
||||
|
||||
size_t contextSize() const SK_OVERRIDE;
|
||||
|
||||
@ -43,9 +42,9 @@ private:
|
||||
|
||||
SkShader* refBitmapShader(const SkMatrix&, const SkMatrix* localMatrix) const;
|
||||
|
||||
const SkPicture* fPicture;
|
||||
SkRect fTile;
|
||||
TileMode fTmx, fTmy;
|
||||
SkAutoTUnref<const SkPicture> fPicture;
|
||||
SkRect fTile;
|
||||
TileMode fTmx, fTmy;
|
||||
|
||||
class PictureShaderContext : public SkShader::Context {
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user