You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

217 lines
7.8 KiB

/*
SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "icc_shader.h"
#include "core/colorlut3d.h"
#include "core/colortransformation.h"
#include "core/iccprofile.h"
#include "opengl/gllut.h"
#include "opengl/gllut3D.h"
#include "opengl/glshader.h"
#include "opengl/glshadermanager.h"
namespace KWin
{
static constexpr size_t lutSize = 1 << 12;
IccShader::IccShader()
: m_shader(ShaderManager::instance()->generateShaderFromFile(ShaderTrait::MapTexture, QString(), QStringLiteral(":/backends/drm/icc.frag")))
{
m_locations = {
.src = m_shader->uniformLocation("src"),
.toXYZD50 = m_shader->uniformLocation("toXYZD50"),
.bsize = m_shader->uniformLocation("Bsize"),
.bsampler = m_shader->uniformLocation("Bsampler"),
.matrix2 = m_shader->uniformLocation("matrix2"),
.msize = m_shader->uniformLocation("Msize"),
.msampler = m_shader->uniformLocation("Msampler"),
.csize = m_shader->uniformLocation("Csize"),
.csampler = m_shader->uniformLocation("Csampler"),
.asize = m_shader->uniformLocation("Asize"),
.asampler = m_shader->uniformLocation("Asampler"),
};
}
IccShader::~IccShader()
{
}
static const XYZ D50{
.X = 0.9642,
.Y = 1.0,
.Z = 0.8249,
};
bool IccShader::setProfile(const std::shared_ptr<IccProfile> &profile)
{
if (!profile) {
m_toXYZD50.setToIdentity();
m_B.reset();
m_matrix2.setToIdentity();
m_M.reset();
m_C.reset();
m_A.reset();
return false;
}
if (m_profile != profile) {
const auto vcgt = profile->vcgt();
QMatrix4x4 toXYZD50;
std::unique_ptr<GlLookUpTable> B;
QMatrix4x4 matrix2;
std::unique_ptr<GlLookUpTable> M;
std::unique_ptr<GlLookUpTable3D> C;
std::unique_ptr<GlLookUpTable> A;
if (const IccProfile::BToATagData *tag = profile->BtToATag()) {
toXYZD50 = Colorimetry::chromaticAdaptationMatrix(profile->colorimetry().white(), D50) * profile->colorimetry().toXYZ();
if (tag->B) {
const auto sample = [&tag](size_t x) {
const float relativeX = x / double(lutSize - 1);
return tag->B->transform(QVector3D(relativeX, relativeX, relativeX));
};
B = GlLookUpTable::create(sample, lutSize);
if (!B) {
return false;
}
}
matrix2 = tag->matrix.value_or(QMatrix4x4());
if (tag->M) {
const auto sample = [&tag](size_t x) {
const float relativeX = x / double(lutSize - 1);
return tag->M->transform(QVector3D(relativeX, relativeX, relativeX));
};
M = GlLookUpTable::create(sample, lutSize);
if (!M) {
return false;
}
}
if (tag->CLut) {
const auto sample = [&tag](size_t x, size_t y, size_t z) {
return tag->CLut->sample(x, y, z);
};
C = GlLookUpTable3D::create(sample, tag->CLut->xSize(), tag->CLut->ySize(), tag->CLut->zSize());
if (!C) {
return false;
}
}
if (tag->A) {
const auto sample = [&tag, vcgt](size_t x) {
const float relativeX = x / double(lutSize - 1);
QVector3D ret = tag->A->transform(QVector3D(relativeX, relativeX, relativeX));
if (vcgt) {
ret = vcgt->transform(ret);
}
return ret;
};
A = GlLookUpTable::create(sample, lutSize);
if (!A) {
return false;
}
} else if (vcgt) {
const auto sample = [&vcgt](size_t x) {
const float relativeX = x / double(lutSize - 1);
return vcgt->transform(QVector3D(relativeX, relativeX, relativeX));
};
A = GlLookUpTable::create(sample, lutSize);
}
} else {
const auto inverseEOTF = profile->inverseEOTF();
const auto sample = [inverseEOTF, vcgt](size_t x) {
const float relativeX = x / double(lutSize - 1);
QVector3D ret(relativeX, relativeX, relativeX);
ret = inverseEOTF->transform(ret);
if (vcgt) {
ret = vcgt->transform(ret);
}
return ret;
};
A = GlLookUpTable::create(sample, lutSize);
if (!A) {
return false;
}
}
m_toXYZD50 = toXYZD50;
m_B = std::move(B);
m_matrix2 = matrix2;
m_M = std::move(M);
m_C = std::move(C);
m_A = std::move(A);
m_profile = profile;
}
return true;
}
GLShader *IccShader::shader() const
{
return m_shader.get();
}
void IccShader::setUniforms(const std::shared_ptr<IccProfile> &profile, const ColorDescription &inputColor, const QVector3D &channelFactors)
{
// this failing can be silently ignored, it should only happen with GPU resets and gets corrected later
setProfile(profile);
QMatrix4x4 nightColor;
nightColor(0, 0) = channelFactors.x();
nightColor(1, 1) = channelFactors.y();
nightColor(2, 2) = channelFactors.z();
m_shader->setUniform(m_locations.toXYZD50, m_toXYZD50 * nightColor);
m_shader->setUniform(GLShader::IntUniform::SourceNamedTransferFunction, inputColor.transferFunction().type);
m_shader->setUniform(GLShader::Vec2Uniform::SourceTransferFunctionParams, QVector2D(inputColor.transferFunction().minLuminance, inputColor.transferFunction().maxLuminance - inputColor.transferFunction().minLuminance));
m_shader->setUniform(GLShader::FloatUniform::SourceReferenceLuminance, inputColor.referenceLuminance());
m_shader->setUniform(GLShader::FloatUniform::DestinationReferenceLuminance, inputColor.referenceLuminance());
m_shader->setUniform(GLShader::FloatUniform::MaxDestinationLuminance, inputColor.referenceLuminance());
glActiveTexture(GL_TEXTURE1);
if (m_B) {
m_shader->setUniform(m_locations.bsize, int(m_B->size()));
m_shader->setUniform(m_locations.bsampler, 1);
m_B->bind();
} else {
m_shader->setUniform(m_locations.bsize, 0);
m_shader->setUniform(m_locations.bsampler, 1);
glBindTexture(GL_TEXTURE_1D, 0);
}
m_shader->setUniform(m_locations.matrix2, m_matrix2);
glActiveTexture(GL_TEXTURE2);
if (m_M) {
m_shader->setUniform(m_locations.msize, int(m_M->size()));
m_shader->setUniform(m_locations.msampler, 2);
m_M->bind();
} else {
m_shader->setUniform(m_locations.msize, 0);
m_shader->setUniform(m_locations.msampler, 1);
glBindTexture(GL_TEXTURE_1D, 0);
}
glActiveTexture(GL_TEXTURE3);
if (m_C) {
m_shader->setUniform(m_locations.csize, m_C->xSize(), m_C->ySize(), m_C->zSize());
m_shader->setUniform(m_locations.csampler, 3);
m_C->bind();
} else {
m_shader->setUniform(m_locations.csize, 0, 0, 0);
m_shader->setUniform(m_locations.csampler, 3);
glBindTexture(GL_TEXTURE_3D, 0);
}
glActiveTexture(GL_TEXTURE4);
if (m_A) {
m_shader->setUniform(m_locations.asize, int(m_A->size()));
m_shader->setUniform(m_locations.asampler, 4);
m_A->bind();
} else {
m_shader->setUniform(m_locations.asize, 0);
m_shader->setUniform(m_locations.asampler, 4);
glBindTexture(GL_TEXTURE_1D, 0);
}
glActiveTexture(GL_TEXTURE0);
m_shader->setUniform(m_locations.src, 0);
}
}