From 0fdc3b263370e16310db11fc903fa040c9912500 Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Mon, 12 Aug 2024 02:33:18 +0200 Subject: [PATCH] core/iccprofile: read and use the black point for black point compensation --- src/backends/drm/drm_output.cpp | 5 +++-- src/core/iccprofile.cpp | 34 ++++++++++++++++++++++----------- src/core/iccprofile.h | 13 ++++++------- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/backends/drm/drm_output.cpp b/src/backends/drm/drm_output.cpp index 2a06bdbb12..a42394ede9 100644 --- a/src/backends/drm/drm_output.cpp +++ b/src/backends/drm/drm_output.cpp @@ -360,8 +360,9 @@ ColorDescription DrmOutput::createColorDescription(const std::shared_ptrwideColorGamut.value_or(m_state.wideColorGamut); const auto iccProfile = props->iccProfile.value_or(m_state.iccProfile); if (colorSource == ColorProfileSource::ICC && !hdr && !wcg && iccProfile) { - const double brightness = iccProfile->brightness().value_or(200); - return ColorDescription(iccProfile->colorimetry(), TransferFunction(TransferFunction::gamma22, 0, brightness), brightness, 0, brightness, brightness); + const double minBrightness = iccProfile->minBrightness().value_or(0); + const double maxBrightness = iccProfile->maxBrightness().value_or(200); + return ColorDescription(iccProfile->colorimetry(), TransferFunction(TransferFunction::gamma22, minBrightness, maxBrightness), maxBrightness, minBrightness, maxBrightness, maxBrightness); } const bool screenSupportsHdr = m_connector->edid()->isValid() && m_connector->edid()->supportsBT2020() && m_connector->edid()->supportsPQ(); const bool driverSupportsHdr = m_connector->colorspace.isValid() && m_connector->hdrMetadata.isValid() && (m_connector->colorspace.hasEnum(DrmConnector::Colorspace::BT2020_RGB) || m_connector->colorspace.hasEnum(DrmConnector::Colorspace::BT2020_YCC)); diff --git a/src/core/iccprofile.cpp b/src/core/iccprofile.cpp index 92993f063e..34b5d5f900 100644 --- a/src/core/iccprofile.cpp +++ b/src/core/iccprofile.cpp @@ -16,21 +16,23 @@ namespace KWin { -IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, BToATagData &&bToATag, const std::shared_ptr &vcgt, std::optional brightness) +IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, BToATagData &&bToATag, const std::shared_ptr &vcgt, std::optional minBrightness, std::optional maxBrightness) : m_handle(handle) , m_colorimetry(colorimetry) , m_bToATag(std::move(bToATag)) , m_vcgt(vcgt) - , m_brightness(brightness) + , m_minBrightness(minBrightness) + , m_maxBrightness(maxBrightness) { } -IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, const std::shared_ptr &inverseEOTF, const std::shared_ptr &vcgt, std::optional brightness) +IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, const std::shared_ptr &inverseEOTF, const std::shared_ptr &vcgt, std::optional minBrightness, std::optional maxBrightness) : m_handle(handle) , m_colorimetry(colorimetry) , m_inverseEOTF(inverseEOTF) , m_vcgt(vcgt) - , m_brightness(brightness) + , m_minBrightness(minBrightness) + , m_maxBrightness(maxBrightness) { } @@ -39,9 +41,14 @@ IccProfile::~IccProfile() cmsCloseProfile(m_handle); } -std::optional IccProfile::brightness() const +std::optional IccProfile::minBrightness() const { - return m_brightness; + return m_minBrightness; +} + +std::optional IccProfile::maxBrightness() const +{ + return m_maxBrightness; } const Colorimetry &IccProfile::colorimetry() const @@ -317,11 +324,16 @@ std::unique_ptr IccProfile::load(const QString &path) return nullptr; } - std::optional brightness; + std::optional minBrightness; + std::optional maxBrightness; if (cmsCIEXYZ *luminance = static_cast(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; + maxBrightness = luminance->Y; + cmsCIEXYZ blackPoint; + if (cmsDetectDestinationBlackPoint(&blackPoint, handle, INTENT_RELATIVE_COLORIMETRIC, 0)) { + minBrightness = blackPoint.Y * luminance->Y; + } } BToATagData lutData; @@ -333,7 +345,7 @@ std::unique_ptr IccProfile::load(const QString &path) // lut based profile, with relative colorimetric intent supported auto data = parseBToATag(handle, cmsSigBToA1Tag); if (data) { - return std::make_unique(handle, Colorimetry(red, green, blue, white), std::move(*data), vcgt, brightness); + return std::make_unique(handle, Colorimetry(red, green, blue, white), std::move(*data), vcgt, minBrightness, maxBrightness); } else { qCWarning(KWIN_CORE, "Parsing BToA1 tag failed"); return nullptr; @@ -343,7 +355,7 @@ std::unique_ptr 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(handle, Colorimetry(red, green, blue, white), std::move(*data), vcgt, brightness); + return std::make_unique(handle, Colorimetry(red, green, blue, white), std::move(*data), vcgt, minBrightness, maxBrightness); } else { qCWarning(KWIN_CORE, "Parsing BToA0 tag failed"); return nullptr; @@ -366,7 +378,7 @@ std::unique_ptr IccProfile::load(const QString &path) std::vector> stages; stages.push_back(std::make_unique(cmsStageAllocToneCurves(nullptr, 3, toneCurves))); const auto inverseEOTF = std::make_shared(std::move(stages)); - return std::make_unique(handle, Colorimetry(red, green, blue, white), inverseEOTF, vcgt, brightness); + return std::make_unique(handle, Colorimetry(red, green, blue, white), inverseEOTF, vcgt, minBrightness, maxBrightness); } } diff --git a/src/core/iccprofile.h b/src/core/iccprofile.h index 1aefefce00..8ece5cc845 100644 --- a/src/core/iccprofile.h +++ b/src/core/iccprofile.h @@ -33,8 +33,8 @@ public: std::unique_ptr A; }; - explicit IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, BToATagData &&bToATag, const std::shared_ptr &vcgt, std::optional brightness); - explicit IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, const std::shared_ptr &inverseEOTF, const std::shared_ptr &vcgt, std::optional brightness); + explicit IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, BToATagData &&bToATag, const std::shared_ptr &vcgt, std::optional minBrightness, std::optional maxBrightness); + explicit IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, const std::shared_ptr &inverseEOTF, const std::shared_ptr &vcgt, std::optional minBrightness, std::optional maxBrightness); ~IccProfile(); /** @@ -52,10 +52,8 @@ public: */ std::shared_ptr vcgt() const; const Colorimetry &colorimetry() const; - /** - * The brightness with a completely white output, in nits - */ - std::optional brightness() const; + std::optional minBrightness() const; + std::optional maxBrightness() const; static std::unique_ptr load(const QString &path); @@ -65,7 +63,8 @@ private: const std::optional m_bToATag; const std::shared_ptr m_inverseEOTF; const std::shared_ptr m_vcgt; - const std::optional m_brightness; + const std::optional m_minBrightness; + const std::optional m_maxBrightness; }; }