onProgram for HighContrastColorFilter
Expose sk_program_transfer_fn helper.
Change-Id: I871b77ecb6733d73c8f900f0bce9f4f3d29b26ec
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/279258
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
diff --git a/src/core/SkColorSpacePriv.h b/src/core/SkColorSpacePriv.h
index 2e8728c..bbd0d00 100644
--- a/src/core/SkColorSpacePriv.h
+++ b/src/core/SkColorSpacePriv.h
@@ -11,6 +11,7 @@
#include "include/core/SkColorSpace.h"
#include "include/private/SkFixed.h"
+#include "src/core/SkVM_fwd.h"
#define SkColorSpacePrintf(...)
@@ -100,6 +101,9 @@
return linearExp || linearFn;
}
+skvm::Color sk_program_transfer_fn(skvm::Builder*, skvm::Uniforms*,
+ const skcms_TransferFunction&, skvm::Color);
+
// Return raw pointers to commonly used SkColorSpaces.
// No need to ref/unref these, but if you do, do it in pairs.
SkColorSpace* sk_srgb_singleton();
diff --git a/src/core/SkColorSpaceXformSteps.cpp b/src/core/SkColorSpaceXformSteps.cpp
index 7bc23c1..67cb62d 100644
--- a/src/core/SkColorSpaceXformSteps.cpp
+++ b/src/core/SkColorSpaceXformSteps.cpp
@@ -156,8 +156,8 @@
if (flags.premul) { p->append(SkRasterPipeline::premul); }
}
-static skvm::Color apply_transfer_function(skvm::Builder* p, skvm::Uniforms* uniforms,
- const skcms_TransferFunction& tf, skvm::Color c) {
+skvm::Color sk_program_transfer_fn(skvm::Builder* p, skvm::Uniforms* uniforms,
+ const skcms_TransferFunction& tf, skvm::Color c) {
skvm::F32 G = p->uniformF(uniforms->pushF(tf.g)),
A = p->uniformF(uniforms->pushF(tf.a)),
B = p->uniformF(uniforms->pushF(tf.b)),
@@ -211,7 +211,7 @@
c = p->unpremul(c);
}
if (flags.linearize) {
- c = apply_transfer_function(p, uniforms, srcTF, c);
+ c = sk_program_transfer_fn(p, uniforms, srcTF, c);
}
if (flags.gamut_transform) {
skvm::F32 m[9];
@@ -224,7 +224,7 @@
c = {R, G, B, c.a};
}
if (flags.encode) {
- c = apply_transfer_function(p, uniforms, dstTFInv, c);
+ c = sk_program_transfer_fn(p, uniforms, dstTFInv, c);
}
if (flags.premul) {
c = p->premul(c);
diff --git a/src/core/SkVM.h b/src/core/SkVM.h
index 7564550..b45890d2 100644
--- a/src/core/SkVM.h
+++ b/src/core/SkVM.h
@@ -465,15 +465,18 @@
return approx_pow2(mul(splat(1.4426950408889634074f), x));
}
- F32 negate(F32 x) {
- return sub(splat(0.0f), x);
- }
+ F32 inv(F32 x) { return sub(splat(1.0f), x); }
+ F32 negate(F32 x) { return sub(splat(0.0f), x); }
+
F32 lerp(F32 lo, F32 hi, F32 t) {
return mad(sub(hi,lo), t, lo);
}
F32 clamp(F32 x, F32 lo, F32 hi) {
return max(lo, min(x, hi));
}
+ F32 clamp01(F32 x) {
+ return clamp(x, splat(0.0f), splat(1.0f));
+ }
F32 abs(F32 x) {
return bit_cast(bit_and(bit_cast(x),
splat(0x7fffffff)));
diff --git a/src/effects/SkHighContrastFilter.cpp b/src/effects/SkHighContrastFilter.cpp
index 5922cf5..03e547c 100644
--- a/src/effects/SkHighContrastFilter.cpp
+++ b/src/effects/SkHighContrastFilter.cpp
@@ -9,9 +9,11 @@
#include "include/effects/SkHighContrastFilter.h"
#include "include/private/SkColorData.h"
#include "src/core/SkArenaAlloc.h"
+#include "src/core/SkColorSpacePriv.h"
#include "src/core/SkEffectPriv.h"
#include "src/core/SkRasterPipeline.h"
#include "src/core/SkReadBuffer.h"
+#include "src/core/SkVM.h"
#include "src/core/SkWriteBuffer.h"
#if SK_SUPPORT_GPU
@@ -41,6 +43,8 @@
#endif
bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
+ skvm::Color onProgram(skvm::Builder*, skvm::Color, SkColorSpace*, skvm::Uniforms*,
+ SkArenaAlloc*) const override;
protected:
void flatten(SkWriteBuffer&) const override;
@@ -128,6 +132,108 @@
return true;
}
+namespace skvm {
+ struct HSLA { F32 h,s,l,a; };
+}
+
+static skvm::HSLA rgb_to_hsl(skvm::Builder* p, skvm::Color c) {
+ auto mx = p->max(p->max(c.r,c.g),c.b),
+ mn = p->min(p->min(c.r,c.g),c.b),
+ d = p->sub(mx,mn),
+ d_rcp = p->div(p->splat(1.0f),d),
+ g_lt_b = p->select(p->lt(c.g,c.b), p->splat(6.0f), p->splat(0.0f));
+
+ auto diffm = [&](auto a, auto b) { return p->mul(p->sub(a,b), d_rcp); };
+
+ auto h = p->mul(p->splat(1/6.0f),
+ p->select(p->eq(mx,mn), p->splat(0.0f),
+ p->select(p->eq(mx, c.r), p->add(diffm(c.g,c.b), g_lt_b),
+ p->select(p->eq(mx, c.g), p->add(diffm(c.b,c.r), p->splat(2.0f)),
+ p->add(diffm(c.r,c.g), p->splat(4.0f))))));
+
+ auto sum = p->add(mx,mn);
+ auto l = p->mul(sum, p->splat(0.5f));
+ auto s = p->select(p->eq(mx,mn), p->splat(0.0f),
+ p->div(d,
+ p->select(p->gt(l,p->splat(0.5f)), p->sub(p->splat(2.0f),sum),
+ sum)));
+ return {h, s, l, c.a};
+}
+
+static skvm::Color hsl_to_rgb(skvm::Builder* p, skvm::HSLA c) {
+ // See GrRGBToHSLFilterEffect.fp
+
+ auto h = c.h,
+ s = c.s,
+ l = c.l,
+ x = p->mul(p->sub(p->splat(1.0f), p->abs(p->sub(p->add(l,l),
+ p->splat(1.0f)))),
+ s);
+
+ auto hue_to_rgb = [&](auto hue) {
+ auto q = p->sub(p->abs(p->mad(p->fract(hue),p->splat(6.0f), p->splat(-3.0f))), p->splat(1.0f));
+ return p->mad(p->sub(p->clamp01(q), p->splat(0.5f)), x, l);
+ };
+
+ return {
+ hue_to_rgb(p->add(h, p->splat(0.0f/3.0f))),
+ hue_to_rgb(p->add(h, p->splat(2.0f/3.0f))),
+ hue_to_rgb(p->add(h, p->splat(1.0f/3.0f))),
+ c.a
+ };
+}
+
+skvm::Color SkHighContrast_Filter::onProgram(skvm::Builder* p, skvm::Color c, SkColorSpace* dstCS,
+ skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
+ c = p->unpremul(c);
+
+ // Linearize before applying high-contrast filter.
+ skcms_TransferFunction tf;
+ if (dstCS) {
+ dstCS->transferFn(&tf);
+ } else {
+ //sk_srgb_singleton()->transferFn(&tf);
+ tf = {2,1, 0,0,0,0,0};
+ }
+ c = sk_program_transfer_fn(p, uniforms, tf, c);
+
+ if (fConfig.fGrayscale) {
+ skvm::F32 gray = p->mad(p->splat(SK_LUM_COEFF_R),c.r,
+ p->mad(p->splat(SK_LUM_COEFF_G),c.g,
+ p->mul(p->splat(SK_LUM_COEFF_B),c.b)));
+ c = {gray, gray, gray, c.a};
+ }
+
+ if (fConfig.fInvertStyle == InvertStyle::kInvertBrightness) {
+ c = {p->inv(c.r), p->inv(c.g), p->inv(c.b), c.a};
+ } else if (fConfig.fInvertStyle == InvertStyle::kInvertLightness) {
+ skvm::HSLA hsla = rgb_to_hsl(p, c);
+ hsla.l = p->inv(hsla.l);
+ c = hsl_to_rgb(p, hsla);
+ }
+
+ if (fConfig.fContrast != 0.0) {
+ const float m = (1 + fConfig.fContrast) / (1 - fConfig.fContrast);
+ const float b = (-0.5f * m + 0.5f);
+ skvm::F32 M = p->uniformF(uniforms->pushF(m));
+ skvm::F32 B = p->uniformF(uniforms->pushF(b));
+ c = {p->mad(M,c.r, B), p->mad(M,c.g, B), p->mad(M,c.b, B), c.a};
+ }
+
+ c = {p->clamp01(c.r), p->clamp01(c.g), p->clamp01(c.b), c.a};
+
+ // Re-encode back from linear.
+ if (dstCS) {
+ dstCS->invTransferFn(&tf);
+ } else {
+ //sk_srgb_singleton()->invTransferFn(&tf);
+ tf = {0.5f,1, 0,0,0,0,0};
+ }
+ c = sk_program_transfer_fn(p, uniforms, tf, c);
+
+ return p->premul(c);
+}
+
void SkHighContrast_Filter::flatten(SkWriteBuffer& buffer) const {
buffer.writeBool(fConfig.fGrayscale);
buffer.writeInt(static_cast<int>(fConfig.fInvertStyle));