colorimetry: use 4x4 matrices for colorimetry transforms
This is so that offsets can be represented in the matrices and not just scaled and rotated coordinate systems
This commit is contained in:
parent
4dd1e91bda
commit
b1414033ef
7 changed files with 41 additions and 64 deletions
|
@ -9,7 +9,7 @@ in vec2 texcoord0;
|
|||
uniform sampler2D src;
|
||||
uniform float sdrBrightness;
|
||||
|
||||
uniform mat3 matrix1;
|
||||
uniform mat4 toXYZD50;
|
||||
|
||||
uniform int Bsize;
|
||||
uniform sampler2D Bsampler;
|
||||
|
@ -39,7 +39,7 @@ void main()
|
|||
vec4 tex = texture2D(src, texcoord0);
|
||||
tex.rgb /= max(tex.a, 0.001);
|
||||
tex.rgb /= sdrBrightness;
|
||||
tex.rgb = matrix1 * tex.rgb;
|
||||
tex.rgb = (toXYZD50 * vec4(tex.rgb, 1.0)).rgb;
|
||||
if (Bsize > 0) {
|
||||
tex.rgb = sample1DLut(tex.rgb, Bsampler, Bsize);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ out vec4 fragColor;
|
|||
uniform sampler2D src;
|
||||
uniform float sdrBrightness;
|
||||
|
||||
uniform mat3 matrix1;
|
||||
uniform mat4 toXYZD50;
|
||||
|
||||
uniform int Bsize;
|
||||
uniform sampler2D Bsampler;
|
||||
|
@ -42,7 +42,7 @@ void main()
|
|||
vec4 tex = texture(src, texcoord0);
|
||||
tex.rgb /= max(tex.a, 0.001);
|
||||
tex.rgb /= sdrBrightness;
|
||||
tex.rgb = matrix1 * tex.rgb;
|
||||
tex.rgb = (toXYZD50 * vec4(tex.rgb, 1.0)).rgb;
|
||||
if (Bsize > 0) {
|
||||
tex.rgb = sample1DLut(tex.rgb, Bsampler, Bsize);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ IccShader::IccShader()
|
|||
m_locations = {
|
||||
.src = m_shader->uniformLocation("src"),
|
||||
.sdrBrightness = m_shader->uniformLocation("sdrBrightness"),
|
||||
.matrix1 = m_shader->uniformLocation("matrix1"),
|
||||
.toXYZD50 = m_shader->uniformLocation("toXYZD50"),
|
||||
.bsize = m_shader->uniformLocation("Bsize"),
|
||||
.bsampler = m_shader->uniformLocation("Bsampler"),
|
||||
.matrix2 = m_shader->uniformLocation("matrix2"),
|
||||
|
@ -45,7 +45,7 @@ static const QVector2D D50 = Colorimetry::xyzToXY(QVector3D(0.9642, 1.0, 0.8249)
|
|||
bool IccShader::setProfile(const std::shared_ptr<IccProfile> &profile)
|
||||
{
|
||||
if (!profile) {
|
||||
m_matrix1.setToIdentity();
|
||||
m_toXYZD50.setToIdentity();
|
||||
m_B.reset();
|
||||
m_matrix2.setToIdentity();
|
||||
m_M.reset();
|
||||
|
@ -55,14 +55,14 @@ bool IccShader::setProfile(const std::shared_ptr<IccProfile> &profile)
|
|||
}
|
||||
if (m_profile != profile) {
|
||||
const auto vcgt = profile->vcgt();
|
||||
QMatrix3x3 matrix1;
|
||||
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()) {
|
||||
matrix1 = Colorimetry::chromaticAdaptationMatrix(profile->colorimetry().white(), D50) * profile->colorimetry().toXYZ();
|
||||
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);
|
||||
|
@ -129,7 +129,7 @@ bool IccShader::setProfile(const std::shared_ptr<IccProfile> &profile)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
m_matrix1 = matrix1;
|
||||
m_toXYZD50 = toXYZD50;
|
||||
m_B = std::move(B);
|
||||
m_matrix2 = matrix2;
|
||||
m_M = std::move(M);
|
||||
|
@ -150,11 +150,11 @@ void IccShader::setUniforms(const std::shared_ptr<IccProfile> &profile, float sd
|
|||
// this failing can be silently ignored, it should only happen with GPU resets and gets corrected later
|
||||
setProfile(profile);
|
||||
|
||||
QMatrix3x3 nightColor;
|
||||
QMatrix4x4 nightColor;
|
||||
nightColor(0, 0) = channelFactors.x();
|
||||
nightColor(1, 1) = channelFactors.y();
|
||||
nightColor(2, 2) = channelFactors.z();
|
||||
m_shader->setUniform(m_locations.matrix1, m_matrix1 * nightColor);
|
||||
m_shader->setUniform(m_locations.toXYZD50, m_toXYZD50 * nightColor);
|
||||
m_shader->setUniform(m_locations.sdrBrightness, sdrBrightness);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
|
|
|
@ -33,7 +33,7 @@ private:
|
|||
std::unique_ptr<GLShader> m_shader;
|
||||
std::shared_ptr<IccProfile> m_profile;
|
||||
|
||||
QMatrix3x3 m_matrix1;
|
||||
QMatrix4x4 m_toXYZD50;
|
||||
std::unique_ptr<GlLookUpTable> m_B;
|
||||
QMatrix4x4 m_matrix2;
|
||||
std::unique_ptr<GlLookUpTable> m_M;
|
||||
|
@ -43,7 +43,7 @@ private:
|
|||
{
|
||||
int src;
|
||||
int sdrBrightness;
|
||||
int matrix1;
|
||||
int toXYZD50;
|
||||
int bsize;
|
||||
int bsampler;
|
||||
int matrix2;
|
||||
|
|
|
@ -10,25 +10,9 @@
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
static QMatrix3x3 inverse(const QMatrix3x3 &m)
|
||||
static QMatrix4x4 matrixFromColumns(const QVector3D &first, const QVector3D &second, const QVector3D &third)
|
||||
{
|
||||
const double determinant = m(0, 0) * (m(1, 1) * m(2, 2) - m(2, 1) * m(1, 2)) - m(0, 1) * (m(1, 0) * m(2, 2) - m(1, 2) * m(2, 0)) + m(0, 2) * (m(1, 0) * m(2, 1) - m(1, 1) * m(2, 0));
|
||||
QMatrix3x3 ret;
|
||||
ret(0, 0) = (m(1, 1) * m(2, 2) - m(2, 1) * m(1, 2)) / determinant;
|
||||
ret(0, 1) = (m(0, 2) * m(2, 1) - m(0, 1) * m(2, 2)) / determinant;
|
||||
ret(0, 2) = (m(0, 1) * m(1, 2) - m(0, 2) * m(1, 1)) / determinant;
|
||||
ret(1, 0) = (m(1, 2) * m(2, 0) - m(1, 0) * m(2, 2)) / determinant;
|
||||
ret(1, 1) = (m(0, 0) * m(2, 2) - m(0, 2) * m(2, 0)) / determinant;
|
||||
ret(1, 2) = (m(1, 0) * m(0, 2) - m(0, 0) * m(1, 2)) / determinant;
|
||||
ret(2, 0) = (m(1, 0) * m(2, 1) - m(2, 0) * m(1, 1)) / determinant;
|
||||
ret(2, 1) = (m(2, 0) * m(0, 1) - m(0, 0) * m(2, 1)) / determinant;
|
||||
ret(2, 2) = (m(0, 0) * m(1, 1) - m(1, 0) * m(0, 1)) / determinant;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static QMatrix3x3 matrixFromColumns(const QVector3D &first, const QVector3D &second, const QVector3D &third)
|
||||
{
|
||||
QMatrix3x3 ret;
|
||||
QMatrix4x4 ret;
|
||||
ret(0, 0) = first.x();
|
||||
ret(1, 0) = first.y();
|
||||
ret(2, 0) = first.z();
|
||||
|
@ -41,14 +25,6 @@ static QMatrix3x3 matrixFromColumns(const QVector3D &first, const QVector3D &sec
|
|||
return ret;
|
||||
}
|
||||
|
||||
static QVector3D operator*(const QMatrix3x3 &mat, const QVector3D &v)
|
||||
{
|
||||
return QVector3D(
|
||||
mat(0, 0) * v.x() + mat(0, 1) * v.y() + mat(0, 2) * v.z(),
|
||||
mat(1, 0) * v.x() + mat(1, 1) * v.y() + mat(1, 2) * v.z(),
|
||||
mat(2, 0) * v.x() + mat(2, 1) * v.y() + mat(2, 2) * v.z());
|
||||
}
|
||||
|
||||
QVector3D Colorimetry::xyToXYZ(QVector2D xy)
|
||||
{
|
||||
return QVector3D(xy.x() / xy.y(), 1, (1 - xy.x() - xy.y()) / xy.y());
|
||||
|
@ -60,10 +36,10 @@ QVector2D Colorimetry::xyzToXY(QVector3D xyz)
|
|||
return QVector2D(xyz.x() / (xyz.x() + xyz.y() + xyz.z()), xyz.y() / (xyz.x() + xyz.y() + xyz.z()));
|
||||
}
|
||||
|
||||
QMatrix3x3 Colorimetry::chromaticAdaptationMatrix(QVector2D sourceWhitepoint, QVector2D destinationWhitepoint)
|
||||
QMatrix4x4 Colorimetry::chromaticAdaptationMatrix(QVector2D sourceWhitepoint, QVector2D destinationWhitepoint)
|
||||
{
|
||||
static const QMatrix3x3 bradford = []() {
|
||||
QMatrix3x3 ret;
|
||||
static const QMatrix4x4 bradford = []() {
|
||||
QMatrix4x4 ret;
|
||||
ret(0, 0) = 0.8951;
|
||||
ret(0, 1) = 0.2664;
|
||||
ret(0, 2) = -0.1614;
|
||||
|
@ -75,8 +51,8 @@ QMatrix3x3 Colorimetry::chromaticAdaptationMatrix(QVector2D sourceWhitepoint, QV
|
|||
ret(2, 2) = 1.0296;
|
||||
return ret;
|
||||
}();
|
||||
static const QMatrix3x3 inverseBradford = []() {
|
||||
QMatrix3x3 ret;
|
||||
static const QMatrix4x4 inverseBradford = []() {
|
||||
QMatrix4x4 ret;
|
||||
ret(0, 0) = 0.9869929;
|
||||
ret(0, 1) = -0.1470543;
|
||||
ret(0, 2) = 0.1599627;
|
||||
|
@ -89,19 +65,19 @@ QMatrix3x3 Colorimetry::chromaticAdaptationMatrix(QVector2D sourceWhitepoint, QV
|
|||
return ret;
|
||||
}();
|
||||
if (sourceWhitepoint == destinationWhitepoint) {
|
||||
return QMatrix3x3{};
|
||||
return QMatrix4x4{};
|
||||
}
|
||||
const QVector3D factors = (bradford * xyToXYZ(destinationWhitepoint)) / (bradford * xyToXYZ(sourceWhitepoint));
|
||||
QMatrix3x3 adaptation{};
|
||||
QMatrix4x4 adaptation{};
|
||||
adaptation(0, 0) = factors.x();
|
||||
adaptation(1, 1) = factors.y();
|
||||
adaptation(2, 2) = factors.z();
|
||||
return inverseBradford * adaptation * bradford;
|
||||
}
|
||||
|
||||
QMatrix3x3 Colorimetry::calculateToXYZMatrix(QVector3D red, QVector3D green, QVector3D blue, QVector3D white)
|
||||
QMatrix4x4 Colorimetry::calculateToXYZMatrix(QVector3D red, QVector3D green, QVector3D blue, QVector3D white)
|
||||
{
|
||||
const auto component_scale = inverse(matrixFromColumns(red, green, blue)) * white;
|
||||
const auto component_scale = (matrixFromColumns(red, green, blue)).inverted() * white;
|
||||
return matrixFromColumns(red * component_scale.x(), green * component_scale.y(), blue * component_scale.z());
|
||||
}
|
||||
|
||||
|
@ -111,7 +87,7 @@ Colorimetry::Colorimetry(QVector2D red, QVector2D green, QVector2D blue, QVector
|
|||
, m_blue(blue)
|
||||
, m_white(white)
|
||||
, m_toXYZ(calculateToXYZMatrix(xyToXYZ(red), xyToXYZ(green), xyToXYZ(blue), xyToXYZ(white)))
|
||||
, m_fromXYZ(inverse(m_toXYZ))
|
||||
, m_fromXYZ(m_toXYZ.inverted())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -121,21 +97,21 @@ Colorimetry::Colorimetry(QVector3D red, QVector3D green, QVector3D blue, QVector
|
|||
, m_blue(xyzToXY(blue))
|
||||
, m_white(xyzToXY(white))
|
||||
, m_toXYZ(calculateToXYZMatrix(red, green, blue, white))
|
||||
, m_fromXYZ(inverse(m_toXYZ))
|
||||
, m_fromXYZ(m_toXYZ.inverted())
|
||||
{
|
||||
}
|
||||
|
||||
const QMatrix3x3 &Colorimetry::toXYZ() const
|
||||
const QMatrix4x4 &Colorimetry::toXYZ() const
|
||||
{
|
||||
return m_toXYZ;
|
||||
}
|
||||
|
||||
const QMatrix3x3 &Colorimetry::fromXYZ() const
|
||||
const QMatrix4x4 &Colorimetry::fromXYZ() const
|
||||
{
|
||||
return m_fromXYZ;
|
||||
}
|
||||
|
||||
QMatrix3x3 Colorimetry::toOther(const Colorimetry &other) const
|
||||
QMatrix4x4 Colorimetry::toOther(const Colorimetry &other) const
|
||||
{
|
||||
// rendering intent is relative colorimetric, so adapt to the different whitepoint
|
||||
return other.fromXYZ() * chromaticAdaptationMatrix(this->white(), other.white()) * toXYZ();
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#pragma once
|
||||
#include <optional>
|
||||
|
||||
#include <QMatrix3x3>
|
||||
#include <QMatrix4x4>
|
||||
#include <QVector2D>
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
@ -39,9 +39,9 @@ public:
|
|||
/**
|
||||
* @returns a matrix adapting XYZ values from the source whitepoint to the destination whitepoint with the Bradford transform
|
||||
*/
|
||||
static QMatrix3x3 chromaticAdaptationMatrix(QVector2D sourceWhitepoint, QVector2D destinationWhitepoint);
|
||||
static QMatrix4x4 chromaticAdaptationMatrix(QVector2D sourceWhitepoint, QVector2D destinationWhitepoint);
|
||||
|
||||
static QMatrix3x3 calculateToXYZMatrix(QVector3D red, QVector3D green, QVector3D blue, QVector3D white);
|
||||
static QMatrix4x4 calculateToXYZMatrix(QVector3D red, QVector3D green, QVector3D blue, QVector3D white);
|
||||
|
||||
explicit Colorimetry(QVector2D red, QVector2D green, QVector2D blue, QVector2D white);
|
||||
explicit Colorimetry(QVector3D red, QVector3D green, QVector3D blue, QVector3D white);
|
||||
|
@ -49,16 +49,16 @@ public:
|
|||
/**
|
||||
* @returns a matrix that transforms from the linear RGB representation of colors in this colorimetry to the XYZ representation
|
||||
*/
|
||||
const QMatrix3x3 &toXYZ() const;
|
||||
const QMatrix4x4 &toXYZ() const;
|
||||
/**
|
||||
* @returns a matrix that transforms from the XYZ representation to the linear RGB representation of colors in this colorimetry
|
||||
*/
|
||||
const QMatrix3x3 &fromXYZ() const;
|
||||
const QMatrix4x4 &fromXYZ() const;
|
||||
/**
|
||||
* @returns a matrix that transforms from linear RGB in this colorimetry to linear RGB in the other colorimetry
|
||||
* the rendering intent is relative colorimetric
|
||||
*/
|
||||
QMatrix3x3 toOther(const Colorimetry &colorimetry) const;
|
||||
QMatrix4x4 toOther(const Colorimetry &colorimetry) const;
|
||||
bool operator==(const Colorimetry &other) const;
|
||||
bool operator==(NamedColorimetry name) const;
|
||||
/**
|
||||
|
@ -76,8 +76,8 @@ private:
|
|||
QVector2D m_green;
|
||||
QVector2D m_blue;
|
||||
QVector2D m_white;
|
||||
QMatrix3x3 m_toXYZ;
|
||||
QMatrix3x3 m_fromXYZ;
|
||||
QMatrix4x4 m_toXYZ;
|
||||
QMatrix4x4 m_fromXYZ;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,7 @@ const int PQ_EOTF = 2;
|
|||
const int scRGB_EOTF = 3;
|
||||
const int gamma22_EOTF = 4;
|
||||
|
||||
uniform mat3 colorimetryTransform;
|
||||
uniform mat4 colorimetryTransform;
|
||||
uniform int sourceNamedTransferFunction;
|
||||
uniform int destinationNamedTransferFunction;
|
||||
uniform float sdrBrightness;// in nits
|
||||
|
@ -57,7 +57,7 @@ vec3 linearToSrgb(vec3 color) {
|
|||
|
||||
vec3 doTonemapping(vec3 color, float maxBrightness) {
|
||||
// TODO do something better here
|
||||
return clamp(color, vec3(0.0), vec3(maxBrightness));
|
||||
return clamp(color.rgb, vec3(0.0), vec3(maxBrightness));
|
||||
}
|
||||
|
||||
vec4 encodingToNits(vec4 color, int sourceTransferFunction) {
|
||||
|
@ -81,7 +81,8 @@ vec4 encodingToNits(vec4 color, int sourceTransferFunction) {
|
|||
|
||||
vec4 sourceEncodingToNitsInDestinationColorspace(vec4 color) {
|
||||
color = encodingToNits(color, sourceNamedTransferFunction);
|
||||
return vec4(doTonemapping(colorimetryTransform * color.rgb, maxHdrBrightness), color.a);
|
||||
color.rgb = (colorimetryTransform * vec4(color.rgb, 1.0)).rgb;
|
||||
return vec4(doTonemapping(color.rgb, maxHdrBrightness), color.a);
|
||||
}
|
||||
|
||||
vec4 nitsToEncoding(vec4 color, int destinationTransferFunction) {
|
||||
|
|
Loading…
Reference in a new issue