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