core/iccprofile: read and use the black point for black point compensation

This commit is contained in:
Xaver Hugl 2024-08-12 02:33:18 +02:00
parent 6f79597f13
commit 0fdc3b2633
3 changed files with 32 additions and 20 deletions

View file

@ -360,8 +360,9 @@ ColorDescription DrmOutput::createColorDescription(const std::shared_ptr<OutputC
const bool wcg = props->wideColorGamut.value_or(m_state.wideColorGamut); const bool wcg = props->wideColorGamut.value_or(m_state.wideColorGamut);
const auto iccProfile = props->iccProfile.value_or(m_state.iccProfile); const auto iccProfile = props->iccProfile.value_or(m_state.iccProfile);
if (colorSource == ColorProfileSource::ICC && !hdr && !wcg && iccProfile) { if (colorSource == ColorProfileSource::ICC && !hdr && !wcg && iccProfile) {
const double brightness = iccProfile->brightness().value_or(200); const double minBrightness = iccProfile->minBrightness().value_or(0);
return ColorDescription(iccProfile->colorimetry(), TransferFunction(TransferFunction::gamma22, 0, brightness), brightness, 0, brightness, brightness); 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 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)); 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));

View file

@ -16,21 +16,23 @@
namespace KWin namespace KWin
{ {
IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, BToATagData &&bToATag, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> brightness) IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, BToATagData &&bToATag, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> minBrightness, std::optional<double> maxBrightness)
: m_handle(handle) : m_handle(handle)
, m_colorimetry(colorimetry) , m_colorimetry(colorimetry)
, m_bToATag(std::move(bToATag)) , m_bToATag(std::move(bToATag))
, m_vcgt(vcgt) , m_vcgt(vcgt)
, m_brightness(brightness) , m_minBrightness(minBrightness)
, m_maxBrightness(maxBrightness)
{ {
} }
IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, const std::shared_ptr<ColorTransformation> &inverseEOTF, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> brightness) IccProfile::IccProfile(cmsHPROFILE handle, const Colorimetry &colorimetry, const std::shared_ptr<ColorTransformation> &inverseEOTF, const std::shared_ptr<ColorTransformation> &vcgt, std::optional<double> minBrightness, std::optional<double> maxBrightness)
: m_handle(handle) : m_handle(handle)
, m_colorimetry(colorimetry) , m_colorimetry(colorimetry)
, m_inverseEOTF(inverseEOTF) , m_inverseEOTF(inverseEOTF)
, m_vcgt(vcgt) , m_vcgt(vcgt)
, m_brightness(brightness) , m_minBrightness(minBrightness)
, m_maxBrightness(maxBrightness)
{ {
} }
@ -39,9 +41,14 @@ IccProfile::~IccProfile()
cmsCloseProfile(m_handle); cmsCloseProfile(m_handle);
} }
std::optional<double> IccProfile::brightness() const std::optional<double> IccProfile::minBrightness() const
{ {
return m_brightness; return m_minBrightness;
}
std::optional<double> IccProfile::maxBrightness() const
{
return m_maxBrightness;
} }
const Colorimetry &IccProfile::colorimetry() const const Colorimetry &IccProfile::colorimetry() const
@ -317,11 +324,16 @@ std::unique_ptr<IccProfile> IccProfile::load(const QString &path)
return nullptr; return nullptr;
} }
std::optional<double> brightness; std::optional<double> minBrightness;
std::optional<double> maxBrightness;
if (cmsCIEXYZ *luminance = static_cast<cmsCIEXYZ *>(cmsReadTag(handle, cmsSigLuminanceTag))) { if (cmsCIEXYZ *luminance = static_cast<cmsCIEXYZ *>(cmsReadTag(handle, cmsSigLuminanceTag))) {
// for some reason, lcms exposes the luminance as a XYZ triple... // for some reason, lcms exposes the luminance as a XYZ triple...
// only Y is non-zero, and it's the brightness in nits // 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; BToATagData lutData;
@ -333,7 +345,7 @@ std::unique_ptr<IccProfile> IccProfile::load(const QString &path)
// lut based profile, with relative colorimetric intent supported // lut based profile, with relative colorimetric intent supported
auto data = parseBToATag(handle, cmsSigBToA1Tag); auto data = parseBToATag(handle, cmsSigBToA1Tag);
if (data) { if (data) {
return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), std::move(*data), vcgt, brightness); return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), std::move(*data), vcgt, minBrightness, maxBrightness);
} else { } else {
qCWarning(KWIN_CORE, "Parsing BToA1 tag failed"); qCWarning(KWIN_CORE, "Parsing BToA1 tag failed");
return nullptr; return nullptr;
@ -343,7 +355,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 // lut based profile, with perceptual intent. The ICC docs say to use this as a fallback
auto data = parseBToATag(handle, cmsSigBToA0Tag); auto data = parseBToATag(handle, cmsSigBToA0Tag);
if (data) { if (data) {
return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), std::move(*data), vcgt, brightness); return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), std::move(*data), vcgt, minBrightness, maxBrightness);
} else { } else {
qCWarning(KWIN_CORE, "Parsing BToA0 tag failed"); qCWarning(KWIN_CORE, "Parsing BToA0 tag failed");
return nullptr; return nullptr;
@ -366,7 +378,7 @@ std::unique_ptr<IccProfile> IccProfile::load(const QString &path)
std::vector<std::unique_ptr<ColorPipelineStage>> stages; std::vector<std::unique_ptr<ColorPipelineStage>> stages;
stages.push_back(std::make_unique<ColorPipelineStage>(cmsStageAllocToneCurves(nullptr, 3, toneCurves))); stages.push_back(std::make_unique<ColorPipelineStage>(cmsStageAllocToneCurves(nullptr, 3, toneCurves)));
const auto inverseEOTF = std::make_shared<ColorTransformation>(std::move(stages)); const auto inverseEOTF = std::make_shared<ColorTransformation>(std::move(stages));
return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), inverseEOTF, vcgt, brightness); return std::make_unique<IccProfile>(handle, Colorimetry(red, green, blue, white), inverseEOTF, vcgt, minBrightness, maxBrightness);
} }
} }

View file

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