refactor high-contrast filter

Rewrite as one cacheable runtime effect with uniforms paralleling
SkHighContrastConfig.  This is mostly for style, in the sense of
readability but also how I'd recommend writing these effects generally:
one program body controlled by uniforms.  Callers needing specialization
can write their own effect (or use our TBD uniform->constant facility).

Test isAlphaUnchanged(), and make it so by not saturating c.a.

MaliT880 was producing seriously bad results until I switched to mix().

Change-Id: Ia4cfef25fe26643f6832a5fa0843b28dafc5b284
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/372295
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
This commit is contained in:
Mike Klein 2021-02-19 05:30:45 -06:00
parent 63f75fc1b6
commit 4d68c244a7
2 changed files with 46 additions and 44 deletions

View File

@ -11,57 +11,58 @@
#include "include/private/SkTPin.h"
#include "src/core/SkRuntimeEffectPriv.h"
sk_sp<SkColorFilter> SkHighContrastFilter::Make(const SkHighContrastConfig& userConfig) {
if (!userConfig.isValid()) {
sk_sp<SkColorFilter> SkHighContrastFilter::Make(const SkHighContrastConfig& config) {
if (!config.isValid()) {
return nullptr;
}
struct Uniforms { float grayscale, invertStyle, contrast; };
static SkRuntimeEffect* effect = []{
SkString code{R"(
uniform shader input;
uniform half grayscale, invertStyle, contrast;
)"};
code += kRGB_to_HSL_sksl;
code += kHSL_to_RGB_sksl;
code += R"(
half4 main() {
half4 c = sample(input); // linear unpremul RGBA in dst gamut.
if (grayscale == 1) {
c.rgb = dot(half3(0.2126, 0.7152, 0.0722), c.rgb).rrr;
}
if (invertStyle == 1/*brightness*/) {
c.rgb = 1 - c.rgb;
} else if (invertStyle == 2/*lightness*/) {
c.rgb = rgb_to_hsl(c.rgb);
c.b = 1 - c.b;
c.rgb = hsl_to_rgb(c.rgb);
}
c.rgb = mix(half3(0.5), c.rgb, contrast);
return half4(saturate(c.rgb), c.a);
}
)";
auto [effect, err] = SkRuntimeEffect::Make(code);
if (!err.isEmpty()) {
SkDebugf("%s\n%s\n", code.c_str(), err.c_str());
}
SkASSERT(effect && err.isEmpty());
return effect.release();
}();
// A contrast setting of exactly +1 would divide by zero (1+c)/(1-c), so pull in to +1-ε.
// I'm not exactly sure why we've historically pinned -1 up to -1+ε, maybe just symmetry?
SkHighContrastConfig config = userConfig;
config.fContrast = SkTPin(config.fContrast,
-1.0f + FLT_EPSILON,
+1.0f - FLT_EPSILON);
float c = SkTPin(config.fContrast,
-1.0f + FLT_EPSILON,
+1.0f - FLT_EPSILON);
struct { float M; } uniforms;
SkString code{
"uniform shader input;"
"uniform half M;"
Uniforms uniforms = {
config.fGrayscale ? 1.0f : 0.0f,
(float)config.fInvertStyle, // 0.0f for none, 1.0f for brightness, 2.0f for lightness
(1+c)/(1-c),
};
code += kRGB_to_HSL_sksl;
code += kHSL_to_RGB_sksl;
code += "half4 main() {";
if (true) {
code += "half4 c = sample(input);"; // c is linear unpremul RGBA in the dst gamut.
}
if (config.fGrayscale) {
code += "c.rgb = dot(half3(0.2126, 0.7152, 0.0722), c.rgb).rrr;";
}
if (config.fInvertStyle == SkHighContrastConfig::InvertStyle::kInvertBrightness) {
code += "c.rgb = 1 - c.rgb;";
}
if (config.fInvertStyle == SkHighContrastConfig::InvertStyle::kInvertLightness) {
code += "c.rgb = rgb_to_hsl(c.rgb);";
code += "c.b = 1 - c.b;";
code += "c.rgb = hsl_to_rgb(c.rgb);";
}
if (float c = config.fContrast) {
uniforms.M = (1+c)/(1-c);
code += "c.rgb = (c.rgb - 0.5)*M + 0.5;";
}
if (true) {
code += "return saturate(c);";
}
code += "}";
auto [effect, err] = SkRuntimeEffect::Make(code);
if (!err.isEmpty()) {
SkDebugf("%s\n%s\n", code.c_str(), err.c_str());
}
SkASSERT(effect && err.isEmpty());
sk_sp<SkColorFilter> input = nullptr;
skcms_TransferFunction linear = SkNamedTransferFn::kLinear;
SkAlphaType unpremul = kUnpremul_SkAlphaType;

View File

@ -45,10 +45,11 @@ DEF_TEST(HighContrastFilter_FilterImage, reporter) {
}
}
DEF_TEST(HighContrastFilter_SanityCheck, reporter) {
DEF_TEST(HighContrastFilter_SmokeTest, reporter) {
SkHighContrastConfig config;
config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
sk_sp<SkColorFilter> filter = SkHighContrastFilter::Make(config);
REPORTER_ASSERT(reporter, filter->isAlphaUnchanged());
SkColor white_inverted = filter->filterColor(SK_ColorWHITE);
REPORTER_ASSERT(reporter, white_inverted == SK_ColorBLACK);