diff -r 000000000000 -r 6474c204b198 gfx/skia/trunk/src/core/SkBitmapProcShader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/skia/trunk/src/core/SkBitmapProcShader.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,453 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SkColorPriv.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" +#include "SkPixelRef.h" +#include "SkErrorInternals.h" +#include "SkBitmapProcShader.h" + +#if SK_SUPPORT_GPU +#include "effects/GrSimpleTextureEffect.h" +#include "effects/GrBicubicEffect.h" +#endif + +bool SkBitmapProcShader::CanDo(const SkBitmap& bm, TileMode tx, TileMode ty) { + switch (bm.colorType()) { + case kAlpha_8_SkColorType: + case kRGB_565_SkColorType: + case kIndex_8_SkColorType: + case kPMColor_SkColorType: + // if (tx == ty && (kClamp_TileMode == tx || kRepeat_TileMode == tx)) + return true; + default: + break; + } + return false; +} + +SkBitmapProcShader::SkBitmapProcShader(const SkBitmap& src, + TileMode tmx, TileMode tmy) { + fRawBitmap = src; + fState.fTileModeX = (uint8_t)tmx; + fState.fTileModeY = (uint8_t)tmy; + fFlags = 0; // computed in setContext +} + +SkBitmapProcShader::SkBitmapProcShader(SkReadBuffer& buffer) + : INHERITED(buffer) { + buffer.readBitmap(&fRawBitmap); + fRawBitmap.setImmutable(); + fState.fTileModeX = buffer.readUInt(); + fState.fTileModeY = buffer.readUInt(); + fFlags = 0; // computed in setContext +} + +SkShader::BitmapType SkBitmapProcShader::asABitmap(SkBitmap* texture, + SkMatrix* texM, + TileMode xy[]) const { + if (texture) { + *texture = fRawBitmap; + } + if (texM) { + texM->reset(); + } + if (xy) { + xy[0] = (TileMode)fState.fTileModeX; + xy[1] = (TileMode)fState.fTileModeY; + } + return kDefault_BitmapType; +} + +void SkBitmapProcShader::flatten(SkWriteBuffer& buffer) const { + this->INHERITED::flatten(buffer); + + buffer.writeBitmap(fRawBitmap); + buffer.writeUInt(fState.fTileModeX); + buffer.writeUInt(fState.fTileModeY); +} + +static bool only_scale_and_translate(const SkMatrix& matrix) { + unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; + return (matrix.getType() & ~mask) == 0; +} + +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 (kIndex_8_SkColorType == bm.colorType()) { + // 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; + if (!fState.chooseProcs(this->getTotalInverse(), paint)) { + this->INHERITED::endContext(); + return false; + } + + const SkBitmap& bitmap = *fState.fBitmap; + bool bitmapIsOpaque = bitmap.isOpaque(); + + // update fFlags + uint32_t flags = 0; + if (bitmapIsOpaque && (255 == this->getPaintAlpha())) { + flags |= kOpaqueAlpha_Flag; + } + + switch (bitmap.colorType()) { + case kRGB_565_SkColorType: + flags |= (kHasSpan16_Flag | kIntrinsicly16_Flag); + break; + case kIndex_8_SkColorType: + case kPMColor_SkColorType: + if (bitmapIsOpaque) { + flags |= kHasSpan16_Flag; + } + break; + case kAlpha_8_SkColorType: + break; // never set kHasSpan16_Flag + default: + break; + } + + if (paint.isDither() && bitmap.colorType() != kRGB_565_SkColorType) { + // gradients can auto-dither in their 16bit sampler, but we don't so + // we clear the flag here. + flags &= ~kHasSpan16_Flag; + } + + // if we're only 1-pixel high, and we don't rotate, then we can claim this + if (1 == bitmap.height() && + only_scale_and_translate(this->getTotalInverse())) { + flags |= kConstInY32_Flag; + if (flags & kHasSpan16_Flag) { + flags |= kConstInY16_Flag; + } + } + + fFlags = flags; + return true; +} + +void SkBitmapProcShader::endContext() { + fState.endContext(); + this->INHERITED::endContext(); +} + +#define BUF_MAX 128 + +#define TEST_BUFFER_OVERRITEx + +#ifdef TEST_BUFFER_OVERRITE + #define TEST_BUFFER_EXTRA 32 + #define TEST_PATTERN 0x88888888 +#else + #define TEST_BUFFER_EXTRA 0 +#endif + +void SkBitmapProcShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) { + const SkBitmapProcState& state = fState; + if (state.getShaderProc32()) { + state.getShaderProc32()(state, x, y, dstC, count); + return; + } + + uint32_t buffer[BUF_MAX + TEST_BUFFER_EXTRA]; + SkBitmapProcState::MatrixProc mproc = state.getMatrixProc(); + SkBitmapProcState::SampleProc32 sproc = state.getSampleProc32(); + int max = fState.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX); + + SkASSERT(state.fBitmap->getPixels()); + SkASSERT(state.fBitmap->pixelRef() == NULL || + state.fBitmap->pixelRef()->isLocked()); + + for (;;) { + int n = count; + if (n > max) { + n = max; + } + SkASSERT(n > 0 && n < BUF_MAX*2); +#ifdef TEST_BUFFER_OVERRITE + for (int i = 0; i < TEST_BUFFER_EXTRA; i++) { + buffer[BUF_MAX + i] = TEST_PATTERN; + } +#endif + mproc(state, buffer, n, x, y); +#ifdef TEST_BUFFER_OVERRITE + for (int j = 0; j < TEST_BUFFER_EXTRA; j++) { + SkASSERT(buffer[BUF_MAX + j] == TEST_PATTERN); + } +#endif + sproc(state, buffer, n, dstC); + + if ((count -= n) == 0) { + break; + } + SkASSERT(count > 0); + x += n; + dstC += n; + } +} + +SkShader::ShadeProc SkBitmapProcShader::asAShadeProc(void** ctx) { + if (fState.getShaderProc32()) { + *ctx = &fState; + return (ShadeProc)fState.getShaderProc32(); + } + return NULL; +} + +void SkBitmapProcShader::shadeSpan16(int x, int y, uint16_t dstC[], int count) { + const SkBitmapProcState& state = fState; + if (state.getShaderProc16()) { + state.getShaderProc16()(state, x, y, dstC, count); + return; + } + + uint32_t buffer[BUF_MAX]; + SkBitmapProcState::MatrixProc mproc = state.getMatrixProc(); + SkBitmapProcState::SampleProc16 sproc = state.getSampleProc16(); + int max = fState.maxCountForBufferSize(sizeof(buffer)); + + SkASSERT(state.fBitmap->getPixels()); + SkASSERT(state.fBitmap->pixelRef() == NULL || + state.fBitmap->pixelRef()->isLocked()); + + for (;;) { + int n = count; + if (n > max) { + n = max; + } + mproc(state, buffer, n, x, y); + sproc(state, buffer, n, dstC); + + if ((count -= n) == 0) { + break; + } + x += n; + dstC += n; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkUnPreMultiply.h" +#include "SkColorShader.h" +#include "SkEmptyShader.h" + +// returns true and set color if the bitmap can be drawn as a single color +// (for efficiency) +static bool canUseColorShader(const SkBitmap& bm, SkColor* color) { + if (1 != bm.width() || 1 != bm.height()) { + return false; + } + + SkAutoLockPixels alp(bm); + if (!bm.readyToDraw()) { + return false; + } + + switch (bm.colorType()) { + case kPMColor_SkColorType: + *color = SkUnPreMultiply::PMColorToColor(*bm.getAddr32(0, 0)); + return true; + case kRGB_565_SkColorType: + *color = SkPixel16ToColor(*bm.getAddr16(0, 0)); + return true; + case kIndex_8_SkColorType: + *color = SkUnPreMultiply::PMColorToColor(bm.getIndex8Color(0, 0)); + return true; + default: // just skip the other configs for now + break; + } + return false; +} + +static bool bitmapIsTooBig(const SkBitmap& bm) { + // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it + // communicates between its matrix-proc and its sampler-proc. Until we can + // widen that, we have to reject bitmaps that are larger. + // + const int maxSize = 65535; + + return bm.width() > maxSize || bm.height() > maxSize; +} + +SkShader* CreateBitmapShader(const SkBitmap& src, SkShader::TileMode tmx, + SkShader::TileMode tmy, SkTBlitterAllocator* allocator) { + SkShader* shader; + SkColor color; + if (src.isNull() || bitmapIsTooBig(src)) { + if (NULL == allocator) { + shader = SkNEW(SkEmptyShader); + } else { + shader = allocator->createT(); + } + } + else if (canUseColorShader(src, &color)) { + if (NULL == allocator) { + shader = SkNEW_ARGS(SkColorShader, (color)); + } else { + shader = allocator->createT(color); + } + } else { + if (NULL == allocator) { + shader = SkNEW_ARGS(SkBitmapProcShader, (src, tmx, tmy)); + } else { + shader = allocator->createT(src, tmx, tmy); + } + } + return shader; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SK_IGNORE_TO_STRING +void SkBitmapProcShader::toString(SkString* str) const { + static const char* gTileModeName[SkShader::kTileModeCount] = { + "clamp", "repeat", "mirror" + }; + + str->append("BitmapShader: ("); + + str->appendf("(%s, %s)", + gTileModeName[fState.fTileModeX], + gTileModeName[fState.fTileModeY]); + + str->append(" "); + fRawBitmap.toString(str); + + this->INHERITED::toString(str); + + str->append(")"); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#if SK_SUPPORT_GPU + +#include "GrTextureAccess.h" +#include "effects/GrSimpleTextureEffect.h" +#include "SkGr.h" + +// Note that this will return -1 if either matrix is perspective. +static SkScalar get_combined_min_stretch(const SkMatrix& viewMatrix, const SkMatrix& localMatrix) { + if (localMatrix.isIdentity()) { + return viewMatrix.getMinStretch(); + } else { + SkMatrix combined; + combined.setConcat(viewMatrix, localMatrix); + return combined.getMinStretch(); + } +} + +GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint& paint) const { + SkMatrix matrix; + matrix.setIDiv(fRawBitmap.width(), fRawBitmap.height()); + + SkMatrix lmInverse; + if (!this->getLocalMatrix().invert(&lmInverse)) { + return NULL; + } + matrix.preConcat(lmInverse); + + SkShader::TileMode tm[] = { + (TileMode)fState.fTileModeX, + (TileMode)fState.fTileModeY, + }; + + // Must set wrap and filter on the sampler before requesting a texture. In two places below + // we check the matrix scale factors to determine how to interpret the filter quality setting. + // This completely ignores the complexity of the drawVertices case where explicit local coords + // are provided by the caller. + SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel(); + GrTextureParams::FilterMode textureFilterMode; + switch(paintFilterLevel) { + case SkPaint::kNone_FilterLevel: + textureFilterMode = GrTextureParams::kNone_FilterMode; + break; + case SkPaint::kLow_FilterLevel: + textureFilterMode = GrTextureParams::kBilerp_FilterMode; + break; + case SkPaint::kMedium_FilterLevel: + if (get_combined_min_stretch(context->getMatrix(), this->getLocalMatrix()) < + SK_Scalar1) { + textureFilterMode = GrTextureParams::kMipMap_FilterMode; + } else { + // Don't trigger MIP level generation unnecessarily. + textureFilterMode = GrTextureParams::kBilerp_FilterMode; + } + break; + case SkPaint::kHigh_FilterLevel: + // Minification can look bad with bicubic filtering. + if (get_combined_min_stretch(context->getMatrix(), this->getLocalMatrix()) >= + SK_Scalar1) { + // fall back to no filtering here; we will install another shader that will do the + // HQ filtering. + textureFilterMode = GrTextureParams::kNone_FilterMode; + } else { + // Fall back to MIP-mapping. + paintFilterLevel = SkPaint::kMedium_FilterLevel; + textureFilterMode = GrTextureParams::kMipMap_FilterMode; + } + break; + default: + SkErrorInternals::SetError( kInvalidPaint_SkError, + "Sorry, I don't understand the filtering " + "mode you asked for. Falling back to " + "MIPMaps."); + textureFilterMode = GrTextureParams::kMipMap_FilterMode; + break; + + } + GrTextureParams params(tm, textureFilterMode); + GrTexture* texture = GrLockAndRefCachedBitmapTexture(context, fRawBitmap, ¶ms); + + if (NULL == texture) { + SkErrorInternals::SetError( kInternalError_SkError, + "Couldn't convert bitmap to texture."); + return NULL; + } + + GrEffectRef* effect = NULL; + if (paintFilterLevel == SkPaint::kHigh_FilterLevel) { + effect = GrBicubicEffect::Create(texture, matrix, tm); + } else { + effect = GrSimpleTextureEffect::Create(texture, matrix, params); + } + GrUnlockAndUnrefCachedBitmapTexture(texture); + return effect; +} +#endif