start to remove lockPixels from bitmapshader

BUG=
R=scroggo@google.com

Review URL: https://codereview.chromium.org/23591030

git-svn-id: http://skia.googlecode.com/svn/trunk@11258 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@google.com 2013-09-13 16:04:49 +00:00
parent f4dc60457b
commit cee9dcb837
7 changed files with 195 additions and 41 deletions

View File

@ -141,6 +141,37 @@ public:
return this->onRefEncodedData();
}
/**
* Experimental -- tells the caller if it is worth it to call decodeInto().
* Just an optimization at this point, to avoid checking the cache first.
* We may remove/change this call in the future.
*/
bool implementsDecodeInto() {
return this->onImplementsDecodeInto();
}
/**
* Return a decoded instance of this pixelRef in bitmap. If this cannot be
* done, return false and the bitmap parameter is ignored/unchanged.
*
* pow2 is the requeste power-of-two downscale that the caller needs. This
* can be ignored, and the "original" size can be returned, but if the
* underlying codec can efficiently return a smaller size, that should be
* done. Some examples:
*
* To request the "base" version (original scale), pass 0 for pow2
* To request 1/2 scale version (1/2 width, 1/2 height), pass 1 for pow2
* To request 1/4 scale version (1/4 width, 1/4 height), pass 2 for pow2
* ...
*
* If this returns true, then bitmap must be "locked" such that
* bitmap->getPixels() will return the correct address.
*/
bool decodeInto(int pow2, SkBitmap* bitmap) {
SkASSERT(pow2 >= 0);
return this->onDecodeInto(pow2, bitmap);
}
/** Are we really wrapping a texture instead of a bitmap?
*/
virtual GrTexture* getTexture() { return NULL; }
@ -190,6 +221,11 @@ protected:
/** Default impl returns true */
virtual bool onLockPixelsAreWritable() const;
// returns false;
virtual bool onImplementsDecodeInto();
// returns false;
virtual bool onDecodeInto(int pow2, SkBitmap* bitmap);
/**
* For pixelrefs that don't have access to their raw pixels, they may be
* able to make a copy of them (e.g. if the pixels are on the GPU).

View File

@ -80,24 +80,37 @@ bool SkBitmapProcShader::isOpaque() const {
return fRawBitmap.isOpaque();
}
static bool valid_for_drawing(const SkBitmap& bm) {
if (0 == bm.width() || 0 == bm.height()) {
return false; // nothing to draw
}
if (NULL == bm.pixelRef()) {
return false; // no pixels to read
}
if (SkBitmap::kIndex8_Config == bm.config()) {
// ugh, I have to lock-pixels to inspect the colortable
SkAutoLockPixels alp(bm);
if (!bm.getColorTable()) {
return false;
}
}
return true;
}
bool SkBitmapProcShader::setContext(const SkBitmap& device,
const SkPaint& paint,
const SkMatrix& matrix) {
if (!fRawBitmap.getTexture() && !valid_for_drawing(fRawBitmap)) {
return false;
}
// do this first, so we have a correct inverse matrix
if (!this->INHERITED::setContext(device, paint, matrix)) {
return false;
}
fState.fOrigBitmap = fRawBitmap;
fState.fOrigBitmap.lockPixels();
if (!fState.fOrigBitmap.getTexture() && !fState.fOrigBitmap.readyToDraw()) {
fState.fOrigBitmap.unlockPixels();
this->INHERITED::endContext();
return false;
}
if (!fState.chooseProcs(this->getTotalInverse(), paint)) {
fState.fOrigBitmap.unlockPixels();
this->INHERITED::endContext();
return false;
}
@ -147,7 +160,6 @@ bool SkBitmapProcShader::setContext(const SkBitmap& device,
}
void SkBitmapProcShader::endContext() {
fState.fOrigBitmap.unlockPixels();
fState.endContext();
this->INHERITED::endContext();
}

View File

@ -13,6 +13,7 @@
#include "SkUtilsArm.h"
#include "SkBitmapScaler.h"
#include "SkMipMap.h"
#include "SkPixelRef.h"
#include "SkScaledImageCache.h"
#if !SK_ARM_NEON_IS_NONE
@ -109,15 +110,14 @@ static SkScalar effective_matrix_scale_sqrd(const SkMatrix& mat) {
// the portion of the image that we're going to need. This will complicate
// the interface to the cache, but might be well worth it.
void SkBitmapProcState::possiblyScaleImage() {
bool SkBitmapProcState::possiblyScaleImage() {
SkASSERT(NULL == fBitmap);
SkASSERT(NULL == fScaledCacheID);
if (fFilterLevel <= SkPaint::kLow_FilterLevel) {
// none or low (bilerp) does not need to look any further
return;
return false;
}
// STEP 1: Highest quality direct scale?
// Check to see if the transformation matrix is simple, and if we're
// doing high quality scaling. If so, do the bitmap scale here and
// remove the scaling component from the matrix.
@ -129,7 +129,6 @@ void SkBitmapProcState::possiblyScaleImage() {
SkScalar invScaleX = fInvMatrix.getScaleX();
SkScalar invScaleY = fInvMatrix.getScaleY();
SkASSERT(NULL == fScaledCacheID);
fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap,
invScaleX, invScaleY,
&fScaledBitmap);
@ -151,7 +150,7 @@ void SkBitmapProcState::possiblyScaleImage() {
simd)) {
// we failed to create fScaledBitmap, so just return and let
// the scanline proc handle it.
return;
return true;
}
fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap,
@ -159,25 +158,19 @@ void SkBitmapProcState::possiblyScaleImage() {
invScaleY,
fScaledBitmap);
}
fScaledBitmap.lockPixels();
fScaledBitmap.lockPixels(); // wonder if Resize() should have locked this
fBitmap = &fScaledBitmap;
// set the inv matrix type to translate-only;
fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScaleX(),
fInvMatrix.getTranslateY() / fInvMatrix.getScaleY());
// no need for any further filtering; we just did it!
fFilterLevel = SkPaint::kNone_FilterLevel;
return;
return true;
}
/*
* If we get here, the caller has requested either Med or High filter-level
*
* If High, then our special-case for scale-only did not take, and so we
* have to make a choice:
* 1. fall back on mipmaps + bilerp
@ -202,7 +195,7 @@ void SkBitmapProcState::possiblyScaleImage() {
const SkScalar bicubicLimit = SkFloatToScalar(4.0f);
const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit;
if (scaleSqd < bicubicLimitSqd) { // use bicubic scanline
return;
return false;
}
// else set the filter-level to Medium, since we're scaling down and
@ -247,15 +240,61 @@ void SkBitmapProcState::possiblyScaleImage() {
level.fRowBytes);
fScaledBitmap.setPixels(level.fPixels);
fBitmap = &fScaledBitmap;
fFilterLevel = SkPaint::kLow_FilterLevel;
return true;
}
}
}
return false;
}
static bool get_locked_pixels(const SkBitmap& src, int pow2, SkBitmap* dst) {
SkPixelRef* pr = src.pixelRef();
if (pr && pr->decodeInto(pow2, dst)) {
return true;
}
/*
* At this point, we may or may not have built a mipmap. Regardless, we
* now fall back on Low so will bilerp whatever fBitmap now points at.
* If decodeInto() fails, it is possibe that we have an old subclass that
* does not, or cannot, implement that. In that case we fall back to the
* older protocol of having the pixelRef handle the caching for us.
*/
fFilterLevel = SkPaint::kLow_FilterLevel;
*dst = src;
dst->lockPixels();
return SkToBool(dst->getPixels());
}
bool SkBitmapProcState::lockBaseBitmap() {
SkPixelRef* pr = fOrigBitmap.pixelRef();
if (pr->isLocked() || !pr->implementsDecodeInto()) {
// fast-case, no need to look in our cache
fScaledBitmap = fOrigBitmap;
} else {
fScaledCacheID = SkScaledImageCache::FindAndLock(fOrigBitmap,
SK_Scalar1, SK_Scalar1,
&fScaledBitmap);
if (NULL == fScaledCacheID) {
if (!get_locked_pixels(fOrigBitmap, 0, &fScaledBitmap)) {
return false;
}
// TODO: if fScaled comes back at a different width/height than fOrig,
// we need to update the matrix we are using to sample from this guy.
fScaledCacheID = SkScaledImageCache::AddAndLock(fOrigBitmap,
SK_Scalar1, SK_Scalar1,
fScaledBitmap);
if (!fScaledCacheID) {
fScaledBitmap.reset();
return false;
}
}
}
fScaledBitmap.lockPixels(); // just 'cause the cache made a copy :(
fBitmap = &fScaledBitmap;
return true;
}
void SkBitmapProcState::endContext() {
@ -277,17 +316,10 @@ SkBitmapProcState::~SkBitmapProcState() {
}
bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
if (fOrigBitmap.width() == 0 || fOrigBitmap.height() == 0) {
return false;
}
SkASSERT(fOrigBitmap.width() && fOrigBitmap.height());
fBitmap = &fOrigBitmap;
fBitmap = NULL;
fInvMatrix = inv;
// initialize our filter quality to the one requested by the caller.
// We may downgrade it later if we determine that we either don't need
// or can't provide as high a quality filtering as the user requested.
fFilterLevel = paint.getFilterLevel();
// possiblyScaleImage will look to see if it can rescale the image as a
@ -295,8 +327,13 @@ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
// a nearby mipmap level. If it does, it will adjust the working
// matrix as well as the working bitmap. It may also adjust the filter
// quality to avoid re-filtering an already perfectly scaled image.
this->possiblyScaleImage();
if (!this->possiblyScaleImage()) {
if (!this->lockBaseBitmap()) {
return false;
}
}
SkASSERT(fBitmap);
bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
@ -322,7 +359,6 @@ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
fInvMatrix.setTranslate(tx, ty);
}
}
}

