File indexing completed on 2024-11-10 04:56:30

0001 /*
0002     SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 #include "icc_shader.h"
0007 #include "core/colorlut3d.h"
0008 #include "core/colortransformation.h"
0009 #include "core/iccprofile.h"
0010 #include "opengl/gllut.h"
0011 #include "opengl/gllut3D.h"
0012 #include "opengl/glshader.h"
0013 #include "opengl/glshadermanager.h"
0014 
0015 namespace KWin
0016 {
0017 
0018 static constexpr size_t lutSize = 1 << 12;
0019 
0020 IccShader::IccShader()
0021     : m_shader(ShaderManager::instance()->generateShaderFromFile(ShaderTrait::MapTexture, QString(), QStringLiteral(":/backends/drm/icc.frag")))
0022 {
0023     m_locations = {
0024         .src = m_shader->uniformLocation("src"),
0025         .sdrBrightness = m_shader->uniformLocation("sdrBrightness"),
0026         .toXYZD50 = m_shader->uniformLocation("toXYZD50"),
0027         .bsize = m_shader->uniformLocation("Bsize"),
0028         .bsampler = m_shader->uniformLocation("Bsampler"),
0029         .matrix2 = m_shader->uniformLocation("matrix2"),
0030         .msize = m_shader->uniformLocation("Msize"),
0031         .msampler = m_shader->uniformLocation("Msampler"),
0032         .csize = m_shader->uniformLocation("Csize"),
0033         .csampler = m_shader->uniformLocation("Csampler"),
0034         .asize = m_shader->uniformLocation("Asize"),
0035         .asampler = m_shader->uniformLocation("Asampler"),
0036     };
0037 }
0038 
0039 IccShader::~IccShader()
0040 {
0041 }
0042 
0043 static const QVector2D D50 = Colorimetry::xyzToXY(QVector3D(0.9642, 1.0, 0.8249));
0044 
0045 bool IccShader::setProfile(const std::shared_ptr<IccProfile> &profile)
0046 {
0047     if (!profile) {
0048         m_toXYZD50.setToIdentity();
0049         m_B.reset();
0050         m_matrix2.setToIdentity();
0051         m_M.reset();
0052         m_C.reset();
0053         m_A.reset();
0054         return false;
0055     }
0056     if (m_profile != profile) {
0057         const auto vcgt = profile->vcgt();
0058         QMatrix4x4 toXYZD50;
0059         std::unique_ptr<GlLookUpTable> B;
0060         QMatrix4x4 matrix2;
0061         std::unique_ptr<GlLookUpTable> M;
0062         std::unique_ptr<GlLookUpTable3D> C;
0063         std::unique_ptr<GlLookUpTable> A;
0064         if (const IccProfile::BToATagData *tag = profile->BtToATag()) {
0065             toXYZD50 = Colorimetry::chromaticAdaptationMatrix(profile->colorimetry().white(), D50) * profile->colorimetry().toXYZ();
0066             if (tag->B) {
0067                 const auto sample = [&tag](size_t x) {
0068                     const float relativeX = x / double(lutSize - 1);
0069                     return tag->B->transform(QVector3D(relativeX, relativeX, relativeX));
0070                 };
0071                 B = GlLookUpTable::create(sample, lutSize);
0072                 if (!B) {
0073                     return false;
0074                 }
0075             }
0076             matrix2 = tag->matrix.value_or(QMatrix4x4());
0077             if (tag->M) {
0078                 const auto sample = [&tag](size_t x) {
0079                     const float relativeX = x / double(lutSize - 1);
0080                     return tag->M->transform(QVector3D(relativeX, relativeX, relativeX));
0081                 };
0082                 M = GlLookUpTable::create(sample, lutSize);
0083                 if (!M) {
0084                     return false;
0085                 }
0086             }
0087             if (tag->CLut) {
0088                 const auto sample = [&tag](size_t x, size_t y, size_t z) {
0089                     return tag->CLut->sample(x, y, z);
0090                 };
0091                 C = GlLookUpTable3D::create(sample, tag->CLut->xSize(), tag->CLut->ySize(), tag->CLut->zSize());
0092                 if (!C) {
0093                     return false;
0094                 }
0095             }
0096             if (tag->A) {
0097                 const auto sample = [&tag, vcgt](size_t x) {
0098                     const float relativeX = x / double(lutSize - 1);
0099                     QVector3D ret = tag->A->transform(QVector3D(relativeX, relativeX, relativeX));
0100                     if (vcgt) {
0101                         ret = vcgt->transform(ret);
0102                     }
0103                     return ret;
0104                 };
0105                 A = GlLookUpTable::create(sample, lutSize);
0106                 if (!A) {
0107                     return false;
0108                 }
0109             } else if (vcgt) {
0110                 const auto sample = [&vcgt](size_t x) {
0111                     const float relativeX = x / double(lutSize - 1);
0112                     return vcgt->transform(QVector3D(relativeX, relativeX, relativeX));
0113                 };
0114                 A = GlLookUpTable::create(sample, lutSize);
0115             }
0116         } else {
0117             const auto inverseEOTF = profile->inverseEOTF();
0118             const auto sample = [inverseEOTF, vcgt](size_t x) {
0119                 const float relativeX = x / double(lutSize - 1);
0120                 QVector3D ret(relativeX, relativeX, relativeX);
0121                 ret = inverseEOTF->transform(ret);
0122                 if (vcgt) {
0123                     ret = vcgt->transform(ret);
0124                 }
0125                 return ret;
0126             };
0127             A = GlLookUpTable::create(sample, lutSize);
0128             if (!A) {
0129                 return false;
0130             }
0131         }
0132         m_toXYZD50 = toXYZD50;
0133         m_B = std::move(B);
0134         m_matrix2 = matrix2;
0135         m_M = std::move(M);
0136         m_C = std::move(C);
0137         m_A = std::move(A);
0138         m_profile = profile;
0139     }
0140     return true;
0141 }
0142 
0143 GLShader *IccShader::shader() const
0144 {
0145     return m_shader.get();
0146 }
0147 
0148 void IccShader::setUniforms(const std::shared_ptr<IccProfile> &profile, float sdrBrightness, const QVector3D &channelFactors)
0149 {
0150     // this failing can be silently ignored, it should only happen with GPU resets and gets corrected later
0151     setProfile(profile);
0152 
0153     QMatrix4x4 nightColor;
0154     nightColor(0, 0) = channelFactors.x();
0155     nightColor(1, 1) = channelFactors.y();
0156     nightColor(2, 2) = channelFactors.z();
0157     m_shader->setUniform(m_locations.toXYZD50, m_toXYZD50 * nightColor);
0158     m_shader->setUniform(m_locations.sdrBrightness, sdrBrightness);
0159 
0160     glActiveTexture(GL_TEXTURE1);
0161     if (m_B) {
0162         m_shader->setUniform(m_locations.bsize, int(m_B->size()));
0163         m_shader->setUniform(m_locations.bsampler, 1);
0164         m_B->bind();
0165     } else {
0166         m_shader->setUniform(m_locations.bsize, 0);
0167         m_shader->setUniform(m_locations.bsampler, 1);
0168         glBindTexture(GL_TEXTURE_1D, 0);
0169     }
0170 
0171     m_shader->setUniform(m_locations.matrix2, m_matrix2);
0172 
0173     glActiveTexture(GL_TEXTURE2);
0174     if (m_M) {
0175         m_shader->setUniform(m_locations.msize, int(m_M->size()));
0176         m_shader->setUniform(m_locations.msampler, 2);
0177         m_M->bind();
0178     } else {
0179         m_shader->setUniform(m_locations.msize, 0);
0180         m_shader->setUniform(m_locations.msampler, 1);
0181         glBindTexture(GL_TEXTURE_1D, 0);
0182     }
0183 
0184     glActiveTexture(GL_TEXTURE3);
0185     if (m_C) {
0186         m_shader->setUniform(m_locations.csize, m_C->xSize(), m_C->ySize(), m_C->zSize());
0187         m_shader->setUniform(m_locations.csampler, 3);
0188         m_C->bind();
0189     } else {
0190         m_shader->setUniform(m_locations.csize, 0, 0, 0);
0191         m_shader->setUniform(m_locations.csampler, 3);
0192         glBindTexture(GL_TEXTURE_3D, 0);
0193     }
0194 
0195     glActiveTexture(GL_TEXTURE4);
0196     if (m_A) {
0197         m_shader->setUniform(m_locations.asize, int(m_A->size()));
0198         m_shader->setUniform(m_locations.asampler, 4);
0199         m_A->bind();
0200     } else {
0201         m_shader->setUniform(m_locations.asize, 0);
0202         m_shader->setUniform(m_locations.asampler, 4);
0203         glBindTexture(GL_TEXTURE_1D, 0);
0204     }
0205 
0206     glActiveTexture(GL_TEXTURE0);
0207     m_shader->setUniform(m_locations.src, 0);
0208 }
0209 
0210 }