stop using bitmapcontroller

This moves all shaders to a common utility for resolve the mip-level
request.

Change-Id: I26709d5a55adf97cb4c61473527a9bbbdc689aa5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/339897
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Mike Reed <reed@google.com>
This commit is contained in:
Mike Reed 2020-12-02 20:41:52 -05:00 committed by Skia Commit-Bot
parent f5e1bf9f01
commit 8c1ad7e8d3
5 changed files with 61 additions and 180 deletions

View File

@ -11,7 +11,6 @@
#include "src/core/SkArenaAlloc.h" #include "src/core/SkArenaAlloc.h"
#include "src/core/SkBitmapCache.h" #include "src/core/SkBitmapCache.h"
#include "src/core/SkBitmapController.h" #include "src/core/SkBitmapController.h"
#include "src/core/SkMatrixPriv.h"
#include "src/core/SkMipmap.h" #include "src/core/SkMipmap.h"
#include "src/image/SkImage_Base.h" #include "src/image/SkImage_Base.h"
@ -27,86 +26,6 @@ static sk_sp<const SkMipmap> try_load_mips(const SkImage_Base* image) {
return mips; return mips;
} }
///////////////////////////////////////////////////////////////////////////////////////////////////
SkBitmapController::State* SkBitmapController::RequestBitmap(const SkImage_Base* image,
const SkMatrix& inv,
const SkSamplingOptions& sampling,
SkArenaAlloc* alloc) {
auto* state = alloc->make<SkBitmapController::State>(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, SkMipmapAccessor::SkMipmapAccessor(const SkImage_Base* image, const SkMatrix& inv,
SkMipmapMode requestedMode) { SkMipmapMode requestedMode) {
fResolvedMode = 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); int levelNum = sk_float_floor2int(level);
float lowerWeight = level - levelNum; // fract(level) float lowerWeight = level - levelNum; // fract(level)
SkASSERT(levelNum >= 0); SkASSERT(levelNum >= 0);
@ -164,10 +88,12 @@ SkMipmapAccessor::SkMipmapAccessor(const SkImage_Base* image, const SkMatrix& in
if (fCurrMip->getLevel(levelNum, &levelRec)) { if (fCurrMip->getLevel(levelNum, &levelRec)) {
fLower = levelRec.fPixmap; fLower = levelRec.fPixmap;
fLowerWeight = lowerWeight; fLowerWeight = lowerWeight;
fLowerInv = post_scale(fLower);
} else { } else {
fResolvedMode = SkMipmapMode::kNearest; fResolvedMode = SkMipmapMode::kNearest;
} }
} }
} }
} }
fUpperInv = post_scale(fUpper);
} }

View File

