1 /* 2 * Copyright 2021 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 #pragma once 18 19 #include <math/mat4.h> 20 #include <tonemap/tonemap.h> 21 #include <ui/GraphicTypes.h> 22 #include <cstddef> 23 24 namespace android::shaders { 25 26 /** 27 * Arguments for creating an effect that applies color transformations in linear XYZ space. 28 * A linear effect is decomposed into the following steps when operating on an image: 29 * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended 30 * relative display brightness of the scene in nits for each RGB channel 31 * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display 32 * luminance. 33 * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone 34 * mapping to display SDR content alongside HDR content, or any number of subjective transformations 35 * 4. Transformation matrix from linear XYZ back to linear RGB brightness. 36 * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to 37 * output RGB colors. 38 * 39 * For further reading, consult the recommendation in ITU-R BT.2390-4: 40 * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf 41 * 42 * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is 43 * intended to be the output surface. However, Skia does not support complex tone mapping such as 44 * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied 45 * to the source colors. so that the tone mapping process is only applied once by this effect. Tone 46 * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions) 47 * alongside other content, whereby maximum input luminance is mapped to maximum output luminance 48 * and intermediate values are interpolated. 49 */ 50 struct LinearEffect { 51 // Input dataspace of the source colors. 52 const ui::Dataspace inputDataspace = ui::Dataspace::SRGB; 53 54 // Working dataspace for the output surface, for conversion from linear space. 55 const ui::Dataspace outputDataspace = ui::Dataspace::SRGB; 56 57 // Sets whether alpha premultiplication must be undone. 58 // This is required if the source colors use premultiplied alpha and is not opaque. 59 const bool undoPremultipliedAlpha = false; 60 61 // "Fake" dataspace of the source colors. This is used for applying an EOTF to compute linear 62 // RGB. This is used when Skia is expected to color manage the input image based on the 63 // dataspace of the provided source image and destination surface. SkRuntimeEffects use the 64 // destination color space as the working color space. RenderEngine deliberately sets the color 65 // space for input images and destination surfaces to be the same whenever LinearEffects are 66 // expected to be used so that color-management is controlled by RenderEngine, but other users 67 // of a LinearEffect may not be able to control the color space of the images and surfaces. So 68 // fakeInputDataspace is used to essentially masquerade the input dataspace to be the output 69 // dataspace for correct conversion to linear colors. 70 ui::Dataspace fakeInputDataspace = ui::Dataspace::UNKNOWN; 71 }; 72 73 static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) { 74 return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace && 75 lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha && 76 lhs.fakeInputDataspace == rhs.fakeInputDataspace; 77 } 78 79 struct LinearEffectHasher { 80 // Inspired by art/runtime/class_linker.cc 81 // Also this is what boost:hash_combine does HashCombineLinearEffectHasher82 static size_t HashCombine(size_t seed, size_t val) { 83 return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2)); 84 } operatorLinearEffectHasher85 size_t operator()(const LinearEffect& le) const { 86 size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace); 87 result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace)); 88 result = HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha)); 89 return HashCombine(result, std::hash<ui::Dataspace>{}(le.fakeInputDataspace)); 90 } 91 }; 92 93 // Generates a shader string that applies color transforms in linear space. 94 // Typical use-cases supported: 95 // 1. Apply tone-mapping 96 // 2. Apply color transform matrices in linear space 97 std::string buildLinearEffectSkSL(const LinearEffect& linearEffect); 98 99 // Generates a list of uniforms to set on the LinearEffect shader above. 100 std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms( 101 const LinearEffect& linearEffect, const mat4& colorTransform, float maxDisplayLuminance, 102 float currentDisplayLuminanceNits, float maxLuminance, AHardwareBuffer* buffer = nullptr, 103 aidl::android::hardware::graphics::composer3::RenderIntent renderIntent = 104 aidl::android::hardware::graphics::composer3::RenderIntent::TONE_MAP_COLORIMETRIC); 105 106 } // namespace android::shaders 107