Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5eacf74c authored by Alec Mouri's avatar Alec Mouri
Browse files

Fix luminance normalization for HLG

When skia color manages, 75% of the HLG signal maps to 1.0. But, this
does not correspond to 203 nits, this actually corresponds to about 265
nits, as the HLG OOTF is not yet applied.

We need to fix this by using a factor of 265 / 1000 to normalize the
signal to a [0-1] range for the LUT lookup. Otherwise, we end up dimming
the HLG signal when tonemapping.

Also, clean up some color management...
* Moving creating the linear working space creating the lut shader,
  rather than applying it to the input + output shader, as that doesn't
  actually apply the working space in the "right" place when executing
  the lut shader
* Remove the toLinearSrgb/fromLinearSrgb intrinsics, which were
  in-place, probably because we weren't actually applying the working
  space. This has the side-effect of looking up linear colors in the
  input image's gamut rather than the sRGB gamut, so we won't clip wide
  colors.

Bug: 418740883
Flag: EXEMPT bug fix
Test: VTS
Test: Photos playback
Test: Youtube playback
Change-Id: I1c8c231b3722ae33112010baeead7cfed94bcc30
parent cd7ca7fe
Loading
Loading
Loading
Loading
+30 −15
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <ui/ColorSpace.h>

#include "include/core/SkColorSpace.h"
#include "skia/ColorSpaces.h"

using aidl::android::hardware::graphics::composer3::LutProperties;

@@ -44,7 +45,7 @@ static const SkString kShader = SkString(R"(

    vec4 main(vec2 xy) {
        float4 rgba = image.eval(xy);
        float3 linear = toLinearSrgb(rgba.rgb) * normalizeScalar;
        float3 linear = rgba.rgb * normalizeScalar;
        if (dimension == 1) {
            // RGB
            if (key == 0) {
@@ -131,7 +132,7 @@ static const SkString kShader = SkString(R"(
                linear = mix(c0, c1, tx);
            }
        }
        return float4(fromLinearSrgb(linear), rgba.a);
        return float4(linear, rgba.a);
    })");

// same as shader::toColorSpace function
@@ -159,6 +160,27 @@ static ColorSpace toColorSpace(ui::Dataspace dataspace) {
    }
}

static float computeHlgScale() {
    static constexpr auto input = 0.7498773651;
    static constexpr auto a = 0.17883277;
    static constexpr auto b = 1 - 4 * a;
    const static auto c = 0.5 - a * std::log(4 * a);
    // Returns about 265 nits -- After the typical HLG OOTF this would map to 203 nits under ideal
    // conditions.
    return (exp((input - c) / a) + b) / 12;
}

static float computePqScale() {
    static constexpr auto input = 0.58068888104;
    static constexpr auto m1 = 0.1593017578125;
    static constexpr auto m2 = 78.84375;
    static constexpr auto c1 = 0.8359375;
    static constexpr auto c2 = 18.8515625;
    static constexpr auto c3 = 18.6875;
    // This should essetially return 203 nits
    return pow((pow(input, 1 / m2) - c1) / (c2 - c3 * pow(input, 1 / m2)), 1 / m1);
}

sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input,
                                             const std::vector<float>& buffers,
                                             const int32_t offset, const int32_t length,
@@ -226,10 +248,10 @@ sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input,
    float normalizeScalar = 1.0;
    switch (srcDataspace & HAL_DATASPACE_TRANSFER_MASK) {
        case HAL_DATASPACE_TRANSFER_HLG:
            normalizeScalar = 0.203;
            normalizeScalar = computeHlgScale();
            break;
        case HAL_DATASPACE_TRANSFER_ST2084:
            normalizeScalar = 0.0203;
            normalizeScalar = computePqScale();
            break;
        default:
            normalizeScalar = 1.0;
@@ -251,7 +273,10 @@ sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input,
    mBuilder->uniform("key") = uKey;
    mBuilder->uniform("dimension") = uDimension;
    mBuilder->uniform("normalizeScalar") = uNormalizeScalar;
    return mBuilder->makeShader();

    // de-gamma the image without changing the primaries
    return mBuilder->makeShader()->makeWithWorkingColorSpace(
            toSkColorSpace(srcDataspace)->makeLinearGamma());
}

sk_sp<SkShader> LutShader::lutShader(sk_sp<SkShader>& input,
@@ -265,14 +290,6 @@ sk_sp<SkShader> LutShader::lutShader(sk_sp<SkShader>& input,

    auto& fd = displayLuts->getLutFileDescriptor();
    if (fd.ok()) {
        // de-gamma the image without changing the primaries
        SkImage* baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr);
        sk_sp<SkColorSpace> baseColorSpace = baseImage && baseImage->colorSpace()
                ? baseImage->refColorSpace()
                : SkColorSpace::MakeSRGB();
        sk_sp<SkColorSpace> lutMathColorSpace = baseColorSpace->makeLinearGamma();
        input = input->makeWithWorkingColorSpace(lutMathColorSpace);

        auto& offsets = displayLuts->offsets;
        auto& lutProperties = displayLuts->lutProperties;
        std::vector<float> buffers;
@@ -307,8 +324,6 @@ sk_sp<SkShader> LutShader::lutShader(sk_sp<SkShader>& input,
                                      lutProperties[i].dimension, lutProperties[i].size,
                                      lutProperties[i].samplingKey, srcDataspace);
        }

        input = input->makeWithWorkingColorSpace(outColorSpace);
    }
    return input;
}