/* * 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/effects/SkHighContrastFilter.h" #include "include/core/SkColorFilter.h" #include "include/core/SkRefCnt.h" #include "include/core/SkTypes.h" #ifdef SK_ENABLE_SKSL #include "include/core/SkAlphaType.h" #include "include/core/SkColorSpace.h" #include "include/core/SkData.h" #include "include/core/SkString.h" #include "include/effects/SkRuntimeEffect.h" #include "include/private/base/SkTPin.h" #include "modules/skcms/skcms.h" #include "src/core/SkColorFilterPriv.h" #include "src/core/SkRuntimeEffectPriv.h" #include sk_sp SkHighContrastFilter::Make(const SkHighContrastConfig& config) { 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 // SK_ENABLE_SKSL sk_sp SkHighContrastFilter::Make(const SkHighContrastConfig& config) { // TODO(skia:12197) return nullptr; } #endif