backends/drm: support HDR content while an ICC profile is set better

The brightness of the screen is read from the luminance tag, and through
the color management protocol(s) passed to apps, so that they can adjust
their content accordingly
This commit is contained in:
Xaver Hugl 2024-02-26 21:41:15 +01:00
parent 183637502d
commit d55f012537
3 changed files with 28 additions and 8 deletions

View file

@ -360,7 +360,8 @@ ColorDescription DrmOutput::createColorDescription(const std::shared_ptr<OutputC
props->maxPeakBrightnessOverride.value_or(m_state.maxPeakBrightnessOverride).value_or(m_connector->edid()->desiredMaxLuminance().value_or(1000)),
Colorimetry::fromName(NamedColorimetry::BT709).interpolateGamutTo(nativeColorimetry, props->sdrGamutWideness.value_or(m_state.sdrGamutWideness)));
} else if (const auto profile = props->iccProfile.value_or(m_state.iccProfile)) {
return ColorDescription(profile->colorimetry(), NamedTransferFunction::gamma22, 200, 0, 200, 200);
const double brightness = profile->brightness().value_or(200);
return ColorDescription(profile->colorimetry(), NamedTransferFunction::gamma22, brightness, 0, brightness, brightness);
} else {
return ColorDescription::sRGB;
}

View file

@ -17,19 +17,21 @@
namespace KWin
{
IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, BToATagData &&bToATag, const std::shared_ptr<ColorTransformation> &vcgt)
IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, BToATagData &&bToATag, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> brightness)
: m_handle(handle)
, m_colorimetry(colorimetry)
, m_bToATag(std::move(bToATag))
, m_vcgt(vcgt)
, m_brightness(brightness)
{
}
IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, const std::shared_ptr<ColorTransformation> &inverseEOTF, const std::shared_ptr<ColorTransformation> &vcgt)
IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, const std::shared_ptr<ColorTransformation> &inverseEOTF, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> brightness)
: m_handle(handle)
, m_colorimetry(colorimetry)
, m_inverseEOTF(inverseEOTF)
, m_vcgt(vcgt)
, m_brightness(brightness)
{
}
@ -38,6 +40,11 @@ IccProfile::~IccProfile()
cmsCloseProfile(m_handle);
}
std::optional<double> IccProfile::brightness() const
{
return m_brightness;
}
const Colorimetry &IccProfile::colorimetry() const
{
return m_colorimetry;
@ -311,6 +318,13 @@ std::unique_ptr<IccProfile> IccProfile::load(const QString &path)
return nullptr;
}
std::optional<double> brightness;
if (cmsCIEXYZ *luminance = static_cast<cmsCIEXYZ *>(cmsReadTag(handle, cmsSigLuminanceTag))) {
// for some reason, lcms exposes the luminance as a XYZ triple...
// only Y is non-zero, and it's the brightness in nits
brightness = luminance->Y;
}
BToATagData lutData;
if (cmsIsTag(handle, cmsSigBToD1Tag) && !cmsIsTag(handle, cmsSigBToA1Tag) && !cmsIsTag(handle, cmsSigBToA0Tag)) {
qCWarning(KWIN_CORE, "Profiles with only BToD tags aren't supported yet");
@ -320,7 +334,7 @@ std::unique_ptr<IccProfile> IccProfile::load(const QString &path)
// lut based profile, with relative colorimetric intent supported
auto data = parseBToATag(handle, cmsSigBToA1Tag);
if (data) {
return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), std::move(*data), vcgt);
return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), std::move(*data), vcgt, brightness);
} else {
qCWarning(KWIN_CORE, "Parsing BToA1 tag failed");
return nullptr;
@ -330,7 +344,7 @@ std::unique_ptr<IccProfile> IccProfile::load(const QString &path)
// lut based profile, with perceptual intent. The ICC docs say to use this as a fallback
auto data = parseBToATag(handle, cmsSigBToA0Tag);
if (data) {
return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), std::move(*data), vcgt);
return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), std::move(*data), vcgt, brightness);
} else {
qCWarning(KWIN_CORE, "Parsing BToA0 tag failed");
return nullptr;
@ -353,7 +367,7 @@ std::unique_ptr<IccProfile> IccProfile::load(const QString &path)
std::vector<std::unique_ptr<ColorPipelineStage>> stages;
stages.push_back(std::make_unique<ColorPipelineStage>(cmsStageAllocToneCurves(nullptr, 3, toneCurves)));
const auto inverseEOTF = std::make_shared<ColorTransformation>(std::move(stages));
return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), inverseEOTF, vcgt);
return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), inverseEOTF, vcgt, brightness);
}
}

View file

@ -33,8 +33,8 @@ public:
std::unique_ptr<ColorTransformation> A;
};
explicit IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, BToATagData &&bToATag, const std::shared_ptr<ColorTransformation> &vcgt);
explicit IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, const std::shared_ptr<ColorTransformation> &inverseEOTF, const std::shared_ptr<ColorTransformation> &vcgt);
explicit IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, BToATagData &&bToATag, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> brightness);
explicit IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, const std::shared_ptr<ColorTransformation> &inverseEOTF, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> brightness);
~IccProfile();
/**
@ -52,6 +52,10 @@ public:
*/
std::shared_ptr<ColorTransformation> vcgt() const;
const Colorimetry &colorimetry() const;
/**
* The brightness with a completely white output, in nits
*/
std::optional<double> brightness() const;
static std::unique_ptr<IccProfile> load(const QString &path);
@ -61,6 +65,7 @@ private:
const std::optional<BToATagData> m_bToATag;
const std::shared_ptr<ColorTransformation> m_inverseEOTF;
const std::shared_ptr<ColorTransformation> m_vcgt;
const std::optional<double> m_brightness;
};
}