@ -9,62 +9,35 @@
#define SkBitmapController_DEFINED #define SkBitmapController_DEFINED
#include "include/core/SkBitmap.h" #include "include/core/SkBitmap.h"
#include "include/core/SkFilterQuality.h"
#include "include/core/SkImage.h" #include "include/core/SkImage.h"
#include "include/core/SkMatrix.h" #include "include/core/SkMatrix.h"
#include "src/core/SkBitmapCache.h"
#include "src/core/SkMipmap.h" #include "src/core/SkMipmap.h"
#include <tuple>
class SkImage_Base; 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<const SkMipmap> fCurrMip;
};
static State* RequestBitmap(const SkImage_Base*, const SkMatrix& inverse,
const SkSamplingOptions&, SkArenaAlloc*);
private:
SkBitmapController() = delete;
};
class SkMipmapAccessor : ::SkNoncopyable { class SkMipmapAccessor : ::SkNoncopyable {
public: public:
SkMipmapAccessor(const SkImage_Base*, const SkMatrix& inv, SkMipmapMode requestedMode); SkMipmapAccessor(const SkImage_Base*, const SkMatrix& inv, SkMipmapMode requestedMode);
const SkPixmap& level() const { return fUpper; } std::pair<SkPixmap, SkMatrix> level() const { return std::make_pair(fUpper, fUpperInv); }
// only valid if mode() == kLinear
const SkPixmap& lowerLevel() const { return fLower; } std::pair<SkPixmap, SkMatrix> lowerLevel() const {
// 0....1. Will be 1.0 if there is no lowerLevel SkASSERT(this->mode() == SkMipmapMode::kLinear);
float lowerWeight() const { return fLowerWeight; } return std::make_pair(fLower, fLowerInv);
SkMipmapMode mode() const { return fResolvedMode; } }
// 0....1. Will be 0 if there is no lowerLevel
float lowerWeight() const { return fLowerWeight; }
SkMipmapMode mode() const { return fResolvedMode; }
private: private:
SkPixmap fUpper, SkPixmap fUpper,
fLower; // only valid for mip_linear fLower; // only valid for mip_linear
float fLowerWeight; // lower * weight + upper * (1 - weight) float fLowerWeight; // lower * weight + upper * (1 - weight)
SkMatrix fUpperInv,
fLowerInv;
SkMipmapMode fResolvedMode; SkMipmapMode fResolvedMode;
// these manage lifetime for the buffers // these manage lifetime for the buffers

View File

@ -143,10 +143,8 @@ SkBitmapProcState::SkBitmapProcState(const SkImage_Base* image, SkTileMode tmx,
: fImage(image) : fImage(image)
, fTileModeX(tmx) , fTileModeX(tmx)
, fTileModeY(tmy) , fTileModeY(tmy)
, fBMState(nullptr)
{} {}
// true iff the matrix has a scale and no more than an optional translate. // true iff the matrix has a scale and no more than an optional translate.
static bool matrix_only_scale_translate(const SkMatrix& m) { static bool matrix_only_scale_translate(const SkMatrix& m) {
return (m.getType() & ~SkMatrix::kTranslate_Mask) == SkMatrix::kScale_Mask; return (m.getType() & ~SkMatrix::kTranslate_Mask) == SkMatrix::kScale_Mask;
@ -187,23 +185,22 @@ bool SkBitmapProcState::init(const SkMatrix& inv, SkColor paintColor,
const SkSamplingOptions& sampling) { const SkSamplingOptions& sampling) {
SkASSERT(!inv.hasPerspective()); SkASSERT(!inv.hasPerspective());
SkASSERT(SkOpts::S32_alpha_D32_filter_DXDY || inv.isScaleTranslate()); SkASSERT(SkOpts::S32_alpha_D32_filter_DXDY || inv.isScaleTranslate());
SkASSERT(!sampling.useCubic);
SkASSERT(sampling.mipmap != SkMipmapMode::kLinear);
fPixmap.reset(); fPixmap.reset();
fInvMatrix = inv; fInvMatrix = inv;
fBilerp = false; fBilerp = false;
fBMState = SkBitmapController::RequestBitmap(fImage, inv, sampling, &fAlloc); auto* access = fAlloc.make<SkMipmapAccessor>(fImage, inv, sampling.mipmap);
std::tie(fPixmap, fInvMatrix) = access->level();
// Note : we allow the controller to return an empty (zero-dimension) result. Should we? // Do we need this check?
if (nullptr == fBMState || fBMState->pixmap().info().isEmpty()) { if (fPixmap.info().isEmpty()) {
return false; return false;
} }
fPixmap = fBMState->pixmap();
fInvMatrix = fBMState->invMatrix();
fPaintColor = paintColor; fPaintColor = paintColor;
SkASSERT(!fBMState->sampling().useCubic); fBilerp = sampling.filter == SkFilterMode::kLinear;
SkASSERT(fBMState->sampling().mipmap == SkMipmapMode::kNone);
fBilerp = fBMState->sampling().filter == SkFilterMode::kLinear;
SkASSERT(fPixmap.addr()); SkASSERT(fPixmap.addr());
bool integral_translate_only = just_trans_integral(fInvMatrix); bool integral_translate_only = just_trans_integral(fInvMatrix);

View File

@ -91,7 +91,6 @@ private:
kBMStateSize = 136 // found by inspection. if too small, we will call new/delete kBMStateSize = 136 // found by inspection. if too small, we will call new/delete
}; };
SkSTArenaAlloc<kBMStateSize> fAlloc; SkSTArenaAlloc<kBMStateSize> fAlloc;
SkBitmapController::State* fBMState;
ShaderProc32 fShaderProc32; // chooseProcs ShaderProc32 fShaderProc32; // chooseProcs
// These are used if the shaderproc is nullptr // These are used if the shaderproc is nullptr

View File

@ -11,6 +11,7 @@
#include "src/core/SkBitmapController.h" #include "src/core/SkBitmapController.h"
#include "src/core/SkColorSpacePriv.h" #include "src/core/SkColorSpacePriv.h"
#include "src/core/SkColorSpaceXformSteps.h" #include "src/core/SkColorSpaceXformSteps.h"
#include "src/core/SkMatrixPriv.h"
#include "src/core/SkMatrixProvider.h" #include "src/core/SkMatrixProvider.h"
#include "src/core/SkOpts.h" #include "src/core/SkOpts.h"
#include "src/core/SkRasterPipeline.h" #include "src/core/SkRasterPipeline.h"
@ -629,16 +630,14 @@ bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater
return false; return false;
} }
const auto* state = SkBitmapController::RequestBitmap(as_IB(fImage.get()), if (sampling.useCubic &&
matrix, sampling, alloc); SkMatrixPriv::AdjustHighQualityFilterLevel(matrix, true) != kHigh_SkFilterQuality)
if (!state) { {
return false; sampling = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNearest);
} }
auto* access = alloc->make<SkMipmapAccessor>(as_IB(fImage.get()), matrix, sampling.mipmap);
const SkPixmap& pm = state->pixmap(); SkPixmap pm;
matrix = state->invMatrix(); std::tie(pm, matrix) = access->level();
sampling = state->sampling();
auto info = pm.info();
p->append(SkRasterPipeline::seed_shader); p->append(SkRasterPipeline::seed_shader);
@ -701,7 +700,7 @@ bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater
} }
void* ctx = gather; void* ctx = gather;
switch (info.colorType()) { switch (pm.colorType()) {
case kAlpha_8_SkColorType: p->append(SkRasterPipeline::gather_a8, ctx); break; case kAlpha_8_SkColorType: p->append(SkRasterPipeline::gather_a8, ctx); break;
case kA16_unorm_SkColorType: p->append(SkRasterPipeline::gather_a16, ctx); break; case kA16_unorm_SkColorType: p->append(SkRasterPipeline::gather_a16, ctx); break;
case kA16_float_SkColorType: p->append(SkRasterPipeline::gather_af16, 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 = [&] { auto append_misc = [&] {
SkColorSpace* cs = info.colorSpace(); SkColorSpace* cs = pm.colorSpace();
SkAlphaType at = info.alphaType(); SkAlphaType at = pm.alphaType();
// Color for A8 images comes from the paint. TODO: all alpha images? none? // 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(); SkColor4f rgb = rec.fPaint.getColor4f();
p->append_set_rgb(alloc, rgb); p->append_set_rgb(alloc, rgb);
@ -774,7 +773,7 @@ bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater
}; };
// Check for fast-path stages. // Check for fast-path stages.
auto ct = info.colorType(); auto ct = pm.colorType();
if (true if (true
&& (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType) && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
&& !sampling.useCubic && sampling.filter == SkFilterMode::kLinear && !sampling.useCubic && sampling.filter == SkFilterMode::kLinear
@ -901,40 +900,28 @@ skvm::Color SkImageShader::onProgram(skvm::Builder* p,
} }
baseInv.normalizePerspective(); 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); auto sampling = fUseSamplingOptions ? fSampling : SkSamplingOptions(paintQuality);
if (sampling.useCubic) { auto* access = alloc->make<SkMipmapAccessor>(as_IB(fImage.get()), baseInv, sampling.mipmap);
auto* access = alloc->make<SkMipmapAccessor>(as_IB(fImage.get()), baseInv,
SkMipmapMode::kNone); auto [upper, upperInv] = access->level();
upper = &access->level(); if (!sampling.useCubic) {
upperInv = post_scale(upper->dimensions(), baseInv);
} else {
auto* access = alloc->make<SkMipmapAccessor>(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();
}
sampling = tweak_filter_and_inv_matrix(sampling, &upperInv); 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); skvm::Coord upperLocal = SkShaderBase::ApplyMatrix(p, upperInv, origLocal, uniforms);
// All existing SkColorTypes pass these checks. We'd only fail here adding new ones. // All existing SkColorTypes pass these checks. We'd only fail here adding new ones.
skvm::PixelFormat unused; skvm::PixelFormat unused;
if (true && !SkColorType_to_PixelFormat(upper->colorType(), &unused)) { if (true && !SkColorType_to_PixelFormat(upper.colorType(), &unused)) {
return {}; return {};
} }
if (lower && !SkColorType_to_PixelFormat(lower->colorType(), &unused)) { 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. // We can exploit image opacity to skip work unpacking alpha channels.
const bool input_is_opaque = SkAlphaTypeIsOpaque(upper->alphaType()) const bool input_is_opaque = SkAlphaTypeIsOpaque(upper.alphaType())
|| SkColorTypeIsAlwaysOpaque(upper->colorType()); || SkColorTypeIsAlwaysOpaque(upper.colorType());
// Each call to sample() will try to rewrite the same uniforms over and over, // 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 // 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) { if (lower) {
auto lowerInv = post_scale(lower->dimensions(), baseInv);
auto lowerLocal = SkShaderBase::ApplyMatrix(p, lowerInv, origLocal, uniforms); auto lowerLocal = SkShaderBase::ApplyMatrix(p, lowerInv, origLocal, uniforms);
// lower * weight + upper * (1 - weight) // lower * weight + upper * (1 - weight)
c = lerp(c, 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). // Alpha-only images get their color from the paint (already converted to dst color space).
SkColorSpace* cs = upper->colorSpace(); SkColorSpace* cs = upper.colorSpace();
SkAlphaType at = upper->alphaType(); SkAlphaType at = upper.alphaType();
if (SkColorTypeIsAlphaOnly(upper->colorType())) { if (SkColorTypeIsAlphaOnly(upper.colorType())) {
c.r = paint.r; c.r = paint.r;
c.g = paint.g; c.g = paint.g;
c.b = paint.b; c.b = paint.b;