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 }