xref: /aosp_15_r20/frameworks/base/libs/hwui/Tonemapper.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker  * Copyright 2022 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 "Tonemapper.h"
18*d57664e9SAndroid Build Coastguard Worker 
19*d57664e9SAndroid Build Coastguard Worker #include <SkRuntimeEffect.h>
20*d57664e9SAndroid Build Coastguard Worker #include <log/log.h>
21*d57664e9SAndroid Build Coastguard Worker // libshaders only exists on Android devices
22*d57664e9SAndroid Build Coastguard Worker #ifdef __ANDROID__
23*d57664e9SAndroid Build Coastguard Worker #include <renderthread/CanvasContext.h>
24*d57664e9SAndroid Build Coastguard Worker #include <shaders/shaders.h>
25*d57664e9SAndroid Build Coastguard Worker #endif
26*d57664e9SAndroid Build Coastguard Worker 
27*d57664e9SAndroid Build Coastguard Worker #include "utils/Color.h"
28*d57664e9SAndroid Build Coastguard Worker 
29*d57664e9SAndroid Build Coastguard Worker namespace android::uirenderer {
30*d57664e9SAndroid Build Coastguard Worker 
31*d57664e9SAndroid Build Coastguard Worker namespace {
32*d57664e9SAndroid Build Coastguard Worker 
33*d57664e9SAndroid Build Coastguard Worker // custom tonemapping only exists on Android devices
34*d57664e9SAndroid Build Coastguard Worker #ifdef __ANDROID__
35*d57664e9SAndroid Build Coastguard Worker class ColorFilterRuntimeEffectBuilder : public SkRuntimeEffectBuilder {
36*d57664e9SAndroid Build Coastguard Worker public:
ColorFilterRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect)37*d57664e9SAndroid Build Coastguard Worker     explicit ColorFilterRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect)
38*d57664e9SAndroid Build Coastguard Worker             : SkRuntimeEffectBuilder(std::move(effect)) {}
39*d57664e9SAndroid Build Coastguard Worker 
makeColorFilter()40*d57664e9SAndroid Build Coastguard Worker     sk_sp<SkColorFilter> makeColorFilter() {
41*d57664e9SAndroid Build Coastguard Worker         return this->effect()->makeColorFilter(this->uniforms());
42*d57664e9SAndroid Build Coastguard Worker     }
43*d57664e9SAndroid Build Coastguard Worker };
44*d57664e9SAndroid Build Coastguard Worker 
createLinearEffectColorFilter(const shaders::LinearEffect & linearEffect,float maxDisplayLuminance,float currentDisplayLuminanceNits,float maxLuminance)45*d57664e9SAndroid Build Coastguard Worker static sk_sp<SkColorFilter> createLinearEffectColorFilter(const shaders::LinearEffect& linearEffect,
46*d57664e9SAndroid Build Coastguard Worker                                                           float maxDisplayLuminance,
47*d57664e9SAndroid Build Coastguard Worker                                                           float currentDisplayLuminanceNits,
48*d57664e9SAndroid Build Coastguard Worker                                                           float maxLuminance) {
49*d57664e9SAndroid Build Coastguard Worker     auto shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
50*d57664e9SAndroid Build Coastguard Worker     auto [runtimeEffect, error] = SkRuntimeEffect::MakeForColorFilter(std::move(shaderString));
51*d57664e9SAndroid Build Coastguard Worker     if (!runtimeEffect) {
52*d57664e9SAndroid Build Coastguard Worker         LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
53*d57664e9SAndroid Build Coastguard Worker     }
54*d57664e9SAndroid Build Coastguard Worker 
55*d57664e9SAndroid Build Coastguard Worker     ColorFilterRuntimeEffectBuilder effectBuilder(std::move(runtimeEffect));
56*d57664e9SAndroid Build Coastguard Worker 
57*d57664e9SAndroid Build Coastguard Worker     auto colorTransform = android::mat4();
58*d57664e9SAndroid Build Coastguard Worker     const auto* context = renderthread::CanvasContext::getActiveContext();
59*d57664e9SAndroid Build Coastguard Worker     if (context) {
60*d57664e9SAndroid Build Coastguard Worker         const auto ratio = context->targetSdrHdrRatio();
61*d57664e9SAndroid Build Coastguard Worker         if (ratio > 1.0f) {
62*d57664e9SAndroid Build Coastguard Worker             colorTransform = android::mat4::scale(vec4(ratio, ratio, ratio, 1.f));
63*d57664e9SAndroid Build Coastguard Worker         }
64*d57664e9SAndroid Build Coastguard Worker     }
65*d57664e9SAndroid Build Coastguard Worker 
66*d57664e9SAndroid Build Coastguard Worker     const auto uniforms =
67*d57664e9SAndroid Build Coastguard Worker             shaders::buildLinearEffectUniforms(linearEffect, colorTransform, maxDisplayLuminance,
68*d57664e9SAndroid Build Coastguard Worker                                                currentDisplayLuminanceNits, maxLuminance);
69*d57664e9SAndroid Build Coastguard Worker 
70*d57664e9SAndroid Build Coastguard Worker     for (const auto& uniform : uniforms) {
71*d57664e9SAndroid Build Coastguard Worker         effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
72*d57664e9SAndroid Build Coastguard Worker     }
73*d57664e9SAndroid Build Coastguard Worker 
74*d57664e9SAndroid Build Coastguard Worker     return effectBuilder.makeColorFilter();
75*d57664e9SAndroid Build Coastguard Worker }
76*d57664e9SAndroid Build Coastguard Worker 
extractTransfer(ui::Dataspace dataspace)77*d57664e9SAndroid Build Coastguard Worker static ui::Dataspace extractTransfer(ui::Dataspace dataspace) {
78*d57664e9SAndroid Build Coastguard Worker     return static_cast<ui::Dataspace>(dataspace & HAL_DATASPACE_TRANSFER_MASK);
79*d57664e9SAndroid Build Coastguard Worker }
80*d57664e9SAndroid Build Coastguard Worker 
isHdrDataspace(ui::Dataspace dataspace)81*d57664e9SAndroid Build Coastguard Worker static bool isHdrDataspace(ui::Dataspace dataspace) {
82*d57664e9SAndroid Build Coastguard Worker     const auto transfer = extractTransfer(dataspace);
83*d57664e9SAndroid Build Coastguard Worker 
84*d57664e9SAndroid Build Coastguard Worker     return transfer == ui::Dataspace::TRANSFER_ST2084 || transfer == ui::Dataspace::TRANSFER_HLG;
85*d57664e9SAndroid Build Coastguard Worker }
86*d57664e9SAndroid Build Coastguard Worker 
getDataspace(const SkImageInfo & image)87*d57664e9SAndroid Build Coastguard Worker static ui::Dataspace getDataspace(const SkImageInfo& image) {
88*d57664e9SAndroid Build Coastguard Worker     return static_cast<ui::Dataspace>(
89*d57664e9SAndroid Build Coastguard Worker             ColorSpaceToADataSpace(image.colorSpace(), image.colorType()));
90*d57664e9SAndroid Build Coastguard Worker }
91*d57664e9SAndroid Build Coastguard Worker #endif
92*d57664e9SAndroid Build Coastguard Worker 
93*d57664e9SAndroid Build Coastguard Worker }  // namespace
94*d57664e9SAndroid Build Coastguard Worker 
95*d57664e9SAndroid Build Coastguard Worker // Given a source and destination image info, and the max content luminance, generate a tonemaping
96*d57664e9SAndroid Build Coastguard Worker // shader and tag it on the supplied paint.
tonemapPaint(const SkImageInfo & source,const SkImageInfo & destination,float maxLuminanceNits,SkPaint & paint)97*d57664e9SAndroid Build Coastguard Worker void tonemapPaint(const SkImageInfo& source, const SkImageInfo& destination, float maxLuminanceNits,
98*d57664e9SAndroid Build Coastguard Worker                   SkPaint& paint) {
99*d57664e9SAndroid Build Coastguard Worker // custom tonemapping only exists on Android devices
100*d57664e9SAndroid Build Coastguard Worker #ifdef __ANDROID__
101*d57664e9SAndroid Build Coastguard Worker     const auto sourceDataspace = getDataspace(source);
102*d57664e9SAndroid Build Coastguard Worker     const auto destinationDataspace = getDataspace(destination);
103*d57664e9SAndroid Build Coastguard Worker 
104*d57664e9SAndroid Build Coastguard Worker     if (extractTransfer(sourceDataspace) != extractTransfer(destinationDataspace) &&
105*d57664e9SAndroid Build Coastguard Worker         (isHdrDataspace(sourceDataspace) || isHdrDataspace(destinationDataspace))) {
106*d57664e9SAndroid Build Coastguard Worker         const auto effect = shaders::LinearEffect{
107*d57664e9SAndroid Build Coastguard Worker                 .inputDataspace = sourceDataspace,
108*d57664e9SAndroid Build Coastguard Worker                 .outputDataspace = destinationDataspace,
109*d57664e9SAndroid Build Coastguard Worker                 .undoPremultipliedAlpha = source.alphaType() == kPremul_SkAlphaType,
110*d57664e9SAndroid Build Coastguard Worker                 .type = shaders::LinearEffect::SkSLType::ColorFilter};
111*d57664e9SAndroid Build Coastguard Worker         constexpr float kMaxDisplayBrightnessNits = 1000.f;
112*d57664e9SAndroid Build Coastguard Worker         constexpr float kCurrentDisplayBrightnessNits = 500.f;
113*d57664e9SAndroid Build Coastguard Worker         sk_sp<SkColorFilter> colorFilter = createLinearEffectColorFilter(
114*d57664e9SAndroid Build Coastguard Worker                 effect, kMaxDisplayBrightnessNits, kCurrentDisplayBrightnessNits, maxLuminanceNits);
115*d57664e9SAndroid Build Coastguard Worker 
116*d57664e9SAndroid Build Coastguard Worker         if (paint.getColorFilter()) {
117*d57664e9SAndroid Build Coastguard Worker             paint.setColorFilter(SkColorFilters::Compose(paint.refColorFilter(), colorFilter));
118*d57664e9SAndroid Build Coastguard Worker         } else {
119*d57664e9SAndroid Build Coastguard Worker             paint.setColorFilter(colorFilter);
120*d57664e9SAndroid Build Coastguard Worker         }
121*d57664e9SAndroid Build Coastguard Worker     }
122*d57664e9SAndroid Build Coastguard Worker #else
123*d57664e9SAndroid Build Coastguard Worker     return;
124*d57664e9SAndroid Build Coastguard Worker #endif
125*d57664e9SAndroid Build Coastguard Worker }
126*d57664e9SAndroid Build Coastguard Worker 
127*d57664e9SAndroid Build Coastguard Worker }  // namespace android::uirenderer
128