• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/private/SkGainmapShader.h"
9 
10 #include "include/core/SkColorSpace.h"
11 #include "include/core/SkImage.h"
12 #include "include/core/SkShader.h"
13 #include "include/effects/SkRuntimeEffect.h"
14 #include "include/private/SkGainmapInfo.h"
15 #include "src/core/SkColorFilterPriv.h"
16 #include "src/core/SkImageInfoPriv.h"
17 
18 #ifdef SK_ENABLE_SKSL
19 static constexpr char gGainmapSKSL[] =
20         "uniform shader base;"
21         "uniform shader gainmap;"
22         "uniform half4 logRatioMin;"
23         "uniform half4 logRatioMax;"
24         "uniform half4 gainmapGamma;"
25         "uniform half4 epsilonSdr;"
26         "uniform half4 epsilonHdr;"
27         "uniform half W;"
28         "uniform int gainmapIsAlpha;"
29         "uniform int gainmapIsRed;"
30         "uniform int singleChannel;"
31         "uniform int noGamma;"
32         ""
33         "half4 main(float2 coord) {"
34         "    half4 S = base.eval(coord);"
35         "    half4 G = gainmap.eval(coord);"
36         "    if (gainmapIsAlpha == 1) {"
37         "        G = half4(G.a, G.a, G.a, 1.0);"
38         "    }"
39         "    if (gainmapIsRed == 1) {"
40         "        G = half4(G.r, G.r, G.r, 1.0);"
41         "    }"
42         "    if (singleChannel == 1) {"
43         "        half L;"
44         "        if (noGamma == 1) {"
45         "            L = mix(logRatioMin.r, logRatioMax.r, G.r);"
46         "        } else {"
47         "            L = mix(logRatioMin.r, logRatioMax.r, pow(G.r, gainmapGamma.r));"
48         "        }"
49         "        half3 H = (S.rgb + epsilonSdr.rgb) * exp(L * W) - epsilonHdr.rgb;"
50         "        return half4(H.r, H.g, H.b, S.a);"
51         "    } else {"
52         "        half3 L;"
53         "        if (noGamma == 1) {"
54         "            L = mix(logRatioMin.rgb, logRatioMax.rgb, G.rgb);"
55         "        } else {"
56         "            L = mix(logRatioMin.rgb, logRatioMax.rgb, pow(G.rgb, gainmapGamma.rgb));"
57         "        }"
58         "        half3 H = (S.rgb + epsilonSdr.rgb) * exp(L * W) - epsilonHdr.rgb;"
59         "        return half4(H.r, H.g, H.b, S.a);"
60         "    }"
61         "}";
62 
gainmap_apply_effect()63 static sk_sp<SkRuntimeEffect> gainmap_apply_effect() {
64     static const SkRuntimeEffect* effect =
65             SkRuntimeEffect::MakeForShader(SkString(gGainmapSKSL), {}).effect.release();
66     SkASSERT(effect);
67     return sk_ref_sp(effect);
68 }
69 
all_channels_equal(const SkColor4f & c)70 static bool all_channels_equal(const SkColor4f& c) {
71     return c.fR == c.fG && c.fR == c.fB;
72 }
73 #endif  // SK_ENABLE_SKSL
74 
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)75 sk_sp<SkShader> SkGainmapShader::Make(const sk_sp<const SkImage>& baseImage,
76                                       const SkRect& baseRect,
77                                       const SkSamplingOptions& baseSamplingOptions,
78                                       const sk_sp<const SkImage>& gainmapImage,
79                                       const SkRect& gainmapRect,
80                                       const SkSamplingOptions& gainmapSamplingOptions,
81                                       const SkGainmapInfo& gainmapInfo,
82                                       const SkRect& dstRect,
83                                       float dstHdrRatio,
84                                       sk_sp<SkColorSpace> dstColorSpace) {
85 #ifdef SK_ENABLE_SKSL
86     sk_sp<SkColorSpace> baseColorSpace =
87             baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB();
88 
89     // Determine the color space in which the gainmap math is to be applied.
90     sk_sp<SkColorSpace> gainmapMathColorSpace = baseColorSpace->makeLinearGamma();
91     if (!dstColorSpace) {
92         dstColorSpace = SkColorSpace::MakeSRGB();
93     }
94 
95     // Create a color filter to transform from the base image's color space to the color space in
96     // which the gainmap is to be applied.
97     auto colorXformSdrToGainmap =
98             SkColorFilterPriv::MakeColorSpaceXform(baseColorSpace, gainmapMathColorSpace);
99 
100     // Create a color filter to transform from the color space in which the gainmap is applied to
101     // the destination color space.
102     auto colorXformGainmapToDst =
103             SkColorFilterPriv::MakeColorSpaceXform(gainmapMathColorSpace, dstColorSpace);
104 
105     // The base image shader will convert into the color space in which the gainmap is applied.
106     const SkMatrix baseRectToDstRect = SkMatrix::RectToRect(baseRect, dstRect);
107     auto baseImageShader = baseImage->makeRawShader(baseSamplingOptions, &baseRectToDstRect)
108                                    ->makeWithColorFilter(colorXformSdrToGainmap);
109 
110     // The gainmap image shader will ignore any color space that the gainmap has.
111     const SkMatrix gainmapRectToDstRect = SkMatrix::RectToRect(gainmapRect, dstRect);
112     auto gainmapImageShader =
113             gainmapImage->makeRawShader(gainmapSamplingOptions, &gainmapRectToDstRect);
114 
115     // Create the shader to apply the gainmap.
116     sk_sp<SkShader> gainmapMathShader;
117     {
118         SkRuntimeShaderBuilder builder(gainmap_apply_effect());
119         const SkColor4f logRatioMin({sk_float_log(gainmapInfo.fGainmapRatioMin.fR),
120                                      sk_float_log(gainmapInfo.fGainmapRatioMin.fG),
121                                      sk_float_log(gainmapInfo.fGainmapRatioMin.fB),
122                                      1.f});
123         const SkColor4f logRatioMax({sk_float_log(gainmapInfo.fGainmapRatioMax.fR),
124                                      sk_float_log(gainmapInfo.fGainmapRatioMax.fG),
125                                      sk_float_log(gainmapInfo.fGainmapRatioMax.fB),
126                                      1.f});
127         const float Wunclamped =
128                 (sk_float_log(dstHdrRatio) - sk_float_log(gainmapInfo.fDisplayRatioSdr)) /
129                 (sk_float_log(gainmapInfo.fDisplayRatioHdr) -
130                  sk_float_log(gainmapInfo.fDisplayRatioSdr));
131         const float W = std::max(std::min(Wunclamped, 1.f), 0.f);
132         const int noGamma =
133             gainmapInfo.fGainmapGamma.fR == 1.f &&
134             gainmapInfo.fGainmapGamma.fG == 1.f &&
135             gainmapInfo.fGainmapGamma.fB == 1.f;
136         const uint32_t colorTypeFlags = SkColorTypeChannelFlags(gainmapImage->colorType());
137         const int gainmapIsAlpha = colorTypeFlags == kAlpha_SkColorChannelFlag;
138         const int gainmapIsRed = colorTypeFlags == kRed_SkColorChannelFlag;
139         const int singleChannel = all_channels_equal(gainmapInfo.fGainmapGamma) &&
140                                   all_channels_equal(gainmapInfo.fGainmapRatioMin) &&
141                                   all_channels_equal(gainmapInfo.fGainmapRatioMax) &&
142                                   (colorTypeFlags == kGray_SkColorChannelFlag ||
143                                    colorTypeFlags == kAlpha_SkColorChannelFlag ||
144                                    colorTypeFlags == kRed_SkColorChannelFlag);
145         builder.child("base") = baseImageShader;
146         builder.child("gainmap") = gainmapImageShader;
147         builder.uniform("logRatioMin") = logRatioMin;
148         builder.uniform("logRatioMax") = logRatioMax;
149         builder.uniform("gainmapGamma") = gainmapInfo.fGainmapGamma;
150         builder.uniform("epsilonSdr") = gainmapInfo.fEpsilonSdr;
151         builder.uniform("epsilonHdr") = gainmapInfo.fEpsilonHdr;
152         builder.uniform("noGamma") = noGamma;
153         builder.uniform("singleChannel") = singleChannel;
154         builder.uniform("gainmapIsAlpha") = gainmapIsAlpha;
155         builder.uniform("gainmapIsRed") = gainmapIsRed;
156         builder.uniform("W") = W;
157         gainmapMathShader = builder.makeShader();
158         SkASSERT(gainmapMathShader);
159     }
160 
161     // Return a shader that will apply the gainmap and then convert to the destination color space.
162     return gainmapMathShader->makeWithColorFilter(colorXformGainmapToDst);
163 #else
164     // This shader is currently only implemented using SkSL.
165     return nullptr;
166 #endif
167 }
168