1 /*
2 * Copyright 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "Tonemapper.h"
18
19 #include <SkRuntimeEffect.h>
20 #include <log/log.h>
21 // libshaders only exists on Android devices
22 #ifdef __ANDROID__
23 #include <shaders/shaders.h>
24 #endif
25
26 #include "utils/Color.h"
27
28 namespace android::uirenderer {
29
30 namespace {
31
32 // custom tonemapping only exists on Android devices
33 #ifdef __ANDROID__
34 class ColorFilterRuntimeEffectBuilder : public SkRuntimeEffectBuilder {
35 public:
ColorFilterRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect)36 explicit ColorFilterRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect)
37 : SkRuntimeEffectBuilder(std::move(effect)) {}
38
makeColorFilter()39 sk_sp<SkColorFilter> makeColorFilter() {
40 return this->effect()->makeColorFilter(this->uniforms());
41 }
42 };
43
createLinearEffectColorFilter(const shaders::LinearEffect & linearEffect,float maxDisplayLuminance,float currentDisplayLuminanceNits,float maxLuminance)44 static sk_sp<SkColorFilter> createLinearEffectColorFilter(const shaders::LinearEffect& linearEffect,
45 float maxDisplayLuminance,
46 float currentDisplayLuminanceNits,
47 float maxLuminance) {
48 auto shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
49 auto [runtimeEffect, error] = SkRuntimeEffect::MakeForColorFilter(std::move(shaderString));
50 if (!runtimeEffect) {
51 LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
52 }
53
54 ColorFilterRuntimeEffectBuilder effectBuilder(std::move(runtimeEffect));
55
56 const auto uniforms =
57 shaders::buildLinearEffectUniforms(linearEffect, android::mat4(), maxDisplayLuminance,
58 currentDisplayLuminanceNits, maxLuminance);
59
60 for (const auto& uniform : uniforms) {
61 effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
62 }
63
64 return effectBuilder.makeColorFilter();
65 }
66
extractTransfer(ui::Dataspace dataspace)67 static ui::Dataspace extractTransfer(ui::Dataspace dataspace) {
68 return static_cast<ui::Dataspace>(dataspace & HAL_DATASPACE_TRANSFER_MASK);
69 }
70
isHdrDataspace(ui::Dataspace dataspace)71 static bool isHdrDataspace(ui::Dataspace dataspace) {
72 const auto transfer = extractTransfer(dataspace);
73
74 return transfer == ui::Dataspace::TRANSFER_ST2084 || transfer == ui::Dataspace::TRANSFER_HLG;
75 }
76
getDataspace(const SkImageInfo & image)77 static ui::Dataspace getDataspace(const SkImageInfo& image) {
78 return static_cast<ui::Dataspace>(
79 ColorSpaceToADataSpace(image.colorSpace(), image.colorType()));
80 }
81 #endif
82
83 } // namespace
84
85 // Given a source and destination image info, and the max content luminance, generate a tonemaping
86 // shader and tag it on the supplied paint.
tonemapPaint(const SkImageInfo & source,const SkImageInfo & destination,float maxLuminanceNits,SkPaint & paint)87 void tonemapPaint(const SkImageInfo& source, const SkImageInfo& destination, float maxLuminanceNits,
88 SkPaint& paint) {
89 // custom tonemapping only exists on Android devices
90 #ifdef __ANDROID__
91 const auto sourceDataspace = getDataspace(source);
92 const auto destinationDataspace = getDataspace(destination);
93
94 if (extractTransfer(sourceDataspace) != extractTransfer(destinationDataspace) &&
95 (isHdrDataspace(sourceDataspace) || isHdrDataspace(destinationDataspace))) {
96 const auto effect = shaders::LinearEffect{
97 .inputDataspace = sourceDataspace,
98 .outputDataspace = destinationDataspace,
99 .undoPremultipliedAlpha = source.alphaType() == kPremul_SkAlphaType,
100 .type = shaders::LinearEffect::SkSLType::ColorFilter};
101 constexpr float kMaxDisplayBrightnessNits = 1000.f;
102 constexpr float kCurrentDisplayBrightnessNits = 500.f;
103 sk_sp<SkColorFilter> colorFilter = createLinearEffectColorFilter(
104 effect, kMaxDisplayBrightnessNits, kCurrentDisplayBrightnessNits, maxLuminanceNits);
105
106 if (paint.getColorFilter()) {
107 paint.setColorFilter(SkColorFilters::Compose(paint.refColorFilter(), colorFilter));
108 } else {
109 paint.setColorFilter(colorFilter);
110 }
111 }
112 #else
113 return;
114 #endif
115 }
116
117 } // namespace android::uirenderer
118