handle unpremul in skvm blitter

- Centralize unpremul() and premul().
- Handle unpremul surfaces.
- Handle unpremul images.

Change-Id: I99967c66f73fefe5940bb17d1ecc3e6d85559cf8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/262504
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
This commit is contained in:
Mike Klein 2020-01-06 18:39:42 -06:00 committed by Skia Commit-Bot
parent d9ecf8bb93
commit 4bc86d590e
5 changed files with 46 additions and 28 deletions

View File

@ -85,18 +85,8 @@ bool SkColorFilter_Matrix::onProgram(skvm::Builder* p,
skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
// TODO: specialize generated code on the 0/1 values of fMatrix?
if (fDomain == Domain::kRGBA) {
// Unpremul.
skvm::F32 invA = p->div(p->splat(1.0f), *a),
inf = p->bit_cast(p->splat(0x7f800000));
p->unpremul(r,g,b,*a);
// If *a is 0, so are *r,*g,*b, so set invA to 0 to avoid 0*inf=NaN (instead 0*0 = 0).
invA = p->bit_cast(p->bit_and(p->lt(invA, inf),
p->bit_cast(invA)));
*r = p->mul(*r, invA);
*g = p->mul(*g, invA);
*b = p->mul(*b, invA);
// Apply matrix.
skvm::Builder::Uniform u = uniforms->pushF(fMatrix, 20);
auto m = [&](int i) { return p->uniformF(u.ptr, u.offset + 4*i); };
@ -113,11 +103,7 @@ bool SkColorFilter_Matrix::onProgram(skvm::Builder* p,
*b = rgba[2];
*a = rgba[3];
// Premul.
*r = p->mul(*r, *a);
*g = p->mul(*g, *a);
*b = p->mul(*b, *a);
p->premul(r,g,b,*a);
return true;
}
return false;

View File

@ -797,6 +797,22 @@ namespace skvm {
};
}
void Builder::unpremul(F32* r, F32* g, F32* b, F32 a) {
skvm::F32 invA = div(splat(1.0f), a),
inf = bit_cast(splat(0x7f800000));
// If a is 0, so are *r,*g,*b, so set invA to 0 to avoid 0*inf=NaN (instead 0*0 = 0).
invA = bit_cast(bit_and(lt(invA, inf),
bit_cast(invA)));
*r = mul(*r, invA);
*g = mul(*g, invA);
*b = mul(*b, invA);
}
void Builder::premul(F32* r, F32* g, F32* b, F32 a) {
*r = mul(*r, a);
*g = mul(*g, a);
*b = mul(*b, a);
}
// ~~~~ Program::eval() and co. ~~~~ //

View File

@ -489,6 +489,9 @@ namespace skvm {
Color unpack_8888(I32 rgba);
Color unpack_565 (I32 bgr ); // bottom 16 bits
void premul(F32* r, F32* g, F32* b, F32 a);
void unpremul(F32* r, F32* g, F32* b, F32 a);
void dump(SkWStream* = nullptr) const;
uint32_t hash() const;

View File

@ -145,8 +145,6 @@ namespace {
case kBGRA_8888_SkColorType: break;
}
if (params.alphaType == kUnpremul_SkAlphaType) { *ok = false; }
if (!skvm::BlendModeSupported(params.blendMode)) {
*ok = false;
}
@ -279,10 +277,11 @@ namespace {
// opaque, ignoring any math that disagrees. So anything involving force_opaque is
// optional, and sometimes helps cut a small amount of work in these programs.
const bool force_opaque = true && params.alphaType == kOpaque_SkAlphaType;
if (force_opaque) { dst.a = splat(1.0f); }
// We'd need to premul dst after loading and unpremul before storing.
if (params.alphaType == kUnpremul_SkAlphaType) { SkUNREACHABLE; }
if (force_opaque) {
dst.a = splat(1.0f);
} else if (params.alphaType == kUnpremul_SkAlphaType) {
premul(&dst.r, &dst.g, &dst.b, dst.a);
}
src = skvm::BlendModeProgram(this, params.blendMode, src, dst);
@ -304,7 +303,11 @@ namespace {
assert_true(gte(src.a, splat(0.0f)));
assert_true(lte(src.a, splat(1.0f)));
}
if (force_opaque) { src.a = splat(1.0f); }
if (force_opaque) {
src.a = splat(1.0f);
} else if (params.alphaType == kUnpremul_SkAlphaType) {
unpremul(&src.r, &src.g, &src.b, src.a);
}
// Store back to the destination.
switch (params.colorType) {

View File

@ -663,11 +663,6 @@ bool SkImageShader::onProgram(skvm::Builder* p,
quality = state.quality();
tweak_quality_and_inv_matrix(&quality, &inv);
// TODO: and need to extend lifetime of this too once supporting steps.flags.mask() != 0.
SkColorSpaceXformSteps steps{pm.colorSpace(), pm.alphaType(),
dstCS, kPremul_SkAlphaType};
if (steps.flags.mask() != 0) { return false; }
if (quality != kNone_SkFilterQuality) { return false; }
// Apply matrix to convert dst coords to sample coords.
@ -765,10 +760,25 @@ bool SkImageShader::onProgram(skvm::Builder* p,
c.a = p->bit_cast(p->bit_and(mask, p->bit_cast(c.a)));
}
// This point corresponds roughly to when we're done with individual
// samples and resolve our final color. The distinction is moot while
// we only support kNone quality, which is one sample.
*r = c.r;
*g = c.g;
*b = c.b;
*a = c.a;
// Follow SkColorSpaceXformSteps to match shader output convention (dstCS, premul).
// TODO: may need to extend lifetime once doing actual transforms? maybe all in uniforms.
auto flags = SkColorSpaceXformSteps{pm.colorSpace(), pm.alphaType(),
dstCS, kPremul_SkAlphaType}.flags;
// TODO: once this all works, move it to SkColorSpaceXformSteps
if (flags.unpremul) { p->unpremul(r,g,b,*a); }
if (flags.linearize) { return false; }
if (flags.gamut_transform) { return false; }
if (flags.encode) { return false; }
if (flags.premul) { p->premul(r,g,b,*a); }
return true;
}