blob: acd8fd2670a6ff8a6db777c4ece74a432c087728 [file] [log] [blame]
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkColorSpace.h"
#include "include/core/SkString.h"
#include "include/effects/SkHighContrastFilter.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/private/SkTPin.h"
#include "src/core/SkColorFilterPriv.h"
#include "src/core/SkRuntimeEffectPriv.h"
sk_sp<SkColorFilter> SkHighContrastFilter::Make(const SkHighContrastConfig& config) {
#ifdef SK_ENABLE_SKSL
if (!config.isValid()) {
return nullptr;
}
struct Uniforms { float grayscale, invertStyle, contrast; };
static constexpr char kHighContrastFilterCode[] =
"uniform half grayscale, invertStyle, contrast;"
// TODO(skia:13540): Investigate using $rgb_to_hsl from sksl_shared instead.
"half3 rgb_to_hsl(half3 c) {"
" half mx = max(max(c.r,c.g),c.b),"
" mn = min(min(c.r,c.g),c.b),"
" d = mx-mn, "
" invd = 1.0 / d, "
" g_lt_b = c.g < c.b ? 6.0 : 0.0;"
// We'd prefer to write these tests like `mx == c.r`, but on some GPUs max(x,y) is
// not always equal to either x or y. So we use long form, c.r >= c.g && c.r >= c.b.
" half h = (1/6.0) * (mx == mn ? 0.0 :"
" /*mx==c.r*/ c.r >= c.g && c.r >= c.b ? invd * (c.g - c.b) + g_lt_b :"
" /*mx==c.g*/ c.g >= c.b ? invd * (c.b - c.r) + 2.0 "
" /*mx==c.b*/ : invd * (c.r - c.g) + 4.0);"
" half sum = mx+mn,"
" l = sum * 0.5,"
" s = mx == mn ? 0.0"
" : d / (l > 0.5 ? 2.0 - sum : sum);"
" return half3(h,s,l);"
"}"
"half4 main(half4 inColor) {"
" half4 c = inColor;" // 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);"
"}";
static const SkRuntimeEffect* effect = SkMakeCachedRuntimeEffect(
SkRuntimeEffect::MakeForColorFilter,
SkString(kHighContrastFilterCode)
).release();
SkASSERT(effect);
// 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?
float c = SkTPin(config.fContrast,
-1.0f + FLT_EPSILON,
+1.0f - FLT_EPSILON);
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),
};
skcms_TransferFunction linear = SkNamedTransferFn::kLinear;
SkAlphaType unpremul = kUnpremul_SkAlphaType;
return SkColorFilterPriv::WithWorkingFormat(
effect->makeColorFilter(SkData::MakeWithCopy(&uniforms,sizeof(uniforms))),
&linear, nullptr/*use dst gamut*/, &unpremul);
#else
// TODO(skia:12197)
return nullptr;
#endif
}