pass more information to shaders

If we're going to write an image shader, we're going to need to know the
matrix and some bits of the paint.

Instead of all possible information, I decided to pass just what the
image shader needs for now, and will pass along more as I go.

I've added a stub implementation of SkImageFilter as a way to make sure
I'm threading everything it'll need through.

Change-Id: I98de1724056dbc0902a34fc31da8ec6bdf38dc34
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/261801
Commit-Queue: Mike Klein <mtklein@google.com>
Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
Mike Klein 2019-12-30 15:24:18 -06:00 committed by Skia Commit-Bot
parent 107c66669d
commit 4f275cfe0e
10 changed files with 79 additions and 21 deletions

View File

@ -21,6 +21,8 @@
// Just a tiny example that the (x,y) coordinate parameters are vaguely working.
// In this case we'll fade the red channel over its span vertically using `y`,
// and green horizontally using `x`.
//
// TODO: this doesn't really need to wrap another shader.
struct Fade : public SkShaderBase {
explicit Fade(sk_sp<SkShader> shader) : fShader(std::move(shader)) {}
@ -29,11 +31,14 @@ struct Fade : public SkShaderBase {
bool isOpaque() const override { return fShader->isOpaque(); }
bool onProgram(skvm::Builder* p,
SkColorSpace* dstCS,
const SkMatrix& ctm, const SkMatrix* localM,
SkFilterQuality quality, SkColorSpace* dstCS,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const override {
if (as_SB(fShader)->program(p, dstCS,
if (as_SB(fShader)->program(p,
ctm, localM,
quality, dstCS,
uniforms,
x,y, r,g,b,a)) {
// In this GM `y` will range over 0-50 and `x` over 50-100.

View File

@ -36,6 +36,8 @@ namespace {
SkAlphaType alphaType;
SkBlendMode blendMode;
Coverage coverage;
SkFilterQuality quality;
SkMatrix ctm;
Params withCoverage(Coverage c) const {
Params p = *this;
@ -52,6 +54,9 @@ namespace {
alphaType,
blendMode,
coverage;
// Params::quality and Params::ctm are only passed to shader->program(),
// not used here by the blitter itself. No need to include them in the key;
// they'll be folded into the shader key if used.
bool operator==(const Key& that) const {
return this->colorSpace == that.colorSpace
@ -148,7 +153,8 @@ namespace {
offsetof(BlitterUniforms, y)));
skvm::F32 r,g,b,a;
if (shader->program(&p,
params.colorSpace.get(),
params.ctm, /*localM=*/nullptr,
params.quality, params.colorSpace.get(),
uniforms,
x,y, &r,&g,&b,&a)) {
shaderHash = p.hash();
@ -200,7 +206,8 @@ namespace {
y = to_f32(uniform32(uniforms->ptr,
offsetof(BlitterUniforms, y)));
SkAssertResult(as_SB(params.shader)->program(this,
params.colorSpace.get(),
params.ctm, /*localM=*/nullptr,
params.quality, params.colorSpace.get(),
uniforms,
x,y, &src.r, &src.g, &src.b, &src.a));
// We don't know if the src color is normalized (logical [0,1], premul [0,a]) or not.
@ -346,6 +353,7 @@ namespace {
};
// Scale the output of another shader by alpha.
// TODO: this would make more sense as a color filter
struct AlphaShader : public SkShaderBase {
AlphaShader(sk_sp<SkShader> shader, float alpha)
: fShader(std::move(shader))
@ -355,11 +363,14 @@ namespace {
float fAlpha;
bool onProgram(skvm::Builder* p,
SkColorSpace* dstCS,
const SkMatrix& ctm, const SkMatrix* localM,
SkFilterQuality quality, SkColorSpace* dstCS,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const override {
if (as_SB(fShader)->program(p, dstCS,
if (as_SB(fShader)->program(p,
ctm, localM,
quality, dstCS,
uniforms,
x,y, r,g,b,a)) {
skvm::F32 A = p->uniformF(uniforms->pushF(fAlpha));
@ -377,7 +388,9 @@ namespace {
const char* getTypeName() const override { return "AlphaShader"; }
};
static Params effective_params(const SkPixmap& device, const SkPaint& paint) {
static Params effective_params(const SkPixmap& device,
const SkPaint& paint,
const SkMatrix& ctm) {
// Color filters have been handled for us by SkBlitter::Choose().
SkASSERT(!paint.getColorFilter());
@ -415,15 +428,17 @@ namespace {
device.alphaType(),
blendMode,
Coverage::Full, // Placeholder... withCoverage() will change as needed.
paint.getFilterQuality(),
ctm,
};
}
class Blitter final : public SkBlitter {
public:
Blitter(const SkPixmap& device, const SkPaint& paint, bool* ok)
Blitter(const SkPixmap& device, const SkPaint& paint, const SkMatrix& ctm, bool* ok)
: fDevice(device)
, fUniforms(kBlitterUniformsCount)
, fParams(effective_params(device, paint))
, fParams(effective_params(device, paint, ctm))
, fKey(Builder::CacheKey(fParams, &fUniforms, ok))
{}
@ -676,6 +691,6 @@ SkBlitter* SkCreateSkVMBlitter(const SkPixmap& device,
const SkMatrix& ctm,
SkArenaAlloc* alloc) {
bool ok = true;
auto blitter = alloc->make<Blitter>(device, paint, &ok);
auto blitter = alloc->make<Blitter>(device, paint, ctm, &ok);
return ok ? blitter : nullptr;
}

View File

@ -61,12 +61,13 @@ bool SkColorFilterShader::onAppendStages(const SkStageRec& rec) const {
}
bool SkColorFilterShader::onProgram(skvm::Builder* p,
SkColorSpace* dstCS,
const SkMatrix& ctm, const SkMatrix* localM,
SkFilterQuality quality, SkColorSpace* dstCS,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
// Run the shader.
if (!as_SB(fShader)->program(p, dstCS, uniforms, x,y, r,g,b,a)) {
if (!as_SB(fShader)->program(p, ctm,localM, quality,dstCS, uniforms, x,y, r,g,b,a)) {
return false;
}

View File

@ -27,7 +27,8 @@ private:
bool onAppendStages(const SkStageRec&) const override;
bool onProgram(skvm::Builder*,
SkColorSpace* dstCS,
const SkMatrix& ctm, const SkMatrix* localM,
SkFilterQuality quality, SkColorSpace* dstCS,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const override;

View File

@ -106,7 +106,8 @@ static bool common_program(SkColor4f color, SkColorSpace* cs,
}
bool SkColorShader::onProgram(skvm::Builder* p,
SkColorSpace* dstCS,
const SkMatrix& /*ctm*/, const SkMatrix* /*localM*/,
SkFilterQuality /*quality*/, SkColorSpace* dstCS,
skvm::Uniforms* uniforms,
skvm::F32 /*x*/, skvm::F32 /*y*/,
skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
@ -114,7 +115,8 @@ bool SkColorShader::onProgram(skvm::Builder* p,
p, dstCS, uniforms, r,g,b,a);
}
bool SkColor4Shader::onProgram(skvm::Builder* p,
SkColorSpace* dstCS,
const SkMatrix& /*ctm*/, const SkMatrix* /*localM*/,
SkFilterQuality /*quality*/, SkColorSpace* dstCS,
skvm::Uniforms* uniforms,
skvm::F32 /*x*/, skvm::F32 /*y*/,
skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {

View File

@ -45,7 +45,8 @@ private:
bool onAppendStages(const SkStageRec&) const override;
bool onProgram(skvm::Builder*,
SkColorSpace* dstCS,
const SkMatrix& ctm, const SkMatrix* localM,
SkFilterQuality quality, SkColorSpace* dstCS,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const override;
@ -71,7 +72,8 @@ private:
bool onAppendStages(const SkStageRec&) const override;
bool onProgram(skvm::Builder*,
SkColorSpace* dstCS,
const SkMatrix& ctm, const SkMatrix* localM,
SkFilterQuality quality, SkColorSpace* dstCS,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const override;

View File

@ -636,3 +636,25 @@ SkStageUpdater* SkImageShader::onAppendUpdatableStages(const SkStageRec& rec) co
return this->doStages(rec, updater) ? updater : nullptr;
}
bool SkImageShader::onProgram(skvm::Builder* p,
const SkMatrix& ctm, const SkMatrix* localM,
SkFilterQuality quality, SkColorSpace* dstCS,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
SkMatrix inv;
if (!this->computeTotalInverse(ctm, localM, &inv)) {
return false;
}
SkBitmapController::State state{as_IB(fImage.get()), inv, quality};
const SkPixmap& pm = state.pixmap();
if (!pm.addr()) {
return false;
}
inv = state.invMatrix();
quality = state.quality();
return false;
}

View File

@ -47,6 +47,13 @@ private:
bool onAppendStages(const SkStageRec&) const override;
SkStageUpdater* onAppendUpdatableStages(const SkStageRec&) const override;
bool onProgram(skvm::Builder*,
const SkMatrix& ctm, const SkMatrix* localM,
SkFilterQuality quality, SkColorSpace* dstCS,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const override;
bool doStages(const SkStageRec&, SkImageStageUpdater* = nullptr) const;
sk_sp<SkImage> fImage;

View File

@ -198,7 +198,8 @@ bool SkShaderBase::onAppendStages(const SkStageRec& rec) const {
}
bool SkShaderBase::program(skvm::Builder* p,
SkColorSpace* dstCS,
const SkMatrix& ctm, const SkMatrix* localM,
SkFilterQuality quality, SkColorSpace* dstCS,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
@ -214,7 +215,7 @@ bool SkShaderBase::program(skvm::Builder* p,
// shader program hash and blitter Key. This makes it safe for us to use
// that bit to make decisions when constructing an SkVMBlitter, like doing
// SrcOver -> Src strength reduction.
if (this->onProgram(p, dstCS, uniforms, x,y, r,g,b,a)) {
if (this->onProgram(p, ctm,localM, quality,dstCS, uniforms, x,y, r,g,b,a)) {
if (this->isOpaque()) {
*a = p->splat(1.0f);
}

View File

@ -208,13 +208,15 @@ public:
}
bool program(skvm::Builder*,
SkColorSpace* dstCS,
const SkMatrix& ctm, const SkMatrix* localM,
SkFilterQuality quality, SkColorSpace* dstCS,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const;
virtual bool onProgram(skvm::Builder*,
SkColorSpace* dstCS,
const SkMatrix& ctm, const SkMatrix* localM,
SkFilterQuality quality, SkColorSpace* dstCS,
skvm::Uniforms* uniforms,
skvm::F32 x, skvm::F32 y,
skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {