diff options
Diffstat (limited to 'gfx/skia/skia/src/shaders/SkGainmapShader.cpp')
-rw-r--r-- | gfx/skia/skia/src/shaders/SkGainmapShader.cpp | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/shaders/SkGainmapShader.cpp b/gfx/skia/skia/src/shaders/SkGainmapShader.cpp new file mode 100644 index 0000000000..1654709c2c --- /dev/null +++ b/gfx/skia/skia/src/shaders/SkGainmapShader.cpp @@ -0,0 +1,167 @@ +/* + * Copyright 2023 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "include/private/SkGainmapShader.h" + +#include "include/core/SkColorSpace.h" +#include "include/core/SkImage.h" +#include "include/core/SkShader.h" +#include "include/effects/SkRuntimeEffect.h" +#include "include/private/SkGainmapInfo.h" +#include "src/core/SkColorFilterPriv.h" +#include "src/core/SkImageInfoPriv.h" + +#ifdef SK_ENABLE_SKSL +static constexpr char gGainmapSKSL[] = + "uniform shader base;" + "uniform shader gainmap;" + "uniform half4 logRatioMin;" + "uniform half4 logRatioMax;" + "uniform half4 gainmapGamma;" + "uniform half4 epsilonSdr;" + "uniform half4 epsilonHdr;" + "uniform half W;" + "uniform int gainmapIsAlpha;" + "uniform int gainmapIsRed;" + "uniform int singleChannel;" + "uniform int noGamma;" + "" + "half4 main(float2 coord) {" + " half4 S = base.eval(coord);" + " half4 G = gainmap.eval(coord);" + " if (gainmapIsAlpha == 1) {" + " G = half4(G.a, G.a, G.a, 1.0);" + " }" + " if (gainmapIsRed == 1) {" + " G = half4(G.r, G.r, G.r, 1.0);" + " }" + " if (singleChannel == 1) {" + " half L;" + " if (noGamma == 1) {" + " L = mix(logRatioMin.r, logRatioMax.r, G.r);" + " } else {" + " L = mix(logRatioMin.r, logRatioMax.r, pow(G.r, gainmapGamma.r));" + " }" + " half3 H = (S.rgb + epsilonSdr.rgb) * exp(L * W) - epsilonHdr.rgb;" + " return half4(H.r, H.g, H.b, S.a);" + " } else {" + " half3 L;" + " if (noGamma == 1) {" + " L = mix(logRatioMin.rgb, logRatioMax.rgb, G.rgb);" + " } else {" + " L = mix(logRatioMin.rgb, logRatioMax.rgb, pow(G.rgb, gainmapGamma.rgb));" + " }" + " half3 H = (S.rgb + epsilonSdr.rgb) * exp(L * W) - epsilonHdr.rgb;" + " return half4(H.r, H.g, H.b, S.a);" + " }" + "}"; + +static sk_sp<SkRuntimeEffect> gainmap_apply_effect() { + static const SkRuntimeEffect* effect = + SkRuntimeEffect::MakeForShader(SkString(gGainmapSKSL), {}).effect.release(); + SkASSERT(effect); + return sk_ref_sp(effect); +} + +static bool all_channels_equal(const SkColor4f& c) { + return c.fR == c.fG && c.fR == c.fB; +} +#endif // SK_ENABLE_SKSL + +sk_sp<SkShader> SkGainmapShader::Make(const sk_sp<const SkImage>& baseImage, + const SkRect& baseRect, + const SkSamplingOptions& baseSamplingOptions, + const sk_sp<const SkImage>& gainmapImage, + const SkRect& gainmapRect, + const SkSamplingOptions& gainmapSamplingOptions, + const SkGainmapInfo& gainmapInfo, + const SkRect& dstRect, + float dstHdrRatio, + sk_sp<SkColorSpace> dstColorSpace) { +#ifdef SK_ENABLE_SKSL + sk_sp<SkColorSpace> baseColorSpace = + baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB(); + + // Determine the color space in which the gainmap math is to be applied. + sk_sp<SkColorSpace> gainmapMathColorSpace = baseColorSpace->makeLinearGamma(); + if (!dstColorSpace) { + dstColorSpace = SkColorSpace::MakeSRGB(); + } + + // Create a color filter to transform from the base image's color space to the color space in + // which the gainmap is to be applied. + auto colorXformSdrToGainmap = + SkColorFilterPriv::MakeColorSpaceXform(baseColorSpace, gainmapMathColorSpace); + + // Create a color filter to transform from the color space in which the gainmap is applied to + // the destination color space. + auto colorXformGainmapToDst = + SkColorFilterPriv::MakeColorSpaceXform(gainmapMathColorSpace, dstColorSpace); + + // The base image shader will convert into the color space in which the gainmap is applied. + const SkMatrix baseRectToDstRect = SkMatrix::RectToRect(baseRect, dstRect); + auto baseImageShader = baseImage->makeRawShader(baseSamplingOptions, &baseRectToDstRect) + ->makeWithColorFilter(colorXformSdrToGainmap); + + // The gainmap image shader will ignore any color space that the gainmap has. + const SkMatrix gainmapRectToDstRect = SkMatrix::RectToRect(gainmapRect, dstRect); + auto gainmapImageShader = + gainmapImage->makeRawShader(gainmapSamplingOptions, &gainmapRectToDstRect); + + // Create the shader to apply the gainmap. + sk_sp<SkShader> gainmapMathShader; + { + SkRuntimeShaderBuilder builder(gainmap_apply_effect()); + const SkColor4f logRatioMin({sk_float_log(gainmapInfo.fGainmapRatioMin.fR), + sk_float_log(gainmapInfo.fGainmapRatioMin.fG), + sk_float_log(gainmapInfo.fGainmapRatioMin.fB), + 1.f}); + const SkColor4f logRatioMax({sk_float_log(gainmapInfo.fGainmapRatioMax.fR), + sk_float_log(gainmapInfo.fGainmapRatioMax.fG), + sk_float_log(gainmapInfo.fGainmapRatioMax.fB), + 1.f}); + const float Wunclamped = + (sk_float_log(dstHdrRatio) - sk_float_log(gainmapInfo.fDisplayRatioSdr)) / + (sk_float_log(gainmapInfo.fDisplayRatioHdr) - + sk_float_log(gainmapInfo.fDisplayRatioSdr)); + const float W = std::max(std::min(Wunclamped, 1.f), 0.f); + const int noGamma = + gainmapInfo.fGainmapGamma.fR == 1.f && + gainmapInfo.fGainmapGamma.fG == 1.f && + gainmapInfo.fGainmapGamma.fB == 1.f; + const uint32_t colorTypeFlags = SkColorTypeChannelFlags(gainmapImage->colorType()); + const int gainmapIsAlpha = colorTypeFlags == kAlpha_SkColorChannelFlag; + const int gainmapIsRed = colorTypeFlags == kRed_SkColorChannelFlag; + const int singleChannel = all_channels_equal(gainmapInfo.fGainmapGamma) && + all_channels_equal(gainmapInfo.fGainmapRatioMin) && + all_channels_equal(gainmapInfo.fGainmapRatioMax) && + (colorTypeFlags == kGray_SkColorChannelFlag || + colorTypeFlags == kAlpha_SkColorChannelFlag || + colorTypeFlags == kRed_SkColorChannelFlag); + builder.child("base") = baseImageShader; + builder.child("gainmap") = gainmapImageShader; + builder.uniform("logRatioMin") = logRatioMin; + builder.uniform("logRatioMax") = logRatioMax; + builder.uniform("gainmapGamma") = gainmapInfo.fGainmapGamma; + builder.uniform("epsilonSdr") = gainmapInfo.fEpsilonSdr; + builder.uniform("epsilonHdr") = gainmapInfo.fEpsilonHdr; + builder.uniform("noGamma") = noGamma; + builder.uniform("singleChannel") = singleChannel; + builder.uniform("gainmapIsAlpha") = gainmapIsAlpha; + builder.uniform("gainmapIsRed") = gainmapIsRed; + builder.uniform("W") = W; + gainmapMathShader = builder.makeShader(); + SkASSERT(gainmapMathShader); + } + + // Return a shader that will apply the gainmap and then convert to the destination color space. + return gainmapMathShader->makeWithColorFilter(colorXformGainmapToDst); +#else + // This shader is currently only implemented using SkSL. + return nullptr; +#endif +} |