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