View File

@ -13,6 +13,7 @@
#include "SkBitmap.h"
#include "SkBitmapFilter.h"
#include "SkMatrix.h"
#include "SkPaint.h"
#include "SkScaledImageCache.h"
#define FractionalInt_IS_64BIT
@ -160,7 +161,13 @@ private:
bool chooseProcs(const SkMatrix& inv, const SkPaint&);
ShaderProc32 chooseShaderProc32();
void possiblyScaleImage();
// returns false if we did not try to scale the image. In that case, we
// will need to "lock" its pixels some other way.
bool possiblyScaleImage();
// returns false if we failed to "lock" the pixels at all. Typically this
// means we have to abort the shader.
bool lockBaseBitmap();
SkBitmapFilter* fBitmapFilter;

View File

@ -167,6 +167,14 @@ bool SkPixelRef::onLockPixelsAreWritable() const {
return true;
}
bool SkPixelRef::onImplementsDecodeInto() {
return false;
}
bool SkPixelRef::onDecodeInto(int pow2, SkBitmap* bitmap) {
return false;
}
uint32_t SkPixelRef::getGenerationID() const {
if (0 == fGenerationID) {
fGenerationID = SkNextPixelRefGenerationID();

View File

@ -145,3 +145,56 @@ SkData* SkLazyPixelRef::onRefEncodedData() {
fData->ref();
return fData;
}
#include "SkImagePriv.h"
static bool init_from_info(SkBitmap* bm, const SkImage::Info& info,
size_t rowBytes) {
bool isOpaque;
SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
if (SkBitmap::kNo_Config == config) {
return false;
}
bm->setConfig(config, info.fWidth, info.fHeight, rowBytes);
bm->setIsOpaque(isOpaque);
return bm->allocPixels();
}
bool SkLazyPixelRef::onImplementsDecodeInto() {
return true;
}
bool SkLazyPixelRef::onDecodeInto(int pow2, SkBitmap* bitmap) {
SkASSERT(fData != NULL && fData->size() > 0);
if (fErrorInDecoding) {
return false;
}
SkImage::Info info;
// Determine the size of the image in order to determine how much memory to allocate.
// FIXME: As an optimization, only do this part once.
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, NULL);
if (fErrorInDecoding) {
return false;
}
SkBitmapFactory::Target target;
(void)ComputeMinRowBytesAndSize(info, &target.fRowBytes);
SkBitmap tmp;
if (!init_from_info(&tmp, info, target.fRowBytes)) {
return false;
}
target.fAddr = tmp.getPixels();
fErrorInDecoding = !fDecodeProc(fData->data(), fData->size(), &info, &target);
if (fErrorInDecoding) {
return false;
}
*bitmap = tmp;
return true;
}

View File

@ -61,6 +61,8 @@ protected:
virtual void onUnlockPixels() SK_OVERRIDE;
virtual bool onLockPixelsAreWritable() const SK_OVERRIDE { return false; }
virtual SkData* onRefEncodedData() SK_OVERRIDE;
virtual bool onImplementsDecodeInto() SK_OVERRIDE;
virtual bool onDecodeInto(int pow2, SkBitmap*) SK_OVERRIDE;
private:
bool fErrorInDecoding;