xref: /aosp_15_r20/frameworks/base/libs/hwui/effects/GainmapRenderer.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker  * Copyright (C) 2023 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker  *
4*d57664e9SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker  *
8*d57664e9SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker  *
10*d57664e9SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker  * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker  */
16*d57664e9SAndroid Build Coastguard Worker 
17*d57664e9SAndroid Build Coastguard Worker #include "GainmapRenderer.h"
18*d57664e9SAndroid Build Coastguard Worker 
19*d57664e9SAndroid Build Coastguard Worker #include <SkGainmapShader.h>
20*d57664e9SAndroid Build Coastguard Worker 
21*d57664e9SAndroid Build Coastguard Worker #include "Gainmap.h"
22*d57664e9SAndroid Build Coastguard Worker #include "Rect.h"
23*d57664e9SAndroid Build Coastguard Worker #include "utils/Trace.h"
24*d57664e9SAndroid Build Coastguard Worker 
25*d57664e9SAndroid Build Coastguard Worker #ifdef __ANDROID__
26*d57664e9SAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
27*d57664e9SAndroid Build Coastguard Worker #include "include/core/SkImage.h"
28*d57664e9SAndroid Build Coastguard Worker #include "include/core/SkShader.h"
29*d57664e9SAndroid Build Coastguard Worker #include "include/effects/SkRuntimeEffect.h"
30*d57664e9SAndroid Build Coastguard Worker #include "include/private/SkGainmapInfo.h"
31*d57664e9SAndroid Build Coastguard Worker #include "renderthread/CanvasContext.h"
32*d57664e9SAndroid Build Coastguard Worker #include "src/core/SkColorFilterPriv.h"
33*d57664e9SAndroid Build Coastguard Worker #include "src/core/SkImageInfoPriv.h"
34*d57664e9SAndroid Build Coastguard Worker #include "src/core/SkRuntimeEffectPriv.h"
35*d57664e9SAndroid Build Coastguard Worker 
36*d57664e9SAndroid Build Coastguard Worker #include <cmath>
37*d57664e9SAndroid Build Coastguard Worker #endif
38*d57664e9SAndroid Build Coastguard Worker 
39*d57664e9SAndroid Build Coastguard Worker namespace android::uirenderer {
40*d57664e9SAndroid Build Coastguard Worker 
41*d57664e9SAndroid Build Coastguard Worker using namespace renderthread;
42*d57664e9SAndroid Build Coastguard Worker 
getTargetHdrSdrRatio(const SkColorSpace * destColorspace)43*d57664e9SAndroid Build Coastguard Worker float getTargetHdrSdrRatio(const SkColorSpace* destColorspace) {
44*d57664e9SAndroid Build Coastguard Worker     // We should always have a known destination colorspace. If we don't we must be in some
45*d57664e9SAndroid Build Coastguard Worker     // legacy mode where we're lost and also definitely not going to HDR
46*d57664e9SAndroid Build Coastguard Worker     if (destColorspace == nullptr) {
47*d57664e9SAndroid Build Coastguard Worker         return 1.f;
48*d57664e9SAndroid Build Coastguard Worker     }
49*d57664e9SAndroid Build Coastguard Worker 
50*d57664e9SAndroid Build Coastguard Worker     constexpr float GenericSdrWhiteNits = 203.f;
51*d57664e9SAndroid Build Coastguard Worker     constexpr float maxPQLux = 10000.f;
52*d57664e9SAndroid Build Coastguard Worker     constexpr float maxHLGLux = 1000.f;
53*d57664e9SAndroid Build Coastguard Worker     skcms_TransferFunction destTF;
54*d57664e9SAndroid Build Coastguard Worker     destColorspace->transferFn(&destTF);
55*d57664e9SAndroid Build Coastguard Worker     if (skcms_TransferFunction_isPQish(&destTF)) {
56*d57664e9SAndroid Build Coastguard Worker         return maxPQLux / GenericSdrWhiteNits;
57*d57664e9SAndroid Build Coastguard Worker     } else if (skcms_TransferFunction_isHLGish(&destTF)) {
58*d57664e9SAndroid Build Coastguard Worker         return maxHLGLux / GenericSdrWhiteNits;
59*d57664e9SAndroid Build Coastguard Worker #ifdef __ANDROID__
60*d57664e9SAndroid Build Coastguard Worker     } else if (RenderThread::isCurrent()) {
61*d57664e9SAndroid Build Coastguard Worker         CanvasContext* context = CanvasContext::getActiveContext();
62*d57664e9SAndroid Build Coastguard Worker         return context ? context->targetSdrHdrRatio() : 1.f;
63*d57664e9SAndroid Build Coastguard Worker #endif
64*d57664e9SAndroid Build Coastguard Worker     }
65*d57664e9SAndroid Build Coastguard Worker     return 1.f;
66*d57664e9SAndroid Build Coastguard Worker }
67*d57664e9SAndroid Build Coastguard Worker 
DrawGainmapBitmap(SkCanvas * c,const sk_sp<const SkImage> & image,const SkRect & src,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint * paint,SkCanvas::SrcRectConstraint constraint,const sk_sp<const SkImage> & gainmapImage,const SkGainmapInfo & gainmapInfo)68*d57664e9SAndroid Build Coastguard Worker void DrawGainmapBitmap(SkCanvas* c, const sk_sp<const SkImage>& image, const SkRect& src,
69*d57664e9SAndroid Build Coastguard Worker                        const SkRect& dst, const SkSamplingOptions& sampling, const SkPaint* paint,
70*d57664e9SAndroid Build Coastguard Worker                        SkCanvas::SrcRectConstraint constraint,
71*d57664e9SAndroid Build Coastguard Worker                        const sk_sp<const SkImage>& gainmapImage, const SkGainmapInfo& gainmapInfo) {
72*d57664e9SAndroid Build Coastguard Worker     ATRACE_CALL();
73*d57664e9SAndroid Build Coastguard Worker #ifdef __ANDROID__
74*d57664e9SAndroid Build Coastguard Worker     auto destColorspace = c->imageInfo().refColorSpace();
75*d57664e9SAndroid Build Coastguard Worker     float targetSdrHdrRatio = getTargetHdrSdrRatio(destColorspace.get());
76*d57664e9SAndroid Build Coastguard Worker     const bool baseImageHdr = gainmapInfo.fBaseImageType == SkGainmapInfo::BaseImageType::kHDR;
77*d57664e9SAndroid Build Coastguard Worker     if (gainmapImage && ((baseImageHdr && targetSdrHdrRatio < gainmapInfo.fDisplayRatioHdr) ||
78*d57664e9SAndroid Build Coastguard Worker                          (!baseImageHdr && targetSdrHdrRatio > gainmapInfo.fDisplayRatioSdr))) {
79*d57664e9SAndroid Build Coastguard Worker         SkPaint gainmapPaint = *paint;
80*d57664e9SAndroid Build Coastguard Worker         float sX = gainmapImage->width() / (float)image->width();
81*d57664e9SAndroid Build Coastguard Worker         float sY = gainmapImage->height() / (float)image->height();
82*d57664e9SAndroid Build Coastguard Worker         SkRect gainmapSrc = src;
83*d57664e9SAndroid Build Coastguard Worker         // TODO: Tweak rounding?
84*d57664e9SAndroid Build Coastguard Worker         gainmapSrc.fLeft *= sX;
85*d57664e9SAndroid Build Coastguard Worker         gainmapSrc.fRight *= sX;
86*d57664e9SAndroid Build Coastguard Worker         gainmapSrc.fTop *= sY;
87*d57664e9SAndroid Build Coastguard Worker         gainmapSrc.fBottom *= sY;
88*d57664e9SAndroid Build Coastguard Worker         auto shader =
89*d57664e9SAndroid Build Coastguard Worker                 SkGainmapShader::Make(image, src, sampling, gainmapImage, gainmapSrc, sampling,
90*d57664e9SAndroid Build Coastguard Worker                                       gainmapInfo, dst, targetSdrHdrRatio, destColorspace);
91*d57664e9SAndroid Build Coastguard Worker         gainmapPaint.setShader(shader);
92*d57664e9SAndroid Build Coastguard Worker         c->drawRect(dst, gainmapPaint);
93*d57664e9SAndroid Build Coastguard Worker     } else
94*d57664e9SAndroid Build Coastguard Worker #endif
95*d57664e9SAndroid Build Coastguard Worker         c->drawImageRect(image.get(), src, dst, sampling, paint, constraint);
96*d57664e9SAndroid Build Coastguard Worker }
97*d57664e9SAndroid Build Coastguard Worker 
98*d57664e9SAndroid Build Coastguard Worker #ifdef __ANDROID__
99*d57664e9SAndroid Build Coastguard Worker 
100*d57664e9SAndroid Build Coastguard Worker static constexpr char gGainmapSKSL[] = R"SKSL(
101*d57664e9SAndroid Build Coastguard Worker     uniform shader linearBase;
102*d57664e9SAndroid Build Coastguard Worker     uniform shader base;
103*d57664e9SAndroid Build Coastguard Worker     uniform shader gainmap;
104*d57664e9SAndroid Build Coastguard Worker     uniform colorFilter workingSpaceToLinearSrgb;
105*d57664e9SAndroid Build Coastguard Worker     uniform half4 logRatioMin;
106*d57664e9SAndroid Build Coastguard Worker     uniform half4 logRatioMax;
107*d57664e9SAndroid Build Coastguard Worker     uniform half4 gainmapGamma;
108*d57664e9SAndroid Build Coastguard Worker     uniform half4 epsilonSdr;
109*d57664e9SAndroid Build Coastguard Worker     uniform half4 epsilonHdr;
110*d57664e9SAndroid Build Coastguard Worker     uniform half W;
111*d57664e9SAndroid Build Coastguard Worker     uniform int gainmapIsAlpha;
112*d57664e9SAndroid Build Coastguard Worker     uniform int gainmapIsRed;
113*d57664e9SAndroid Build Coastguard Worker     uniform int singleChannel;
114*d57664e9SAndroid Build Coastguard Worker     uniform int noGamma;
115*d57664e9SAndroid Build Coastguard Worker 
116*d57664e9SAndroid Build Coastguard Worker     half4 toDest(half4 working) {
117*d57664e9SAndroid Build Coastguard Worker         half4 ls = workingSpaceToLinearSrgb.eval(working);
118*d57664e9SAndroid Build Coastguard Worker         vec3 dest = fromLinearSrgb(ls.rgb);
119*d57664e9SAndroid Build Coastguard Worker         return half4(dest.r, dest.g, dest.b, ls.a);
120*d57664e9SAndroid Build Coastguard Worker     }
121*d57664e9SAndroid Build Coastguard Worker 
122*d57664e9SAndroid Build Coastguard Worker     half4 main(float2 coord) {
123*d57664e9SAndroid Build Coastguard Worker         if (W == 0.0) {
124*d57664e9SAndroid Build Coastguard Worker             return base.eval(coord);
125*d57664e9SAndroid Build Coastguard Worker         }
126*d57664e9SAndroid Build Coastguard Worker 
127*d57664e9SAndroid Build Coastguard Worker         half4 S = linearBase.eval(coord);
128*d57664e9SAndroid Build Coastguard Worker         half4 G = gainmap.eval(coord);
129*d57664e9SAndroid Build Coastguard Worker         if (gainmapIsAlpha == 1) {
130*d57664e9SAndroid Build Coastguard Worker             G = half4(G.a, G.a, G.a, 1.0);
131*d57664e9SAndroid Build Coastguard Worker         }
132*d57664e9SAndroid Build Coastguard Worker         if (gainmapIsRed == 1) {
133*d57664e9SAndroid Build Coastguard Worker             G = half4(G.r, G.r, G.r, 1.0);
134*d57664e9SAndroid Build Coastguard Worker         }
135*d57664e9SAndroid Build Coastguard Worker         if (singleChannel == 1) {
136*d57664e9SAndroid Build Coastguard Worker             half L;
137*d57664e9SAndroid Build Coastguard Worker             if (noGamma == 1) {
138*d57664e9SAndroid Build Coastguard Worker                 L = mix(logRatioMin.r, logRatioMax.r, G.r);
139*d57664e9SAndroid Build Coastguard Worker             } else {
140*d57664e9SAndroid Build Coastguard Worker                 L = mix(logRatioMin.r, logRatioMax.r, pow(G.r, gainmapGamma.r));
141*d57664e9SAndroid Build Coastguard Worker             }
142*d57664e9SAndroid Build Coastguard Worker             half3 H = (S.rgb + epsilonSdr.rgb) * exp(L * W) - epsilonHdr.rgb;
143*d57664e9SAndroid Build Coastguard Worker             return toDest(half4(H.r, H.g, H.b, S.a));
144*d57664e9SAndroid Build Coastguard Worker         } else {
145*d57664e9SAndroid Build Coastguard Worker             half3 L;
146*d57664e9SAndroid Build Coastguard Worker             if (noGamma == 1) {
147*d57664e9SAndroid Build Coastguard Worker                 L = mix(logRatioMin.rgb, logRatioMax.rgb, G.rgb);
148*d57664e9SAndroid Build Coastguard Worker             } else {
149*d57664e9SAndroid Build Coastguard Worker                 L = mix(logRatioMin.rgb, logRatioMax.rgb, pow(G.rgb, gainmapGamma.rgb));
150*d57664e9SAndroid Build Coastguard Worker             }
151*d57664e9SAndroid Build Coastguard Worker             half3 H = (S.rgb + epsilonSdr.rgb) * exp(L * W) - epsilonHdr.rgb;
152*d57664e9SAndroid Build Coastguard Worker             return toDest(half4(H.r, H.g, H.b, S.a));
153*d57664e9SAndroid Build Coastguard Worker         }
154*d57664e9SAndroid Build Coastguard Worker     }
155*d57664e9SAndroid Build Coastguard Worker )SKSL";
156*d57664e9SAndroid Build Coastguard Worker 
gainmap_apply_effect()157*d57664e9SAndroid Build Coastguard Worker static sk_sp<SkRuntimeEffect> gainmap_apply_effect() {
158*d57664e9SAndroid Build Coastguard Worker     static const SkRuntimeEffect* effect = []() -> SkRuntimeEffect* {
159*d57664e9SAndroid Build Coastguard Worker         auto buildResult = SkRuntimeEffect::MakeForShader(SkString(gGainmapSKSL), {});
160*d57664e9SAndroid Build Coastguard Worker         if (buildResult.effect) {
161*d57664e9SAndroid Build Coastguard Worker             return buildResult.effect.release();
162*d57664e9SAndroid Build Coastguard Worker         } else {
163*d57664e9SAndroid Build Coastguard Worker             LOG_ALWAYS_FATAL("Failed to build gainmap shader: %s", buildResult.errorText.c_str());
164*d57664e9SAndroid Build Coastguard Worker         }
165*d57664e9SAndroid Build Coastguard Worker     }();
166*d57664e9SAndroid Build Coastguard Worker     SkASSERT(effect);
167*d57664e9SAndroid Build Coastguard Worker     return sk_ref_sp(effect);
168*d57664e9SAndroid Build Coastguard Worker }
169*d57664e9SAndroid Build Coastguard Worker 
all_channels_equal(const SkColor4f & c)170*d57664e9SAndroid Build Coastguard Worker static bool all_channels_equal(const SkColor4f& c) {
171*d57664e9SAndroid Build Coastguard Worker     return c.fR == c.fG && c.fR == c.fB;
172*d57664e9SAndroid Build Coastguard Worker }
173*d57664e9SAndroid Build Coastguard Worker 
174*d57664e9SAndroid Build Coastguard Worker class DeferredGainmapShader {
175*d57664e9SAndroid Build Coastguard Worker private:
176*d57664e9SAndroid Build Coastguard Worker     sk_sp<SkRuntimeEffect> mShader{gainmap_apply_effect()};
177*d57664e9SAndroid Build Coastguard Worker     SkRuntimeShaderBuilder mBuilder{mShader};
178*d57664e9SAndroid Build Coastguard Worker     SkGainmapInfo mGainmapInfo;
179*d57664e9SAndroid Build Coastguard Worker     std::mutex mUniformGuard;
180*d57664e9SAndroid Build Coastguard Worker 
setupChildren(const sk_sp<const SkImage> & baseImage,const sk_sp<const SkImage> & gainmapImage,SkTileMode tileModeX,SkTileMode tileModeY,const SkSamplingOptions & samplingOptions)181*d57664e9SAndroid Build Coastguard Worker     void setupChildren(const sk_sp<const SkImage>& baseImage,
182*d57664e9SAndroid Build Coastguard Worker                        const sk_sp<const SkImage>& gainmapImage, SkTileMode tileModeX,
183*d57664e9SAndroid Build Coastguard Worker                        SkTileMode tileModeY, const SkSamplingOptions& samplingOptions) {
184*d57664e9SAndroid Build Coastguard Worker         sk_sp<SkColorSpace> baseColorSpace =
185*d57664e9SAndroid Build Coastguard Worker                 baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB();
186*d57664e9SAndroid Build Coastguard Worker 
187*d57664e9SAndroid Build Coastguard Worker         // Determine the color space in which the gainmap math is to be applied.
188*d57664e9SAndroid Build Coastguard Worker         sk_sp<SkColorSpace> gainmapMathColorSpace =
189*d57664e9SAndroid Build Coastguard Worker                 mGainmapInfo.fGainmapMathColorSpace
190*d57664e9SAndroid Build Coastguard Worker                         ? mGainmapInfo.fGainmapMathColorSpace->makeLinearGamma()
191*d57664e9SAndroid Build Coastguard Worker                         : baseColorSpace->makeLinearGamma();
192*d57664e9SAndroid Build Coastguard Worker 
193*d57664e9SAndroid Build Coastguard Worker         // Create a color filter to transform from the base image's color space to the color space
194*d57664e9SAndroid Build Coastguard Worker         // in which the gainmap is to be applied.
195*d57664e9SAndroid Build Coastguard Worker         auto colorXformSdrToGainmap =
196*d57664e9SAndroid Build Coastguard Worker                 SkColorFilterPriv::MakeColorSpaceXform(baseColorSpace, gainmapMathColorSpace);
197*d57664e9SAndroid Build Coastguard Worker 
198*d57664e9SAndroid Build Coastguard Worker         // The base image shader will convert into the color space in which the gainmap is applied.
199*d57664e9SAndroid Build Coastguard Worker         auto linearBaseImageShader = baseImage->makeRawShader(tileModeX, tileModeY, samplingOptions)
200*d57664e9SAndroid Build Coastguard Worker                                              ->makeWithColorFilter(colorXformSdrToGainmap);
201*d57664e9SAndroid Build Coastguard Worker 
202*d57664e9SAndroid Build Coastguard Worker         auto baseImageShader = baseImage->makeShader(tileModeX, tileModeY, samplingOptions);
203*d57664e9SAndroid Build Coastguard Worker 
204*d57664e9SAndroid Build Coastguard Worker         // The gainmap image shader will ignore any color space that the gainmap has.
205*d57664e9SAndroid Build Coastguard Worker         const SkMatrix gainmapRectToDstRect =
206*d57664e9SAndroid Build Coastguard Worker                 SkMatrix::RectToRect(SkRect::MakeWH(gainmapImage->width(), gainmapImage->height()),
207*d57664e9SAndroid Build Coastguard Worker                                      SkRect::MakeWH(baseImage->width(), baseImage->height()));
208*d57664e9SAndroid Build Coastguard Worker         auto gainmapImageShader = gainmapImage->makeRawShader(tileModeX, tileModeY, samplingOptions,
209*d57664e9SAndroid Build Coastguard Worker                                                               &gainmapRectToDstRect);
210*d57664e9SAndroid Build Coastguard Worker 
211*d57664e9SAndroid Build Coastguard Worker         // Create a color filter to transform from the color space in which the gainmap is applied
212*d57664e9SAndroid Build Coastguard Worker         // to the intermediate destination color space.
213*d57664e9SAndroid Build Coastguard Worker         auto colorXformGainmapToDst = SkColorFilterPriv::MakeColorSpaceXform(
214*d57664e9SAndroid Build Coastguard Worker                 gainmapMathColorSpace, SkColorSpace::MakeSRGBLinear());
215*d57664e9SAndroid Build Coastguard Worker 
216*d57664e9SAndroid Build Coastguard Worker         mBuilder.child("linearBase") = std::move(linearBaseImageShader);
217*d57664e9SAndroid Build Coastguard Worker         mBuilder.child("base") = std::move(baseImageShader);
218*d57664e9SAndroid Build Coastguard Worker         mBuilder.child("gainmap") = std::move(gainmapImageShader);
219*d57664e9SAndroid Build Coastguard Worker         mBuilder.child("workingSpaceToLinearSrgb") = std::move(colorXformGainmapToDst);
220*d57664e9SAndroid Build Coastguard Worker     }
221*d57664e9SAndroid Build Coastguard Worker 
setupGenericUniforms(const sk_sp<const SkImage> & gainmapImage,const SkGainmapInfo & gainmapInfo)222*d57664e9SAndroid Build Coastguard Worker     void setupGenericUniforms(const sk_sp<const SkImage>& gainmapImage,
223*d57664e9SAndroid Build Coastguard Worker                               const SkGainmapInfo& gainmapInfo) {
224*d57664e9SAndroid Build Coastguard Worker         const SkColor4f logRatioMin({std::log(gainmapInfo.fGainmapRatioMin.fR),
225*d57664e9SAndroid Build Coastguard Worker                                      std::log(gainmapInfo.fGainmapRatioMin.fG),
226*d57664e9SAndroid Build Coastguard Worker                                      std::log(gainmapInfo.fGainmapRatioMin.fB), 1.f});
227*d57664e9SAndroid Build Coastguard Worker         const SkColor4f logRatioMax({std::log(gainmapInfo.fGainmapRatioMax.fR),
228*d57664e9SAndroid Build Coastguard Worker                                      std::log(gainmapInfo.fGainmapRatioMax.fG),
229*d57664e9SAndroid Build Coastguard Worker                                      std::log(gainmapInfo.fGainmapRatioMax.fB), 1.f});
230*d57664e9SAndroid Build Coastguard Worker         const int noGamma = gainmapInfo.fGainmapGamma.fR == 1.f &&
231*d57664e9SAndroid Build Coastguard Worker                             gainmapInfo.fGainmapGamma.fG == 1.f &&
232*d57664e9SAndroid Build Coastguard Worker                             gainmapInfo.fGainmapGamma.fB == 1.f;
233*d57664e9SAndroid Build Coastguard Worker         const uint32_t colorTypeFlags = SkColorTypeChannelFlags(gainmapImage->colorType());
234*d57664e9SAndroid Build Coastguard Worker         const int gainmapIsAlpha = colorTypeFlags == kAlpha_SkColorChannelFlag;
235*d57664e9SAndroid Build Coastguard Worker         const int gainmapIsRed = colorTypeFlags == kRed_SkColorChannelFlag;
236*d57664e9SAndroid Build Coastguard Worker         const int singleChannel = all_channels_equal(gainmapInfo.fGainmapGamma) &&
237*d57664e9SAndroid Build Coastguard Worker                                   all_channels_equal(gainmapInfo.fGainmapRatioMin) &&
238*d57664e9SAndroid Build Coastguard Worker                                   all_channels_equal(gainmapInfo.fGainmapRatioMax) &&
239*d57664e9SAndroid Build Coastguard Worker                                   (colorTypeFlags == kGray_SkColorChannelFlag ||
240*d57664e9SAndroid Build Coastguard Worker                                    colorTypeFlags == kAlpha_SkColorChannelFlag ||
241*d57664e9SAndroid Build Coastguard Worker                                    colorTypeFlags == kRed_SkColorChannelFlag);
242*d57664e9SAndroid Build Coastguard Worker         mBuilder.uniform("logRatioMin") = logRatioMin;
243*d57664e9SAndroid Build Coastguard Worker         mBuilder.uniform("logRatioMax") = logRatioMax;
244*d57664e9SAndroid Build Coastguard Worker         mBuilder.uniform("gainmapGamma") = gainmapInfo.fGainmapGamma;
245*d57664e9SAndroid Build Coastguard Worker         mBuilder.uniform("epsilonSdr") = gainmapInfo.fEpsilonSdr;
246*d57664e9SAndroid Build Coastguard Worker         mBuilder.uniform("epsilonHdr") = gainmapInfo.fEpsilonHdr;
247*d57664e9SAndroid Build Coastguard Worker         mBuilder.uniform("noGamma") = noGamma;
248*d57664e9SAndroid Build Coastguard Worker         mBuilder.uniform("singleChannel") = singleChannel;
249*d57664e9SAndroid Build Coastguard Worker         mBuilder.uniform("gainmapIsAlpha") = gainmapIsAlpha;
250*d57664e9SAndroid Build Coastguard Worker         mBuilder.uniform("gainmapIsRed") = gainmapIsRed;
251*d57664e9SAndroid Build Coastguard Worker     }
252*d57664e9SAndroid Build Coastguard Worker 
build(float targetHdrSdrRatio)253*d57664e9SAndroid Build Coastguard Worker     sk_sp<const SkData> build(float targetHdrSdrRatio) {
254*d57664e9SAndroid Build Coastguard Worker         sk_sp<const SkData> uniforms;
255*d57664e9SAndroid Build Coastguard Worker         {
256*d57664e9SAndroid Build Coastguard Worker             // If we are called concurrently from multiple threads, we need to guard the call
257*d57664e9SAndroid Build Coastguard Worker             // to writableUniforms() which mutates mUniform. This is otherwise safe because
258*d57664e9SAndroid Build Coastguard Worker             // writeableUniforms() will make a copy if it's not unique before mutating
259*d57664e9SAndroid Build Coastguard Worker             // This can happen if a BitmapShader is used on multiple canvas', such as a
260*d57664e9SAndroid Build Coastguard Worker             // software + hardware canvas, which is otherwise valid as SkShader is "immutable"
261*d57664e9SAndroid Build Coastguard Worker             std::lock_guard _lock(mUniformGuard);
262*d57664e9SAndroid Build Coastguard Worker             // Compute the weight parameter that will be used to blend between the images.
263*d57664e9SAndroid Build Coastguard Worker             float W = 0.f;
264*d57664e9SAndroid Build Coastguard Worker             if (targetHdrSdrRatio > mGainmapInfo.fDisplayRatioSdr) {
265*d57664e9SAndroid Build Coastguard Worker                 if (targetHdrSdrRatio < mGainmapInfo.fDisplayRatioHdr) {
266*d57664e9SAndroid Build Coastguard Worker                     W = (std::log(targetHdrSdrRatio) -
267*d57664e9SAndroid Build Coastguard Worker                          std::log(mGainmapInfo.fDisplayRatioSdr)) /
268*d57664e9SAndroid Build Coastguard Worker                         (std::log(mGainmapInfo.fDisplayRatioHdr) -
269*d57664e9SAndroid Build Coastguard Worker                          std::log(mGainmapInfo.fDisplayRatioSdr));
270*d57664e9SAndroid Build Coastguard Worker                 } else {
271*d57664e9SAndroid Build Coastguard Worker                     W = 1.f;
272*d57664e9SAndroid Build Coastguard Worker                 }
273*d57664e9SAndroid Build Coastguard Worker             }
274*d57664e9SAndroid Build Coastguard Worker 
275*d57664e9SAndroid Build Coastguard Worker             if (mGainmapInfo.fBaseImageType == SkGainmapInfo::BaseImageType::kHDR) {
276*d57664e9SAndroid Build Coastguard Worker                 W -= 1.f;
277*d57664e9SAndroid Build Coastguard Worker             }
278*d57664e9SAndroid Build Coastguard Worker             mBuilder.uniform("W") = W;
279*d57664e9SAndroid Build Coastguard Worker             uniforms = mBuilder.uniforms();
280*d57664e9SAndroid Build Coastguard Worker         }
281*d57664e9SAndroid Build Coastguard Worker         return uniforms;
282*d57664e9SAndroid Build Coastguard Worker     }
283*d57664e9SAndroid Build Coastguard Worker 
284*d57664e9SAndroid Build Coastguard Worker public:
DeferredGainmapShader(const sk_sp<const SkImage> & image,const sk_sp<const SkImage> & gainmapImage,const SkGainmapInfo & gainmapInfo,SkTileMode tileModeX,SkTileMode tileModeY,const SkSamplingOptions & sampling)285*d57664e9SAndroid Build Coastguard Worker     explicit DeferredGainmapShader(const sk_sp<const SkImage>& image,
286*d57664e9SAndroid Build Coastguard Worker                                    const sk_sp<const SkImage>& gainmapImage,
287*d57664e9SAndroid Build Coastguard Worker                                    const SkGainmapInfo& gainmapInfo, SkTileMode tileModeX,
288*d57664e9SAndroid Build Coastguard Worker                                    SkTileMode tileModeY, const SkSamplingOptions& sampling) {
289*d57664e9SAndroid Build Coastguard Worker         mGainmapInfo = gainmapInfo;
290*d57664e9SAndroid Build Coastguard Worker         setupChildren(image, gainmapImage, tileModeX, tileModeY, sampling);
291*d57664e9SAndroid Build Coastguard Worker         setupGenericUniforms(gainmapImage, gainmapInfo);
292*d57664e9SAndroid Build Coastguard Worker     }
293*d57664e9SAndroid Build Coastguard Worker 
Make(const sk_sp<const SkImage> & image,const sk_sp<const SkImage> & gainmapImage,const SkGainmapInfo & gainmapInfo,SkTileMode tileModeX,SkTileMode tileModeY,const SkSamplingOptions & sampling)294*d57664e9SAndroid Build Coastguard Worker     static sk_sp<SkShader> Make(const sk_sp<const SkImage>& image,
295*d57664e9SAndroid Build Coastguard Worker                                 const sk_sp<const SkImage>& gainmapImage,
296*d57664e9SAndroid Build Coastguard Worker                                 const SkGainmapInfo& gainmapInfo, SkTileMode tileModeX,
297*d57664e9SAndroid Build Coastguard Worker                                 SkTileMode tileModeY, const SkSamplingOptions& sampling) {
298*d57664e9SAndroid Build Coastguard Worker         auto deferredHandler = std::make_shared<DeferredGainmapShader>(
299*d57664e9SAndroid Build Coastguard Worker                 image, gainmapImage, gainmapInfo, tileModeX, tileModeY, sampling);
300*d57664e9SAndroid Build Coastguard Worker         auto callback =
301*d57664e9SAndroid Build Coastguard Worker                 [deferredHandler](const SkRuntimeEffectPriv::UniformsCallbackContext& renderContext)
302*d57664e9SAndroid Build Coastguard Worker                 -> sk_sp<const SkData> {
303*d57664e9SAndroid Build Coastguard Worker             return deferredHandler->build(getTargetHdrSdrRatio(renderContext.fDstColorSpace));
304*d57664e9SAndroid Build Coastguard Worker         };
305*d57664e9SAndroid Build Coastguard Worker         return SkRuntimeEffectPriv::MakeDeferredShader(deferredHandler->mShader.get(), callback,
306*d57664e9SAndroid Build Coastguard Worker                                                        deferredHandler->mBuilder.children());
307*d57664e9SAndroid Build Coastguard Worker     }
308*d57664e9SAndroid Build Coastguard Worker };
309*d57664e9SAndroid Build Coastguard Worker 
MakeGainmapShader(const sk_sp<const SkImage> & image,const sk_sp<const SkImage> & gainmapImage,const SkGainmapInfo & gainmapInfo,SkTileMode tileModeX,SkTileMode tileModeY,const SkSamplingOptions & sampling)310*d57664e9SAndroid Build Coastguard Worker sk_sp<SkShader> MakeGainmapShader(const sk_sp<const SkImage>& image,
311*d57664e9SAndroid Build Coastguard Worker                                   const sk_sp<const SkImage>& gainmapImage,
312*d57664e9SAndroid Build Coastguard Worker                                   const SkGainmapInfo& gainmapInfo, SkTileMode tileModeX,
313*d57664e9SAndroid Build Coastguard Worker                                   SkTileMode tileModeY, const SkSamplingOptions& sampling) {
314*d57664e9SAndroid Build Coastguard Worker     return DeferredGainmapShader::Make(image, gainmapImage, gainmapInfo, tileModeX, tileModeY,
315*d57664e9SAndroid Build Coastguard Worker                                        sampling);
316*d57664e9SAndroid Build Coastguard Worker }
317*d57664e9SAndroid Build Coastguard Worker 
318*d57664e9SAndroid Build Coastguard Worker #else  // __ANDROID__
319*d57664e9SAndroid Build Coastguard Worker 
MakeGainmapShader(const sk_sp<const SkImage> & image,const sk_sp<const SkImage> & gainmapImage,const SkGainmapInfo & gainmapInfo,SkTileMode tileModeX,SkTileMode tileModeY,const SkSamplingOptions & sampling)320*d57664e9SAndroid Build Coastguard Worker sk_sp<SkShader> MakeGainmapShader(const sk_sp<const SkImage>& image,
321*d57664e9SAndroid Build Coastguard Worker                                   const sk_sp<const SkImage>& gainmapImage,
322*d57664e9SAndroid Build Coastguard Worker                                   const SkGainmapInfo& gainmapInfo, SkTileMode tileModeX,
323*d57664e9SAndroid Build Coastguard Worker                                   SkTileMode tileModeY, const SkSamplingOptions& sampling) {
324*d57664e9SAndroid Build Coastguard Worker         return nullptr;
325*d57664e9SAndroid Build Coastguard Worker }
326*d57664e9SAndroid Build Coastguard Worker 
327*d57664e9SAndroid Build Coastguard Worker #endif  // __ANDROID__
328*d57664e9SAndroid Build Coastguard Worker 
329*d57664e9SAndroid Build Coastguard Worker }  // namespace android::uirenderer
330