diff --git a/src/core/SkBitmapController.cpp b/src/core/SkBitmapController.cpp index fb7e9a431e..ba16256db5 100644 --- a/src/core/SkBitmapController.cpp +++ b/src/core/SkBitmapController.cpp @@ -11,7 +11,6 @@ #include "src/core/SkArenaAlloc.h" #include "src/core/SkBitmapCache.h" #include "src/core/SkBitmapController.h" -#include "src/core/SkMatrixPriv.h" #include "src/core/SkMipmap.h" #include "src/image/SkImage_Base.h" @@ -27,86 +26,6 @@ static sk_sp try_load_mips(const SkImage_Base* image) { return mips; } -/////////////////////////////////////////////////////////////////////////////////////////////////// - -SkBitmapController::State* SkBitmapController::RequestBitmap(const SkImage_Base* image, - const SkMatrix& inv, - const SkSamplingOptions& sampling, - SkArenaAlloc* alloc) { - auto* state = alloc->make(image, inv, sampling); - - return state->pixmap().addr() ? state : nullptr; -} - -/* - * Modulo internal errors, this should always succeed *if* the matrix is downscaling - * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling) - */ -bool SkBitmapController::State::extractMipLevel(const SkImage_Base* image) { - SkASSERT(!fSampling.useCubic); - if (fSampling.mipmap != SkMipmapMode::kNearest) { - return false; - } - - // We will extract the right level here, so mark fSampling to know that has already happened. - fSampling = SkSamplingOptions(fSampling.filter, SkMipmapMode::kNone); - - SkSize invScaleSize; - if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) { - return false; - } - - if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) { - fCurrMip = try_load_mips(image); - if (!fCurrMip) { - return false; - } - // diagnostic for a crasher... - SkASSERT_RELEASE(fCurrMip->data()); - - const SkSize scale = SkSize::Make(SkScalarInvert(invScaleSize.width()), - SkScalarInvert(invScaleSize.height())); - SkMipmap::Level level; - if (fCurrMip->extractLevel(scale, &level)) { - const SkSize& invScaleFixup = level.fScale; - fInvMatrix.postScale(invScaleFixup.width(), invScaleFixup.height()); - - // todo: if we could wrap the fCurrMip in a pixelref, then we could just install - // that here, and not need to explicitly track it ourselves. - return fResultBitmap.installPixels(level.fPixmap); - } else { - // failed to extract, so release the mipmap - fCurrMip.reset(nullptr); - } - } - return false; -} - -SkBitmapController::State::State(const SkImage_Base* image, - const SkMatrix& inv, - const SkSamplingOptions& sampling) - : fInvMatrix(inv) - , fSampling(sampling) -{ - if (fSampling.useCubic && - SkMatrixPriv::AdjustHighQualityFilterLevel(fInvMatrix, true) != kHigh_SkFilterQuality) - { - fSampling = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNearest); - } - - if (!fSampling.useCubic && this->extractMipLevel(image)) { - SkASSERT(fResultBitmap.getPixels()); - } else { - (void)image->getROPixels(nullptr, &fResultBitmap); - } - - // fResultBitmap.getPixels() may be null, but our caller knows to check fPixmap.addr() - // and will destroy us if it is nullptr. - fPixmap.reset(fResultBitmap.info(), fResultBitmap.getPixels(), fResultBitmap.rowBytes()); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - SkMipmapAccessor::SkMipmapAccessor(const SkImage_Base* image, const SkMatrix& inv, SkMipmapMode requestedMode) { fResolvedMode = requestedMode; @@ -134,6 +53,11 @@ SkMipmapAccessor::SkMipmapAccessor(const SkImage_Base* image, const SkMatrix& in } } + auto post_scale = [image, inv](const SkPixmap& pm) { + return SkMatrix::Scale(SkIntToScalar(pm.width()) / image->width(), + SkIntToScalar(pm.height()) / image->height()) * inv; + }; + int levelNum = sk_float_floor2int(level); float lowerWeight = level - levelNum; // fract(level) SkASSERT(levelNum >= 0); @@ -164,10 +88,12 @@ SkMipmapAccessor::SkMipmapAccessor(const SkImage_Base* image, const SkMatrix& in if (fCurrMip->getLevel(levelNum, &levelRec)) { fLower = levelRec.fPixmap; fLowerWeight = lowerWeight; + fLowerInv = post_scale(fLower); } else { fResolvedMode = SkMipmapMode::kNearest; } } } } + fUpperInv = post_scale(fUpper); } diff --git a/src/core/SkBitmapController.h b/src/core/SkBitmapController.h index 7460027afe..794abee2ed 100644 --- a/src/core/SkBitmapController.h +++ b/src/core/SkBitmapController.h @@ -9,62 +9,35 @@ #define SkBitmapController_DEFINED #include "include/core/SkBitmap.h" -#include "include/core/SkFilterQuality.h" #include "include/core/SkImage.h" #include "include/core/SkMatrix.h" -#include "src/core/SkBitmapCache.h" #include "src/core/SkMipmap.h" +#include class SkImage_Base; -/** - * Handles request to scale, filter, and lock a bitmap to be rasterized. - */ -class SkBitmapController : ::SkNoncopyable { -public: - class State : ::SkNoncopyable { - public: - State(const SkImage_Base*, const SkMatrix& inv, const SkSamplingOptions&); - - const SkPixmap& pixmap() const { return fPixmap; } - const SkMatrix& invMatrix() const { return fInvMatrix; } - const SkSamplingOptions& sampling() const { return fSampling; } - - private: - bool extractMipLevel(const SkImage_Base*); - - SkPixmap fPixmap; - SkMatrix fInvMatrix; - SkSamplingOptions fSampling; - - // Pixmap storage. - SkBitmap fResultBitmap; - sk_sp fCurrMip; - - }; - - static State* RequestBitmap(const SkImage_Base*, const SkMatrix& inverse, - const SkSamplingOptions&, SkArenaAlloc*); - -private: - SkBitmapController() = delete; -}; - class SkMipmapAccessor : ::SkNoncopyable { public: SkMipmapAccessor(const SkImage_Base*, const SkMatrix& inv, SkMipmapMode requestedMode); - const SkPixmap& level() const { return fUpper; } - // only valid if mode() == kLinear - const SkPixmap& lowerLevel() const { return fLower; } - // 0....1. Will be 1.0 if there is no lowerLevel - float lowerWeight() const { return fLowerWeight; } - SkMipmapMode mode() const { return fResolvedMode; } + std::pair level() const { return std::make_pair(fUpper, fUpperInv); } + + std::pair lowerLevel() const { + SkASSERT(this->mode() == SkMipmapMode::kLinear); + return std::make_pair(fLower, fLowerInv); + } + + // 0....1. Will be 0 if there is no lowerLevel + float lowerWeight() const { return fLowerWeight; } + + SkMipmapMode mode() const { return fResolvedMode; } private: SkPixmap fUpper, fLower; // only valid for mip_linear float fLowerWeight; // lower * weight + upper * (1 - weight) + SkMatrix fUpperInv, + fLowerInv; SkMipmapMode fResolvedMode; // these manage lifetime for the buffers diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp index f46e68b7c8..d028cd2792 100755 --- a/src/core/SkBitmapProcState.cpp +++ b/src/core/SkBitmapProcState.cpp @@ -143,10 +143,8 @@ SkBitmapProcState::SkBitmapProcState(const SkImage_Base* image, SkTileMode tmx, : fImage(image) , fTileModeX(tmx) , fTileModeY(tmy) - , fBMState(nullptr) {} - // true iff the matrix has a scale and no more than an optional translate. static bool matrix_only_scale_translate(const SkMatrix& m) { return (m.getType() & ~SkMatrix::kTranslate_Mask) == SkMatrix::kScale_Mask; @@ -187,23 +185,22 @@ bool SkBitmapProcState::init(const SkMatrix& inv, SkColor paintColor, const SkSamplingOptions& sampling) { SkASSERT(!inv.hasPerspective()); SkASSERT(SkOpts::S32_alpha_D32_filter_DXDY || inv.isScaleTranslate()); + SkASSERT(!sampling.useCubic); + SkASSERT(sampling.mipmap != SkMipmapMode::kLinear); fPixmap.reset(); fInvMatrix = inv; fBilerp = false; - fBMState = SkBitmapController::RequestBitmap(fImage, inv, sampling, &fAlloc); + auto* access = fAlloc.make(fImage, inv, sampling.mipmap); + std::tie(fPixmap, fInvMatrix) = access->level(); - // Note : we allow the controller to return an empty (zero-dimension) result. Should we? - if (nullptr == fBMState || fBMState->pixmap().info().isEmpty()) { + // Do we need this check? + if (fPixmap.info().isEmpty()) { return false; } - fPixmap = fBMState->pixmap(); - fInvMatrix = fBMState->invMatrix(); fPaintColor = paintColor; - SkASSERT(!fBMState->sampling().useCubic); - SkASSERT(fBMState->sampling().mipmap == SkMipmapMode::kNone); - fBilerp = fBMState->sampling().filter == SkFilterMode::kLinear; + fBilerp = sampling.filter == SkFilterMode::kLinear; SkASSERT(fPixmap.addr()); bool integral_translate_only = just_trans_integral(fInvMatrix); diff --git a/src/core/SkBitmapProcState.h b/src/core/SkBitmapProcState.h index c4799a2042..16f3d93b4f 100644 --- a/src/core/SkBitmapProcState.h +++ b/src/core/SkBitmapProcState.h @@ -91,7 +91,6 @@ private: kBMStateSize = 136 // found by inspection. if too small, we will call new/delete }; SkSTArenaAlloc fAlloc; - SkBitmapController::State* fBMState; ShaderProc32 fShaderProc32; // chooseProcs // These are used if the shaderproc is nullptr diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp index fbc1a8a0ef..806fb9bcdc 100755 --- a/src/shaders/SkImageShader.cpp +++ b/src/shaders/SkImageShader.cpp @@ -11,6 +11,7 @@ #include "src/core/SkBitmapController.h" #include "src/core/SkColorSpacePriv.h" #include "src/core/SkColorSpaceXformSteps.h" +#include "src/core/SkMatrixPriv.h" #include "src/core/SkMatrixProvider.h" #include "src/core/SkOpts.h" #include "src/core/SkRasterPipeline.h" @@ -629,16 +630,14 @@ bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater return false; } - const auto* state = SkBitmapController::RequestBitmap(as_IB(fImage.get()), - matrix, sampling, alloc); - if (!state) { - return false; + if (sampling.useCubic && + SkMatrixPriv::AdjustHighQualityFilterLevel(matrix, true) != kHigh_SkFilterQuality) + { + sampling = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNearest); } - - const SkPixmap& pm = state->pixmap(); - matrix = state->invMatrix(); - sampling = state->sampling(); - auto info = pm.info(); + auto* access = alloc->make(as_IB(fImage.get()), matrix, sampling.mipmap); + SkPixmap pm; + std::tie(pm, matrix) = access->level(); p->append(SkRasterPipeline::seed_shader); @@ -701,7 +700,7 @@ bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater } void* ctx = gather; - switch (info.colorType()) { + switch (pm.colorType()) { case kAlpha_8_SkColorType: p->append(SkRasterPipeline::gather_a8, ctx); break; case kA16_unorm_SkColorType: p->append(SkRasterPipeline::gather_a16, ctx); break; case kA16_float_SkColorType: p->append(SkRasterPipeline::gather_af16, ctx); break; @@ -745,11 +744,11 @@ bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater }; auto append_misc = [&] { - SkColorSpace* cs = info.colorSpace(); - SkAlphaType at = info.alphaType(); + SkColorSpace* cs = pm.colorSpace(); + SkAlphaType at = pm.alphaType(); // Color for A8 images comes from the paint. TODO: all alpha images? none? - if (info.colorType() == kAlpha_8_SkColorType) { + if (pm.colorType() == kAlpha_8_SkColorType) { SkColor4f rgb = rec.fPaint.getColor4f(); p->append_set_rgb(alloc, rgb); @@ -774,7 +773,7 @@ bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater }; // Check for fast-path stages. - auto ct = info.colorType(); + auto ct = pm.colorType(); if (true && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) && !sampling.useCubic && sampling.filter == SkFilterMode::kLinear @@ -901,40 +900,28 @@ skvm::Color SkImageShader::onProgram(skvm::Builder* p, } baseInv.normalizePerspective(); - const SkPixmap *upper = nullptr, - *lower = nullptr; - SkMatrix upperInv; - float lowerWeight = 0; - - auto post_scale = [&](SkISize level, const SkMatrix& base) { - return SkMatrix::Scale(SkIntToScalar(level.width()) / fImage->width(), - SkIntToScalar(level.height()) / fImage->height()) - * base; - }; - auto sampling = fUseSamplingOptions ? fSampling : SkSamplingOptions(paintQuality); - if (sampling.useCubic) { - auto* access = alloc->make(as_IB(fImage.get()), baseInv, - SkMipmapMode::kNone); - upper = &access->level(); - upperInv = post_scale(upper->dimensions(), baseInv); - } else { - auto* access = alloc->make(as_IB(fImage.get()), baseInv, - sampling.mipmap); - upper = &access->level(); - upperInv = post_scale(upper->dimensions(), baseInv); - lowerWeight = access->lowerWeight(); - if (lowerWeight > 0) { - lower = &access->lowerLevel(); - } + auto* access = alloc->make(as_IB(fImage.get()), baseInv, sampling.mipmap); + + auto [upper, upperInv] = access->level(); + if (!sampling.useCubic) { sampling = tweak_filter_and_inv_matrix(sampling, &upperInv); } + SkPixmap lowerPixmap; + SkMatrix lowerInv; + SkPixmap* lower = nullptr; + float lowerWeight = access->lowerWeight(); + if (lowerWeight > 0) { + std::tie(lowerPixmap, lowerInv) = access->lowerLevel(); + lower = &lowerPixmap; + } + skvm::Coord upperLocal = SkShaderBase::ApplyMatrix(p, upperInv, origLocal, uniforms); // All existing SkColorTypes pass these checks. We'd only fail here adding new ones. skvm::PixelFormat unused; - if (true && !SkColorType_to_PixelFormat(upper->colorType(), &unused)) { + if (true && !SkColorType_to_PixelFormat(upper.colorType(), &unused)) { return {}; } if (lower && !SkColorType_to_PixelFormat(lower->colorType(), &unused)) { @@ -942,8 +929,8 @@ skvm::Color SkImageShader::onProgram(skvm::Builder* p, } // We can exploit image opacity to skip work unpacking alpha channels. - const bool input_is_opaque = SkAlphaTypeIsOpaque(upper->alphaType()) - || SkColorTypeIsAlwaysOpaque(upper->colorType()); + const bool input_is_opaque = SkAlphaTypeIsOpaque(upper.alphaType()) + || SkColorTypeIsAlwaysOpaque(upper.colorType()); // Each call to sample() will try to rewrite the same uniforms over and over, // so remember where we start and reset back there each time. That way each @@ -1121,9 +1108,8 @@ skvm::Color SkImageShader::onProgram(skvm::Builder* p, } }; - skvm::Color c = sample_level(*upper, upperInv, upperLocal); + skvm::Color c = sample_level(upper, upperInv, upperLocal); if (lower) { - auto lowerInv = post_scale(lower->dimensions(), baseInv); auto lowerLocal = SkShaderBase::ApplyMatrix(p, lowerInv, origLocal, uniforms); // lower * weight + upper * (1 - weight) c = lerp(c, @@ -1140,9 +1126,9 @@ skvm::Color SkImageShader::onProgram(skvm::Builder* p, } // Alpha-only images get their color from the paint (already converted to dst color space). - SkColorSpace* cs = upper->colorSpace(); - SkAlphaType at = upper->alphaType(); - if (SkColorTypeIsAlphaOnly(upper->colorType())) { + SkColorSpace* cs = upper.colorSpace(); + SkAlphaType at = upper.alphaType(); + if (SkColorTypeIsAlphaOnly(upper.colorType())) { c.r = paint.r; c.g = paint.g; c.b = paint.b;