core: introduce icc profile helper
This commit is contained in:
parent
4d9f1453d0
commit
44ae4ba004
6 changed files with 137 additions and 39 deletions
|
@ -49,6 +49,7 @@ target_sources(kwin PRIVATE
|
|||
core/graphicsbuffer.cpp
|
||||
core/graphicsbufferallocator.cpp
|
||||
core/graphicsbufferview.cpp
|
||||
core/iccprofile.cpp
|
||||
core/inputbackend.cpp
|
||||
core/inputdevice.cpp
|
||||
core/output.cpp
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "colordevice.h"
|
||||
#include "core/colorpipelinestage.h"
|
||||
#include "core/colortransformation.h"
|
||||
#include "core/iccprofile.h"
|
||||
#include "core/output.h"
|
||||
#include "utils/common.h"
|
||||
|
||||
|
@ -49,7 +50,7 @@ public:
|
|||
Output *output;
|
||||
DirtyToneCurves dirtyCurves;
|
||||
QTimer *updateTimer;
|
||||
QString profile;
|
||||
QString profilePath;
|
||||
uint brightness = 100;
|
||||
uint temperature = 6500;
|
||||
|
||||
|
@ -57,7 +58,7 @@ public:
|
|||
QVector3D temperatureFactors = QVector3D(1, 1, 1);
|
||||
std::unique_ptr<ColorPipelineStage> brightnessStage;
|
||||
QVector3D brightnessFactors = QVector3D(1, 1, 1);
|
||||
std::unique_ptr<ColorPipelineStage> calibrationStage;
|
||||
std::unique_ptr<IccProfile> iccProfile;
|
||||
|
||||
std::shared_ptr<ColorTransformation> transformation;
|
||||
// used if only limited per-channel multiplication is available
|
||||
|
@ -78,13 +79,6 @@ void ColorDevicePrivate::rebuildPipeline()
|
|||
dirtyCurves = DirtyToneCurves();
|
||||
|
||||
std::vector<std::unique_ptr<ColorPipelineStage>> stages;
|
||||
if (calibrationStage) {
|
||||
if (auto s = calibrationStage->dup()) {
|
||||
stages.push_back(std::move(s));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (brightnessStage) {
|
||||
if (auto s = brightnessStage->dup()) {
|
||||
stages.push_back(std::move(s));
|
||||
|
@ -101,6 +95,9 @@ void ColorDevicePrivate::rebuildPipeline()
|
|||
}
|
||||
|
||||
const auto tmp = std::make_shared<ColorTransformation>(std::move(stages));
|
||||
if (iccProfile && iccProfile->vcgt()) {
|
||||
tmp->append(iccProfile->vcgt().get());
|
||||
}
|
||||
if (tmp->valid()) {
|
||||
transformation = tmp;
|
||||
simpleTransformation = brightnessFactors * temperatureFactors;
|
||||
|
@ -114,7 +111,7 @@ static qreal interpolate(qreal a, qreal b, qreal blendFactor)
|
|||
|
||||
QString ColorDevice::profile() const
|
||||
{
|
||||
return d->profile;
|
||||
return d->profilePath;
|
||||
}
|
||||
|
||||
void ColorDevicePrivate::updateTemperatureToneCurves()
|
||||
|
@ -210,32 +207,7 @@ void ColorDevicePrivate::updateBrightnessToneCurves()
|
|||
|
||||
void ColorDevicePrivate::updateCalibrationToneCurves()
|
||||
{
|
||||
calibrationStage.reset();
|
||||
|
||||
if (profile.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmsHPROFILE handle = cmsOpenProfileFromFile(profile.toUtf8(), "r");
|
||||
if (!handle) {
|
||||
qCWarning(KWIN_CORE) << "Failed to open color profile file:" << profile;
|
||||
return;
|
||||
}
|
||||
|
||||
cmsToneCurve **vcgt = static_cast<cmsToneCurve **>(cmsReadTag(handle, cmsSigVcgtTag));
|
||||
if (!vcgt || !vcgt[0]) {
|
||||
qCWarning(KWIN_CORE) << "Profile" << profile << "has no VCGT tag";
|
||||
} else {
|
||||
// Need to duplicate the VCGT tone curves as they are owned by the profile.
|
||||
cmsToneCurve *toneCurves[] = {
|
||||
cmsDupToneCurve(vcgt[0]),
|
||||
cmsDupToneCurve(vcgt[1]),
|
||||
cmsDupToneCurve(vcgt[2]),
|
||||
};
|
||||
calibrationStage = std::make_unique<ColorPipelineStage>(cmsStageAllocToneCurves(nullptr, 3, toneCurves));
|
||||
}
|
||||
|
||||
cmsCloseProfile(handle);
|
||||
iccProfile = IccProfile::load(profilePath);
|
||||
}
|
||||
|
||||
ColorDevice::ColorDevice(Output *output, QObject *parent)
|
||||
|
@ -306,10 +278,10 @@ void ColorDevice::setTemperature(uint temperature)
|
|||
|
||||
void ColorDevice::setProfile(const QString &profile)
|
||||
{
|
||||
if (d->profile == profile) {
|
||||
if (d->profilePath == profile) {
|
||||
return;
|
||||
}
|
||||
d->profile = profile;
|
||||
d->profilePath = profile;
|
||||
d->dirtyCurves |= ColorDevicePrivate::DirtyCalibrationToneCurve;
|
||||
scheduleUpdate();
|
||||
Q_EMIT profileChanged();
|
||||
|
|
|
@ -45,6 +45,19 @@ ColorTransformation::~ColorTransformation()
|
|||
}
|
||||
}
|
||||
|
||||
void ColorTransformation::append(ColorTransformation *transformation)
|
||||
{
|
||||
for (auto &stage : transformation->m_stages) {
|
||||
auto dup = stage->dup();
|
||||
if (!cmsPipelineInsertStage(m_pipeline, cmsAT_END, dup->stage())) {
|
||||
qCWarning(KWIN_CORE) << "Failed to insert cmsPipeline stage!";
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
m_stages.push_back(std::move(dup));
|
||||
}
|
||||
}
|
||||
|
||||
bool ColorTransformation::valid() const
|
||||
{
|
||||
return m_valid;
|
||||
|
|
|
@ -28,13 +28,15 @@ public:
|
|||
ColorTransformation(std::vector<std::unique_ptr<ColorPipelineStage>> &&stages);
|
||||
~ColorTransformation();
|
||||
|
||||
void append(ColorTransformation *transformation);
|
||||
|
||||
bool valid() const;
|
||||
|
||||
std::tuple<uint16_t, uint16_t, uint16_t> transform(uint16_t r, uint16_t g, uint16_t b) const;
|
||||
|
||||
private:
|
||||
cmsPipeline *const m_pipeline;
|
||||
const std::vector<std::unique_ptr<ColorPipelineStage>> m_stages;
|
||||
std::vector<std::unique_ptr<ColorPipelineStage>> m_stages;
|
||||
bool m_valid = true;
|
||||
};
|
||||
|
||||
|
|
75
src/core/iccprofile.cpp
Normal file
75
src/core/iccprofile.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "iccprofile.h"
|
||||
#include "colorlut.h"
|
||||
#include "colorpipelinestage.h"
|
||||
#include "colortransformation.h"
|
||||
#include "utils/common.h"
|
||||
|
||||
#include <lcms2.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
IccProfile::IccProfile(cmsHPROFILE handle, const std::shared_ptr<ColorTransformation> &vcgt)
|
||||
: m_handle(handle)
|
||||
, m_vcgt(vcgt)
|
||||
{
|
||||
}
|
||||
|
||||
IccProfile::~IccProfile()
|
||||
{
|
||||
cmsCloseProfile(m_handle);
|
||||
}
|
||||
|
||||
std::shared_ptr<ColorTransformation> IccProfile::vcgt() const
|
||||
{
|
||||
return m_vcgt;
|
||||
}
|
||||
|
||||
std::unique_ptr<IccProfile> IccProfile::load(const QString &path)
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
cmsHPROFILE handle = cmsOpenProfileFromFile(path.toUtf8(), "r");
|
||||
if (!handle) {
|
||||
qCWarning(KWIN_CORE) << "Failed to open color profile file:" << path;
|
||||
return nullptr;
|
||||
}
|
||||
if (cmsGetDeviceClass(handle) != cmsSigDisplayClass) {
|
||||
qCWarning(KWIN_CORE) << "Only Display ICC profiles are supported";
|
||||
return nullptr;
|
||||
}
|
||||
if (cmsGetPCS(handle) != cmsColorSpaceSignature::cmsSigXYZData) {
|
||||
qCWarning(KWIN_CORE) << "Only ICC profiles with a XYZ connection space are supported";
|
||||
return nullptr;
|
||||
}
|
||||
if (cmsGetColorSpace(handle) != cmsColorSpaceSignature::cmsSigRgbData) {
|
||||
qCWarning(KWIN_CORE) << "Only ICC profiles with RGB color spaces are supported";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<ColorTransformation> vcgt;
|
||||
cmsToneCurve **vcgtTag = static_cast<cmsToneCurve **>(cmsReadTag(handle, cmsSigVcgtTag));
|
||||
if (!vcgtTag || !vcgtTag[0]) {
|
||||
qCWarning(KWIN_CORE) << "Profile" << path << "has no VCGT tag";
|
||||
} else {
|
||||
// Need to duplicate the VCGT tone curves as they are owned by the profile.
|
||||
cmsToneCurve *toneCurves[] = {
|
||||
cmsDupToneCurve(vcgtTag[0]),
|
||||
cmsDupToneCurve(vcgtTag[1]),
|
||||
cmsDupToneCurve(vcgtTag[2]),
|
||||
};
|
||||
std::vector<std::unique_ptr<ColorPipelineStage>> stages;
|
||||
stages.push_back(std::make_unique<ColorPipelineStage>(cmsStageAllocToneCurves(nullptr, 3, toneCurves)));
|
||||
vcgt = std::make_shared<ColorTransformation>(std::move(stages));
|
||||
}
|
||||
|
||||
return std::make_unique<IccProfile>(handle, vcgt);
|
||||
}
|
||||
|
||||
}
|
35
src/core/iccprofile.h
Normal file
35
src/core/iccprofile.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QString>
|
||||
#include <memory>
|
||||
|
||||
typedef void *cmsHPROFILE;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class ColorTransformation;
|
||||
|
||||
class KWIN_EXPORT IccProfile
|
||||
{
|
||||
public:
|
||||
explicit IccProfile(cmsHPROFILE handle, const std::shared_ptr<ColorTransformation> &vcgt);
|
||||
~IccProfile();
|
||||
|
||||
std::shared_ptr<ColorTransformation> vcgt() const;
|
||||
|
||||
static std::unique_ptr<IccProfile> load(const QString &path);
|
||||
|
||||
private:
|
||||
cmsHPROFILE const m_handle;
|
||||
const std::shared_ptr<ColorTransformation> m_vcgt;
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in a new issue