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 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));

View file

@ -16,21 +16,23 @@
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_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<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_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<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
@ -317,11 +324,16 @@ std::unique_ptr<IccProfile> IccProfile::load(const QString &path)
return nullptr;
}
std::optional<double> brightness;
std::optional<double> minBrightness;
std::optional<double> maxBrightness;
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;
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> 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, brightness);
return std::make_unique<IccProfile>(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> 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, brightness);
return std::make_unique<IccProfile>(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> 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, 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;
};
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);
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> minBrightness, std::optional<double> maxBrightness);
~IccProfile();
/**
@ -52,10 +52,8 @@ 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;
std::optional<double> minBrightness() const;
std::optional<double> maxBrightness() const;
static std::unique_ptr<IccProfile> load(const QString &path);
@ -65,7 +63,8 @@ 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;
const std::optional<double> m_minBrightness;
const std::optional<double> m_maxBrightness;
};
}