blob: dd750acc3c4f66424519a6143d3a3746facb641e [file] [log] [blame]
/*
* Copyright 2020 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
@header {
#include "include/effects/SkHighContrastFilter.h"
}
in fragmentProcessor? inputFP;
in uniform half contrastMod;
layout(key) in bool hasContrast;
layout(key) in bool grayscale;
// invertBrightness and invertLightness are intended to be mutually exclusive.
layout(key) in bool invertBrightness;
layout(key) in bool invertLightness;
layout(key) in bool linearize;
half HSLToRGB(half p, half q, half t) {
if (t < 0)
t += 1;
if (t > 1)
t -= 1;
return (t < 1/6.) ? p + (q - p) * 6 * t :
(t < 3/6.) ? q :
(t < 4/6.) ? p + (q - p) * (2/3. - t) * 6 :
p;
}
void main() {
const half3 SK_ITU_BT709_LUM_COEFF = half3(0.2126, 0.7152, 0.0722);
half4 inColor = sample(inputFP);
half4 color = unpremul(inColor);
@if (linearize) {
// We approximate the transfer function as gamma 2.0.
color.rgb = color.rgb * color.rgb;
}
@if (grayscale) {
color = half4(half3(dot(color.rgb, SK_ITU_BT709_LUM_COEFF)), 0);
}
@if (invertBrightness) {
color = half4(1) - color;
}
@if (invertLightness) {
half fmax = max(color.r, max(color.g, color.b));
half fmin = min(color.r, min(color.g, color.b));
half l = fmax + fmin;
half h;
half s;
if (fmax == fmin) {
h = 0;
s = 0;
} else {
half d = fmax - fmin;
s = (l > 1) ? d / (2 - l) : d / l;
// We'd like to just write "if (color.r == fmax) { ... }". On many GPUs, running the
// angle_d3d9_es2 config, that failed. It seems that max(x, y) is not necessarily equal
// to either x or y. Tried several ways to fix it, but this was the only reasonable fix.
if (color.r >= color.g && color.r >= color.b) {
h = (color.g - color.b) / d + (color.g < color.b ? 6 : 0);
} else if (color.g >= color.b) {
h = (color.b - color.r) / d + 2;
} else {
h = (color.r - color.g) / d + 4;
}
h *= 1/6.;
}
l = 1.0 + (l * -0.5);
if (s == 0) {
color = half4(l, l, l, 0);
} else {
half q = l < 0.5 ? l * (1 + s) : l + s - l * s;
half p = 2 * l - q;
color.r = HSLToRGB(p, q, h + 1/3.);
color.g = HSLToRGB(p, q, h);
color.b = HSLToRGB(p, q, h - 1/3.);
}
}
@if (hasContrast) {
half off = (-0.5 * contrastMod) + 0.5;
color = contrastMod * color + off;
}
color = saturate(color);
@if (linearize) {
color.rgb = sqrt(color.rgb);
}
sk_OutColor = color.rgb1 * inColor.a;
}
@optimizationFlags {
kNone_OptimizationFlags
}
@make {
static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> inputFP,
const SkHighContrastConfig& config,
bool linearize) {
float contrastMod = (1 + config.fContrast) / (1 - config.fContrast);
return std::unique_ptr<GrFragmentProcessor>(new GrHighContrastFilterEffect(
std::move(inputFP),
contrastMod,
contrastMod != 1.0,
config.fGrayscale,
config.fInvertStyle == SkHighContrastConfig::InvertStyle::kInvertBrightness,
config.fInvertStyle == SkHighContrastConfig::InvertStyle::kInvertLightness,
linearize));
}
}
@test(d) {
using InvertStyle = SkHighContrastConfig::InvertStyle;
SkHighContrastConfig config{/*grayscale=*/d->fRandom->nextBool(),
InvertStyle(d->fRandom->nextRangeU(0, int(InvertStyle::kLast))),
/*contrast=*/d->fRandom->nextF()};
return GrHighContrastFilterEffect::Make(d->inputFP(), config,
/*linearize=*/d->fRandom->nextBool());
}