core: add transfer function minimum and maximum luminance values
This redefines the transfer functions to have a custom luminance at encoded value zero, and a custom luminance at encoded value 1, neither of which are tied to the reference luminance, even for relative transfer functions. The goal of that is that we can use a gamma 2.2 transfer function for the shadow buffer, with the reference luminance being much lower than the maximum luminance. For example, on an HDR screen you might have the reference luminance of 600 nits, while the maximum luminance is 1000 nits. By representing this in gamma 2.2, we can use a much smaller amount of bits per color to store the values than if we used a linear transfer function. An additional benefit is that this way the values in the buffer can be scaled by arbitrary amounts, for example to limit the range of values to [0, 1], which can be represented in a normalized buffer
This commit is contained in:
parent
3e4d9ce939
commit
bea4d1064c
22 changed files with 248 additions and 189 deletions
|
@ -49,7 +49,6 @@ void TestColorspaces::roundtripConversion_data()
|
|||
|
||||
QTest::addRow("BT709 (sRGB) <-> BT2020 (linear)") << NamedColorimetry::BT709 << TransferFunction::sRGB << NamedColorimetry::BT2020 << TransferFunction::linear << s_resolution10bit;
|
||||
QTest::addRow("BT709 (gamma 2.2) <-> BT2020 (linear)") << NamedColorimetry::BT709 << TransferFunction::gamma22 << NamedColorimetry::BT2020 << TransferFunction::linear << s_resolution10bit;
|
||||
QTest::addRow("BT709 (scRGB) <-> BT2020 (linear)") << NamedColorimetry::BT709 << TransferFunction::scRGB << NamedColorimetry::BT2020 << TransferFunction::linear << s_resolution10bit;
|
||||
QTest::addRow("BT709 (linear) <-> BT2020 (linear)") << NamedColorimetry::BT709 << TransferFunction::linear << NamedColorimetry::BT2020 << TransferFunction::linear << s_resolution10bit;
|
||||
QTest::addRow("BT709 (PQ) <-> BT2020 (linear)") << NamedColorimetry::BT709 << TransferFunction::PerceptualQuantizer << NamedColorimetry::BT2020 << TransferFunction::linear << 3 * s_resolution10bit;
|
||||
}
|
||||
|
|
|
@ -145,9 +145,9 @@ void LegacyLutColorOp::program(DrmAtomicCommit *commit, std::span<const ColorOp>
|
|||
QVector3D output(scaledInput, scaledInput, scaledInput);
|
||||
for (const auto &op : operations) {
|
||||
if (auto tf = std::get_if<ColorTransferFunction>(&op.operation)) {
|
||||
output = tf->tf.encodedToNits(output, tf->referenceLuminance);
|
||||
output = tf->tf.encodedToNits(output);
|
||||
} else if (auto tf = std::get_if<InverseColorTransferFunction>(&op.operation)) {
|
||||
output = tf->tf.nitsToEncoded(output, tf->referenceLuminance);
|
||||
output = tf->tf.nitsToEncoded(output);
|
||||
} else if (auto mult = std::get_if<ColorMultiplier>(&op.operation)) {
|
||||
output *= mult->factors;
|
||||
} else {
|
||||
|
|
|
@ -100,11 +100,11 @@ bool EglGbmLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescriptio
|
|||
if (m_pipeline->output()->needsColormanagement()) {
|
||||
// with color management enabled, the factors have to be applied in linear space
|
||||
// the pipeline will optimize out the unnecessary transformations
|
||||
pipeline.addTransferFunction(m_pipeline->colorDescription().transferFunction(), m_pipeline->colorDescription().referenceLuminance());
|
||||
pipeline.addTransferFunction(m_pipeline->colorDescription().transferFunction());
|
||||
}
|
||||
pipeline.addMultiplier(m_pipeline->output()->effectiveChannelFactors());
|
||||
if (m_pipeline->output()->needsColormanagement()) {
|
||||
pipeline.addInverseTransferFunction(m_pipeline->colorDescription().transferFunction(), m_pipeline->colorDescription().referenceLuminance());
|
||||
pipeline.addInverseTransferFunction(m_pipeline->colorDescription().transferFunction());
|
||||
}
|
||||
m_colorPipeline = pipeline;
|
||||
// kernel documentation says that
|
||||
|
|
|
@ -185,16 +185,12 @@ bool EglGbmLayerSurface::endRendering(const QRegion &damagedRegion, OutputFrame
|
|||
if (m_surface->iccShader) {
|
||||
m_surface->iccShader->setUniforms(m_surface->iccProfile, m_surface->intermediaryColorDescription, m_surface->channelFactors);
|
||||
} else {
|
||||
binder.shader()->setColorspaceUniforms(m_surface->intermediaryColorDescription, m_surface->targetColorDescription);
|
||||
QMatrix4x4 ctm;
|
||||
ctm(0, 0) = m_surface->channelFactors.x();
|
||||
ctm(1, 1) = m_surface->channelFactors.y();
|
||||
ctm(2, 2) = m_surface->channelFactors.z();
|
||||
binder.shader()->setUniform(GLShader::Mat4Uniform::ColorimetryTransformation, ctm);
|
||||
binder.shader()->setUniform(GLShader::IntUniform::SourceNamedTransferFunction, m_surface->intermediaryColorDescription.transferFunction().type);
|
||||
binder.shader()->setUniform(GLShader::IntUniform::DestinationNamedTransferFunction, m_surface->targetColorDescription.transferFunction().type);
|
||||
binder.shader()->setUniform(GLShader::FloatUniform::SourceReferenceLuminance, m_surface->intermediaryColorDescription.referenceLuminance());
|
||||
binder.shader()->setUniform(GLShader::FloatUniform::DestinationReferenceLuminance, m_surface->intermediaryColorDescription.referenceLuminance());
|
||||
binder.shader()->setUniform(GLShader::FloatUniform::MaxDestinationLuminance, m_surface->intermediaryColorDescription.maxHdrLuminance().value_or(800));
|
||||
}
|
||||
QMatrix4x4 mat;
|
||||
mat.scale(1, -1);
|
||||
|
|
|
@ -481,9 +481,9 @@ bool DrmOutput::doSetChannelFactors(const QVector3D &rgb)
|
|||
// TODO this doesn't allow using only a CTM for night light offloading
|
||||
// maybe relax correctness in that case and apply night light in non-linear space?
|
||||
ColorPipeline pipeline{ValueRange{}};
|
||||
pipeline.addTransferFunction(m_state.colorDescription.transferFunction(), m_state.colorDescription.referenceLuminance());
|
||||
pipeline.addTransferFunction(m_state.colorDescription.transferFunction());
|
||||
pipeline.addMultiplier(rgb);
|
||||
pipeline.addInverseTransferFunction(m_state.colorDescription.transferFunction(), m_state.colorDescription.referenceLuminance());
|
||||
pipeline.addInverseTransferFunction(m_state.colorDescription.transferFunction());
|
||||
m_pipeline->setCrtcColorPipeline(pipeline);
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test) == DrmPipeline::Error::None) {
|
||||
m_pipeline->applyPendingChanges();
|
||||
|
|
|
@ -172,9 +172,9 @@ DrmPipeline::Error DrmPipeline::setLegacyGamma()
|
|||
QVector3D output = QVector3D(input, input, input);
|
||||
for (const auto &op : m_pending.crtcColorPipeline.ops) {
|
||||
if (auto tf = std::get_if<ColorTransferFunction>(&op.operation)) {
|
||||
output = tf->tf.encodedToNits(output, tf->referenceLuminance);
|
||||
output = tf->tf.encodedToNits(output);
|
||||
} else if (auto tf = std::get_if<InverseColorTransferFunction>(&op.operation)) {
|
||||
output = tf->tf.nitsToEncoded(output, tf->referenceLuminance);
|
||||
output = tf->tf.nitsToEncoded(output);
|
||||
} else if (auto mult = std::get_if<ColorMultiplier>(&op.operation)) {
|
||||
output *= mult->factors;
|
||||
} else {
|
||||
|
|
|
@ -26,8 +26,6 @@ uniform sampler3D Csampler;
|
|||
uniform int Asize;
|
||||
uniform sampler2D Asampler;
|
||||
|
||||
uniform float referenceLuminance;
|
||||
|
||||
vec3 sample1DLut(vec3 input, sampler2D lut, int lutSize) {
|
||||
float lutOffset = 0.5 / float(lutSize);
|
||||
float lutScale = 1.0 - lutOffset * 2.0;
|
||||
|
@ -40,10 +38,7 @@ vec3 sample1DLut(vec3 input, sampler2D lut, int lutSize) {
|
|||
void main()
|
||||
{
|
||||
vec4 tex = texture2D(src, texcoord0);
|
||||
tex = encodingToNits(tex, sourceNamedTransferFunction, referenceLuminance);
|
||||
tex.rgb /= max(tex.a, 0.001);
|
||||
tex.rgb /= referenceLuminance;
|
||||
tex.rgb = (toXYZD50 * vec4(tex.rgb, 1.0)).rgb;
|
||||
tex = sourceEncodingToNitsInDestinationColorspace(tex) / destinationReferenceLuminance;
|
||||
if (Bsize > 0) {
|
||||
tex.rgb = sample1DLut(tex.rgb, Bsampler, Bsize);
|
||||
}
|
||||
|
|
|
@ -29,8 +29,6 @@ uniform sampler3D Csampler;
|
|||
uniform int Asize;
|
||||
uniform sampler2D Asampler;
|
||||
|
||||
uniform float referenceLuminance;
|
||||
|
||||
vec3 sample1DLut(in vec3 srcColor, in sampler2D lut, in int lutSize) {
|
||||
float lutOffset = 0.5 / float(lutSize);
|
||||
float lutScale = 1.0 - lutOffset * 2.0;
|
||||
|
@ -43,10 +41,7 @@ vec3 sample1DLut(in vec3 srcColor, in sampler2D lut, in int lutSize) {
|
|||
void main()
|
||||
{
|
||||
vec4 tex = texture(src, texcoord0);
|
||||
tex = encodingToNits(tex, sourceNamedTransferFunction,referenceLuminance);
|
||||
tex.rgb /= max(tex.a, 0.001);
|
||||
tex.rgb /= referenceLuminance;
|
||||
tex.rgb = (toXYZD50 * vec4(tex.rgb, 1.0)).rgb;
|
||||
tex = sourceEncodingToNitsInDestinationColorspace(tex) / destinationReferenceLuminance;
|
||||
if (Bsize > 0) {
|
||||
tex.rgb = sample1DLut(tex.rgb, Bsampler, Bsize);
|
||||
}
|
||||
|
|
|
@ -22,8 +22,6 @@ IccShader::IccShader()
|
|||
{
|
||||
m_locations = {
|
||||
.src = m_shader->uniformLocation("src"),
|
||||
.sourceNamedTransferFunction = m_shader->uniformLocation("sourceNamedTransferFunction"),
|
||||
.referenceLuminance = m_shader->uniformLocation("referenceLuminance"),
|
||||
.toXYZD50 = m_shader->uniformLocation("toXYZD50"),
|
||||
.bsize = m_shader->uniformLocation("Bsize"),
|
||||
.bsampler = m_shader->uniformLocation("Bsampler"),
|
||||
|
@ -156,8 +154,11 @@ void IccShader::setUniforms(const std::shared_ptr<IccProfile> &profile, const Co
|
|||
nightColor(1, 1) = channelFactors.y();
|
||||
nightColor(2, 2) = channelFactors.z();
|
||||
m_shader->setUniform(m_locations.toXYZD50, m_toXYZD50 * nightColor);
|
||||
m_shader->setUniform(m_locations.sourceNamedTransferFunction, inputColor.transferFunction().type);
|
||||
m_shader->setUniform(m_locations.referenceLuminance, inputColor.referenceLuminance());
|
||||
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) {
|
||||
|
|
|
@ -43,8 +43,6 @@ private:
|
|||
struct Locations
|
||||
{
|
||||
int src;
|
||||
int sourceNamedTransferFunction;
|
||||
int referenceLuminance;
|
||||
int toXYZD50;
|
||||
int bsize;
|
||||
int bsampler;
|
||||
|
|
|
@ -47,9 +47,9 @@ bool X11Output::setChannelFactors(const QVector3D &rgb)
|
|||
return true;
|
||||
}
|
||||
ColorPipeline pipeline;
|
||||
pipeline.addTransferFunction(TransferFunction::gamma22, 1);
|
||||
pipeline.addTransferFunction(TransferFunction::gamma22);
|
||||
pipeline.addMultiplier(rgb);
|
||||
pipeline.addInverseTransferFunction(TransferFunction::gamma22, 1);
|
||||
pipeline.addInverseTransferFunction(TransferFunction::gamma22);
|
||||
std::vector<uint16_t> red(m_gammaRampSize);
|
||||
std::vector<uint16_t> green(m_gammaRampSize);
|
||||
std::vector<uint16_t> blue(m_gammaRampSize);
|
||||
|
|
|
@ -55,7 +55,7 @@ void ColorDevicePrivate::recalculateFactors()
|
|||
blackbodyColor[blackBodyColorIndex + 5],
|
||||
blendFactor);
|
||||
// the values in the blackbodyColor array are "gamma corrected", but we need a linear value
|
||||
temperatureFactors = TransferFunction(TransferFunction::gamma22).encodedToNits(QVector3D(xWhitePoint, yWhitePoint, zWhitePoint), 1);
|
||||
temperatureFactors = TransferFunction(TransferFunction::gamma22, 0, 1).encodedToNits(QVector3D(xWhitePoint, yWhitePoint, zWhitePoint));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,17 +15,17 @@ ColorPipeline ColorPipeline::create(const ColorDescription &from, const ColorDes
|
|||
{
|
||||
const auto range1 = ValueRange(from.minLuminance(), from.maxHdrLuminance().value_or(from.referenceLuminance()));
|
||||
ColorPipeline ret(ValueRange{
|
||||
.min = from.transferFunction().nitsToEncoded(range1.min, from.referenceLuminance()),
|
||||
.max = from.transferFunction().nitsToEncoded(range1.max, from.referenceLuminance()),
|
||||
.min = from.transferFunction().nitsToEncoded(range1.min),
|
||||
.max = from.transferFunction().nitsToEncoded(range1.max),
|
||||
});
|
||||
ret.addTransferFunction(from.transferFunction(), from.referenceLuminance());
|
||||
ret.addTransferFunction(from.transferFunction());
|
||||
ret.addMultiplier(to.referenceLuminance() / from.referenceLuminance());
|
||||
|
||||
// FIXME this assumes that the range stays the same with matrix multiplication
|
||||
// that's not necessarily true, and figuring out the actual range could be complicated..
|
||||
ret.addMatrix(from.containerColorimetry().toOther(to.containerColorimetry()), ret.currentOutputRange());
|
||||
|
||||
ret.addInverseTransferFunction(to.transferFunction(), to.referenceLuminance());
|
||||
ret.addInverseTransferFunction(to.transferFunction());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -77,11 +77,13 @@ void ColorPipeline::addMultiplier(const QVector3D &factors)
|
|||
return;
|
||||
} else if (factors.x() == factors.y() && factors.y() == factors.z()) {
|
||||
if (const auto tf = std::get_if<ColorTransferFunction>(lastOp); tf && tf->tf.isRelative()) {
|
||||
tf->referenceLuminance *= factors.x();
|
||||
tf->tf.minLuminance *= factors.x();
|
||||
tf->tf.maxLuminance *= factors.x();
|
||||
ops.back().output = output;
|
||||
return;
|
||||
} else if (const auto tf = std::get_if<InverseColorTransferFunction>(lastOp); tf && tf->tf.isRelative()) {
|
||||
tf->referenceLuminance /= factors.x();
|
||||
tf->tf.minLuminance /= factors.x();
|
||||
tf->tf.maxLuminance /= factors.x();
|
||||
ops.back().output = output;
|
||||
return;
|
||||
}
|
||||
|
@ -94,59 +96,61 @@ void ColorPipeline::addMultiplier(const QVector3D &factors)
|
|||
});
|
||||
}
|
||||
|
||||
void ColorPipeline::addTransferFunction(TransferFunction tf, double referenceLuminance)
|
||||
void ColorPipeline::addTransferFunction(TransferFunction tf)
|
||||
{
|
||||
if (tf == TransferFunction::linear) {
|
||||
return;
|
||||
}
|
||||
if (!ops.empty()) {
|
||||
if (const auto otherTf = std::get_if<InverseColorTransferFunction>(&ops.back().operation)) {
|
||||
if (otherTf->tf == tf) {
|
||||
const double reference = otherTf->referenceLuminance;
|
||||
if (const auto invTf = std::get_if<InverseColorTransferFunction>(&ops.back().operation)) {
|
||||
if (invTf->tf == tf) {
|
||||
ops.erase(ops.end() - 1);
|
||||
addMultiplier(referenceLuminance / reference);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tf == TransferFunction::scRGB) {
|
||||
addMultiplier(80.0);
|
||||
if (tf == TransferFunction::linear) {
|
||||
QMatrix4x4 mat;
|
||||
mat.translate(tf.minLuminance, tf.minLuminance, tf.minLuminance);
|
||||
mat.scale(tf.maxLuminance - tf.minLuminance);
|
||||
addMatrix(mat, ValueRange{
|
||||
.min = (mat * QVector3D(currentOutputRange().min, 0, 0)).x(),
|
||||
.max = (mat * QVector3D(currentOutputRange().max, 0, 0)).x(),
|
||||
});
|
||||
} else {
|
||||
ops.push_back(ColorOp{
|
||||
.input = currentOutputRange(),
|
||||
.operation = ColorTransferFunction(tf, referenceLuminance),
|
||||
.operation = ColorTransferFunction(tf),
|
||||
.output = ValueRange{
|
||||
.min = tf.encodedToNits(currentOutputRange().min, referenceLuminance),
|
||||
.max = tf.encodedToNits(currentOutputRange().max, referenceLuminance),
|
||||
.min = tf.encodedToNits(currentOutputRange().min),
|
||||
.max = tf.encodedToNits(currentOutputRange().max),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ColorPipeline::addInverseTransferFunction(TransferFunction tf, double referenceLuminance)
|
||||
void ColorPipeline::addInverseTransferFunction(TransferFunction tf)
|
||||
{
|
||||
if (tf == TransferFunction::linear) {
|
||||
return;
|
||||
}
|
||||
if (!ops.empty()) {
|
||||
if (const auto otherTf = std::get_if<ColorTransferFunction>(&ops.back().operation)) {
|
||||
if (otherTf->tf == tf) {
|
||||
const double reference = otherTf->referenceLuminance;
|
||||
ops.erase(ops.end() - 1);
|
||||
addMultiplier(reference / referenceLuminance);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tf == TransferFunction::scRGB) {
|
||||
addMultiplier(1.0 / 80.0);
|
||||
if (tf == TransferFunction::linear) {
|
||||
QMatrix4x4 mat;
|
||||
mat.scale(1.0 / (tf.maxLuminance - tf.minLuminance));
|
||||
mat.translate(-tf.minLuminance, -tf.minLuminance, -tf.minLuminance);
|
||||
addMatrix(mat, ValueRange{
|
||||
.min = (mat * QVector3D(currentOutputRange().min, 0, 0)).x(),
|
||||
.max = (mat * QVector3D(currentOutputRange().max, 0, 0)).x(),
|
||||
});
|
||||
} else {
|
||||
ops.push_back(ColorOp{
|
||||
.input = currentOutputRange(),
|
||||
.operation = InverseColorTransferFunction(tf, referenceLuminance),
|
||||
.operation = InverseColorTransferFunction(tf),
|
||||
.output = ValueRange{
|
||||
.min = tf.nitsToEncoded(currentOutputRange().min, referenceLuminance),
|
||||
.max = tf.nitsToEncoded(currentOutputRange().max, referenceLuminance),
|
||||
.min = tf.nitsToEncoded(currentOutputRange().min),
|
||||
.max = tf.nitsToEncoded(currentOutputRange().max),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -168,6 +172,24 @@ static bool isFuzzyIdentity(const QMatrix4x4 &mat)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool isFuzzyScalingOnly(const QMatrix4x4 &mat)
|
||||
{
|
||||
// matrix calculations with floating point numbers can result in very small errors
|
||||
// -> ignore them, as that just causes inefficiencies and more rounding errors
|
||||
constexpr float maxResolution = 0.0000001;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
if (i < 3 && i == j) {
|
||||
continue;
|
||||
}
|
||||
if (std::abs(mat(i, j)) > maxResolution) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ColorPipeline::addMatrix(const QMatrix4x4 &mat, const ValueRange &output)
|
||||
{
|
||||
if (isFuzzyIdentity(mat)) {
|
||||
|
@ -190,6 +212,11 @@ void ColorPipeline::addMatrix(const QMatrix4x4 &mat, const ValueRange &output)
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (isFuzzyScalingOnly(mat)) {
|
||||
// pure scaling, this can be simplified
|
||||
addMultiplier(QVector3D(mat(0, 0), mat(1, 1), mat(2, 2)));
|
||||
return;
|
||||
}
|
||||
ops.push_back(ColorOp{
|
||||
.input = currentOutputRange(),
|
||||
.operation = ColorMatrix(mat),
|
||||
|
@ -209,9 +236,9 @@ void ColorPipeline::add(const ColorOp &op)
|
|||
} else if (const auto mult = std::get_if<ColorMultiplier>(&op.operation)) {
|
||||
addMultiplier(mult->factors);
|
||||
} else if (const auto tf = std::get_if<ColorTransferFunction>(&op.operation)) {
|
||||
addTransferFunction(tf->tf, tf->referenceLuminance);
|
||||
addTransferFunction(tf->tf);
|
||||
} else if (const auto tf = std::get_if<InverseColorTransferFunction>(&op.operation)) {
|
||||
addInverseTransferFunction(tf->tf, tf->referenceLuminance);
|
||||
addInverseTransferFunction(tf->tf);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,23 +261,21 @@ QVector3D ColorPipeline::evaluate(const QVector3D &input) const
|
|||
} else if (const auto mult = std::get_if<ColorMultiplier>(&op.operation)) {
|
||||
ret *= mult->factors;
|
||||
} else if (const auto tf = std::get_if<ColorTransferFunction>(&op.operation)) {
|
||||
ret = tf->tf.encodedToNits(ret, tf->referenceLuminance);
|
||||
ret = tf->tf.encodedToNits(ret);
|
||||
} else if (const auto tf = std::get_if<InverseColorTransferFunction>(&op.operation)) {
|
||||
ret = tf->tf.nitsToEncoded(ret, tf->referenceLuminance);
|
||||
ret = tf->tf.nitsToEncoded(ret);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ColorTransferFunction::ColorTransferFunction(TransferFunction tf, double referenceLLuminance)
|
||||
ColorTransferFunction::ColorTransferFunction(TransferFunction tf)
|
||||
: tf(tf)
|
||||
, referenceLuminance(referenceLLuminance)
|
||||
{
|
||||
}
|
||||
|
||||
InverseColorTransferFunction::InverseColorTransferFunction(TransferFunction tf, double referenceLLuminance)
|
||||
InverseColorTransferFunction::InverseColorTransferFunction(TransferFunction tf)
|
||||
: tf(tf)
|
||||
, referenceLuminance(referenceLLuminance)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -25,23 +25,21 @@ public:
|
|||
class KWIN_EXPORT ColorTransferFunction
|
||||
{
|
||||
public:
|
||||
explicit ColorTransferFunction(TransferFunction tf, double referenceLuminance);
|
||||
explicit ColorTransferFunction(TransferFunction tf);
|
||||
|
||||
bool operator==(const ColorTransferFunction &) const = default;
|
||||
|
||||
TransferFunction tf;
|
||||
double referenceLuminance;
|
||||
};
|
||||
|
||||
class KWIN_EXPORT InverseColorTransferFunction
|
||||
{
|
||||
public:
|
||||
explicit InverseColorTransferFunction(TransferFunction tf, double referenceLuminance);
|
||||
explicit InverseColorTransferFunction(TransferFunction tf);
|
||||
|
||||
bool operator==(const InverseColorTransferFunction &) const = default;
|
||||
|
||||
TransferFunction tf;
|
||||
double referenceLuminance;
|
||||
};
|
||||
|
||||
class KWIN_EXPORT ColorMatrix
|
||||
|
@ -92,8 +90,8 @@ public:
|
|||
|
||||
void addMultiplier(double factor);
|
||||
void addMultiplier(const QVector3D &factors);
|
||||
void addTransferFunction(TransferFunction tf, double referenceLuminance);
|
||||
void addInverseTransferFunction(TransferFunction tf, double referenceLuminance);
|
||||
void addTransferFunction(TransferFunction tf);
|
||||
void addInverseTransferFunction(TransferFunction tf);
|
||||
void addMatrix(const QMatrix4x4 &mat, const ValueRange &output);
|
||||
void add(const ColorOp &op);
|
||||
|
||||
|
|
|
@ -198,7 +198,7 @@ const Colorimetry &Colorimetry::fromName(NamedColorimetry name)
|
|||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
const ColorDescription ColorDescription::sRGB = ColorDescription(NamedColorimetry::BT709, TransferFunction::gamma22, 100, 0, 100, 100);
|
||||
const ColorDescription ColorDescription::sRGB = ColorDescription(NamedColorimetry::BT709, TransferFunction::gamma22, TransferFunction::defaultReferenceLuminanceFor(TransferFunction::gamma22), TransferFunction::defaultMinLuminanceFor(TransferFunction::gamma22), TransferFunction::defaultMaxLuminanceFor(TransferFunction::gamma22), TransferFunction::defaultMaxLuminanceFor(TransferFunction::gamma22));
|
||||
|
||||
ColorDescription::ColorDescription(const Colorimetry &containerColorimetry, TransferFunction tf, double referenceLuminance, double minLuminance, std::optional<double> maxAverageLuminance, std::optional<double> maxHdrLuminance)
|
||||
: ColorDescription(containerColorimetry, tf, referenceLuminance, minLuminance, maxAverageLuminance, maxHdrLuminance, std::nullopt, Colorimetry::fromName(NamedColorimetry::BT709))
|
||||
|
@ -267,105 +267,135 @@ std::optional<double> ColorDescription::maxHdrLuminance() const
|
|||
return m_maxHdrLuminance;
|
||||
}
|
||||
|
||||
static float srgbToLinear(float sRGB)
|
||||
{
|
||||
if (sRGB < 0.04045) {
|
||||
return std::max(sRGB / 12.92, 0.0);
|
||||
} else {
|
||||
return std::clamp(std::pow((sRGB + 0.055) / 1.055, 12.0 / 5.0), 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
static float linearToSRGB(float linear)
|
||||
{
|
||||
if (linear < 0.0031308) {
|
||||
return std::max(linear / 12.92, 0.0);
|
||||
} else {
|
||||
return std::clamp(std::pow(linear, 5.0 / 12.0) * 1.055 - 0.055, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
static float nitsToPQ(float nits)
|
||||
{
|
||||
const float normalized = std::clamp(nits / 10000.0f, 0.0f, 1.0f);
|
||||
const float c1 = 0.8359375;
|
||||
const float c2 = 18.8515625;
|
||||
const float c3 = 18.6875;
|
||||
const float m1 = 0.1593017578125;
|
||||
const float m2 = 78.84375;
|
||||
const float powed = std::pow(normalized, m1);
|
||||
const float num = c1 + c2 * powed;
|
||||
const float denum = 1 + c3 * powed;
|
||||
return std::pow(num / denum, m2);
|
||||
}
|
||||
|
||||
static float pqToNits(float pq)
|
||||
{
|
||||
const float c1 = 0.8359375;
|
||||
const float c2 = 18.8515625;
|
||||
const float c3 = 18.6875;
|
||||
const float m1_inv = 1.0 / 0.1593017578125;
|
||||
const float m2_inv = 1.0 / 78.84375;
|
||||
const float powed = std::pow(pq, m2_inv);
|
||||
const float num = std::max(powed - c1, 0.0f);
|
||||
const float den = c2 - c3 * powed;
|
||||
return 10000.0f * std::pow(num / den, m1_inv);
|
||||
}
|
||||
|
||||
QVector3D ColorDescription::mapTo(QVector3D rgb, const ColorDescription &dst) const
|
||||
{
|
||||
rgb = m_transferFunction.encodedToNits(rgb, m_referenceLuminance);
|
||||
rgb = m_transferFunction.encodedToNits(rgb);
|
||||
rgb *= dst.referenceLuminance() / m_referenceLuminance;
|
||||
rgb = m_containerColorimetry.toOther(dst.containerColorimetry()) * rgb;
|
||||
return dst.transferFunction().nitsToEncoded(rgb, dst.referenceLuminance());
|
||||
return dst.transferFunction().nitsToEncoded(rgb);
|
||||
}
|
||||
|
||||
double TransferFunction::defaultMinLuminanceFor(Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case Type::sRGB:
|
||||
case Type::gamma22:
|
||||
return 0.01;
|
||||
case Type::linear:
|
||||
return 0;
|
||||
case Type::PerceptualQuantizer:
|
||||
return 0.005;
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
double TransferFunction::defaultMaxLuminanceFor(Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case Type::sRGB:
|
||||
case Type::gamma22:
|
||||
return 100;
|
||||
case Type::linear:
|
||||
return 1;
|
||||
case Type::PerceptualQuantizer:
|
||||
return 10'000;
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
double TransferFunction::defaultReferenceLuminanceFor(Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case Type::PerceptualQuantizer:
|
||||
return 203;
|
||||
case Type::linear:
|
||||
return 80;
|
||||
case Type::sRGB:
|
||||
case Type::gamma22:
|
||||
return 100;
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
TransferFunction::TransferFunction(Type tf)
|
||||
: TransferFunction(tf, defaultMinLuminanceFor(tf), defaultMaxLuminanceFor(tf))
|
||||
{
|
||||
}
|
||||
|
||||
TransferFunction::TransferFunction(Type tf, double minLuminance, double maxLuminance)
|
||||
: type(tf)
|
||||
, minLuminance(minLuminance)
|
||||
, maxLuminance(maxLuminance)
|
||||
{
|
||||
}
|
||||
|
||||
double TransferFunction::encodedToNits(double encoded, double referenceLuminance) const
|
||||
double TransferFunction::encodedToNits(double encoded) const
|
||||
{
|
||||
switch (type) {
|
||||
case TransferFunction::sRGB:
|
||||
return referenceLuminance * srgbToLinear(encoded);
|
||||
case TransferFunction::sRGB: {
|
||||
if (encoded < 0.04045) {
|
||||
return std::max(encoded / 12.92, 0.0) * (maxLuminance - minLuminance) + minLuminance;
|
||||
} else {
|
||||
return std::clamp(std::pow((encoded + 0.055) / 1.055, 12.0 / 5.0), 0.0, 1.0) * (maxLuminance - minLuminance) + minLuminance;
|
||||
}
|
||||
}
|
||||
case TransferFunction::gamma22:
|
||||
return referenceLuminance * std::pow(encoded, 2.2);
|
||||
return std::pow(encoded, 2.2) * (maxLuminance - minLuminance) + minLuminance;
|
||||
case TransferFunction::linear:
|
||||
return encoded;
|
||||
case TransferFunction::scRGB:
|
||||
return encoded * 80.0f;
|
||||
case TransferFunction::PerceptualQuantizer:
|
||||
return pqToNits(encoded);
|
||||
return encoded * (maxLuminance - minLuminance) + minLuminance;
|
||||
case TransferFunction::PerceptualQuantizer: {
|
||||
const double c1 = 0.8359375;
|
||||
const double c2 = 18.8515625;
|
||||
const double c3 = 18.6875;
|
||||
const double m1_inv = 1.0 / 0.1593017578125;
|
||||
const double m2_inv = 1.0 / 78.84375;
|
||||
const double powed = std::pow(encoded, m2_inv);
|
||||
const double num = std::max(powed - c1, 0.0);
|
||||
const double den = c2 - c3 * powed;
|
||||
return std::pow(num / den, m1_inv) * (maxLuminance - minLuminance) + minLuminance;
|
||||
}
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
QVector3D TransferFunction::encodedToNits(const QVector3D &encoded, double referenceLuminance) const
|
||||
QVector3D TransferFunction::encodedToNits(const QVector3D &encoded) const
|
||||
{
|
||||
return QVector3D(encodedToNits(encoded.x(), referenceLuminance), encodedToNits(encoded.y(), referenceLuminance), encodedToNits(encoded.z(), referenceLuminance));
|
||||
return QVector3D(encodedToNits(encoded.x()), encodedToNits(encoded.y()), encodedToNits(encoded.z()));
|
||||
}
|
||||
|
||||
double TransferFunction::nitsToEncoded(double nits, double referenceLuminance) const
|
||||
double TransferFunction::nitsToEncoded(double nits) const
|
||||
{
|
||||
const double normalized = (nits - minLuminance) / (maxLuminance - minLuminance);
|
||||
switch (type) {
|
||||
case TransferFunction::sRGB:
|
||||
return linearToSRGB(std::clamp(nits / referenceLuminance, 0.0, 1.0));
|
||||
case TransferFunction::sRGB: {
|
||||
if (normalized < 0.0031308) {
|
||||
return std::max(normalized / 12.92, 0.0);
|
||||
} else {
|
||||
return std::clamp(std::pow(normalized, 5.0 / 12.0) * 1.055 - 0.055, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
case TransferFunction::gamma22:
|
||||
return std::pow(std::clamp(nits / referenceLuminance, 0.0, 1.0), 1.0 / 2.2);
|
||||
return std::pow(std::clamp(normalized, 0.0, 1.0), 1.0 / 2.2);
|
||||
case TransferFunction::linear:
|
||||
return nits;
|
||||
case TransferFunction::scRGB:
|
||||
return nits / 80.0f;
|
||||
case TransferFunction::PerceptualQuantizer:
|
||||
return nitsToPQ(nits);
|
||||
return normalized;
|
||||
case TransferFunction::PerceptualQuantizer: {
|
||||
const double c1 = 0.8359375;
|
||||
const double c2 = 18.8515625;
|
||||
const double c3 = 18.6875;
|
||||
const double m1 = 0.1593017578125;
|
||||
const double m2 = 78.84375;
|
||||
const double powed = std::pow(std::clamp(normalized, 0.0, 1.0), m1);
|
||||
const double num = c1 + c2 * powed;
|
||||
const double denum = 1 + c3 * powed;
|
||||
return std::pow(num / denum, m2);
|
||||
}
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
QVector3D TransferFunction::nitsToEncoded(const QVector3D &nits, double referenceLuminance) const
|
||||
QVector3D TransferFunction::nitsToEncoded(const QVector3D &nits) const
|
||||
{
|
||||
return QVector3D(nitsToEncoded(nits.x(), referenceLuminance), nitsToEncoded(nits.y(), referenceLuminance), nitsToEncoded(nits.z(), referenceLuminance));
|
||||
return QVector3D(nitsToEncoded(nits.x()), nitsToEncoded(nits.y()), nitsToEncoded(nits.z()));
|
||||
}
|
||||
|
||||
bool TransferFunction::isRelative() const
|
||||
|
@ -376,7 +406,6 @@ bool TransferFunction::isRelative() const
|
|||
return true;
|
||||
case TransferFunction::linear:
|
||||
case TransferFunction::PerceptualQuantizer:
|
||||
case TransferFunction::scRGB:
|
||||
return false;
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
|
|
|
@ -94,21 +94,32 @@ public:
|
|||
sRGB = 0,
|
||||
linear = 1,
|
||||
PerceptualQuantizer = 2,
|
||||
scRGB = 3,
|
||||
gamma22 = 4,
|
||||
gamma22 = 3,
|
||||
};
|
||||
|
||||
TransferFunction(Type tf);
|
||||
explicit TransferFunction(Type tf, double minLuminance, double maxLuminance);
|
||||
|
||||
auto operator<=>(const TransferFunction &) const = default;
|
||||
|
||||
bool isRelative() const;
|
||||
double encodedToNits(double encoded, double referenceLuminance) const;
|
||||
double nitsToEncoded(double nits, double referenceLuminance) const;
|
||||
QVector3D encodedToNits(const QVector3D &encoded, double referenceLuminance) const;
|
||||
QVector3D nitsToEncoded(const QVector3D &nits, double referenceLuminance) const;
|
||||
double encodedToNits(double encoded) const;
|
||||
double nitsToEncoded(double nits) const;
|
||||
QVector3D encodedToNits(const QVector3D &encoded) const;
|
||||
QVector3D nitsToEncoded(const QVector3D &nits) const;
|
||||
|
||||
Type type;
|
||||
/**
|
||||
* the luminance at encoded value zero
|
||||
*/
|
||||
double minLuminance;
|
||||
/**
|
||||
* the luminance at encoded value 1
|
||||
*/
|
||||
double maxLuminance;
|
||||
|
||||
static double defaultMinLuminanceFor(Type type);
|
||||
static double defaultMaxLuminanceFor(Type type);
|
||||
static double defaultReferenceLuminanceFor(Type type);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,39 +1,50 @@
|
|||
const int sRGB_EOTF = 0;
|
||||
const int linear_EOTF = 1;
|
||||
const int PQ_EOTF = 2;
|
||||
const int scRGB_EOTF = 3;
|
||||
const int gamma22_EOTF = 4;
|
||||
const int gamma22_EOTF = 3;
|
||||
|
||||
uniform mat4 colorimetryTransform;
|
||||
|
||||
uniform int sourceNamedTransferFunction;
|
||||
/**
|
||||
* x: min luminance
|
||||
* y: max luminance - min luminance
|
||||
*/
|
||||
uniform vec2 sourceTransferFunctionParams;
|
||||
|
||||
uniform int destinationNamedTransferFunction;
|
||||
/**
|
||||
* x: min luminance
|
||||
* y: max luminance - min luminance
|
||||
*/
|
||||
uniform vec2 destinationTransferFunctionParams;
|
||||
|
||||
// in nits
|
||||
uniform float sourceReferenceLuminance;
|
||||
uniform float destinationReferenceLuminance;
|
||||
uniform float maxDestinationLuminance;
|
||||
|
||||
vec3 nitsToPq(vec3 nits) {
|
||||
vec3 normalized = clamp(nits / 10000.0, vec3(0), vec3(1));
|
||||
vec3 linearToPq(vec3 linear) {
|
||||
const float c1 = 0.8359375;
|
||||
const float c2 = 18.8515625;
|
||||
const float c3 = 18.6875;
|
||||
const float m1 = 0.1593017578125;
|
||||
const float m2 = 78.84375;
|
||||
vec3 powed = pow(normalized, vec3(m1));
|
||||
vec3 powed = pow(clamp(linear, vec3(0), vec3(1)), vec3(m1));
|
||||
vec3 num = vec3(c1) + c2 * powed;
|
||||
vec3 denum = vec3(1.0) + c3 * powed;
|
||||
return pow(num / denum, vec3(m2));
|
||||
}
|
||||
vec3 pqToNits(vec3 pq) {
|
||||
vec3 pqToLinear(vec3 pq) {
|
||||
const float c1 = 0.8359375;
|
||||
const float c2 = 18.8515625;
|
||||
const float c3 = 18.6875;
|
||||
const float m1_inv = 1.0 / 0.1593017578125;
|
||||
const float m2_inv = 1.0 / 78.84375;
|
||||
vec3 powed = pow(pq, vec3(m2_inv));
|
||||
vec3 powed = pow(clamp(pq, vec3(0.0), vec3(1.0)), vec3(m2_inv));
|
||||
vec3 num = max(powed - c1, vec3(0.0));
|
||||
vec3 den = c2 - c3 * powed;
|
||||
return 10000.0 * pow(num / den, vec3(m1_inv));
|
||||
return pow(num / den, vec3(m1_inv));
|
||||
}
|
||||
vec3 srgbToLinear(vec3 color) {
|
||||
bvec3 isLow = lessThanEqual(color, vec3(0.04045f));
|
||||
|
@ -62,51 +73,51 @@ vec3 doTonemapping(vec3 color, float maxBrightness) {
|
|||
return clamp(color.rgb, vec3(0.0), vec3(maxBrightness));
|
||||
}
|
||||
|
||||
vec4 encodingToNits(vec4 color, int sourceTransferFunction, float referenceLuminance) {
|
||||
vec4 encodingToNits(vec4 color, int sourceTransferFunction, float luminanceOffset, float luminanceScale) {
|
||||
if (sourceTransferFunction == sRGB_EOTF) {
|
||||
color.rgb /= max(color.a, 0.001);
|
||||
color.rgb = referenceLuminance * srgbToLinear(color.rgb);
|
||||
color.rgb = srgbToLinear(color.rgb) * luminanceScale + vec3(luminanceOffset);
|
||||
color.rgb *= color.a;
|
||||
} else if (sourceTransferFunction == linear_EOTF) {
|
||||
color.rgb = color.rgb * luminanceScale + vec3(luminanceOffset);
|
||||
} else if (sourceTransferFunction == PQ_EOTF) {
|
||||
color.rgb /= max(color.a, 0.001);
|
||||
color.rgb = pqToNits(color.rgb);
|
||||
color.rgb = pqToLinear(color.rgb) * luminanceScale + vec3(luminanceOffset);
|
||||
color.rgb *= color.a;
|
||||
} else if (sourceTransferFunction == scRGB_EOTF) {
|
||||
color.rgb *= 80.0;
|
||||
} else if (sourceTransferFunction == gamma22_EOTF) {
|
||||
color.rgb /= max(color.a, 0.001);
|
||||
color.rgb = referenceLuminance * pow(color.rgb, vec3(2.2));
|
||||
color.rgb = pow(max(color.rgb, vec3(0.0)), vec3(2.2)) * luminanceScale + vec3(luminanceOffset);
|
||||
color.rgb *= color.a;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
vec4 sourceEncodingToNitsInDestinationColorspace(vec4 color) {
|
||||
color = encodingToNits(color, sourceNamedTransferFunction, sourceReferenceLuminance);
|
||||
color = encodingToNits(color, sourceNamedTransferFunction, sourceTransferFunctionParams.x, sourceTransferFunctionParams.y);
|
||||
color.rgb = color.rgb * (destinationReferenceLuminance / sourceReferenceLuminance);
|
||||
color.rgb = (colorimetryTransform * vec4(color.rgb, 1.0)).rgb;
|
||||
return vec4(doTonemapping(color.rgb, maxDestinationLuminance), color.a);
|
||||
}
|
||||
|
||||
vec4 nitsToEncoding(vec4 color, int destinationTransferFunction, float referenceLuminance) {
|
||||
vec4 nitsToEncoding(vec4 color, int destinationTransferFunction, float luminanceOffset, float luminanceScale) {
|
||||
if (destinationTransferFunction == sRGB_EOTF) {
|
||||
color.rgb /= max(color.a, 0.001);
|
||||
color.rgb = linearToSrgb(doTonemapping(color.rgb, referenceLuminance) / referenceLuminance);
|
||||
color.rgb = linearToSrgb((color.rgb - vec3(luminanceOffset)) / luminanceScale);
|
||||
color.rgb *= color.a;
|
||||
} else if (destinationTransferFunction == linear_EOTF) {
|
||||
color.rgb = (color.rgb - vec3(luminanceOffset)) / luminanceScale;
|
||||
} else if (destinationTransferFunction == PQ_EOTF) {
|
||||
color.rgb /= max(color.a, 0.001);
|
||||
color.rgb = nitsToPq(color.rgb);
|
||||
color.rgb = linearToPq((color.rgb - vec3(luminanceOffset)) / luminanceScale);
|
||||
color.rgb *= color.a;
|
||||
} else if (destinationTransferFunction == scRGB_EOTF) {
|
||||
color.rgb /= 80.0;
|
||||
} else if (destinationTransferFunction == gamma22_EOTF) {
|
||||
color.rgb /= max(color.a, 0.001);
|
||||
color.rgb = pow(color.rgb / referenceLuminance, vec3(1.0 / 2.2));
|
||||
color.rgb = pow(max((color.rgb - vec3(luminanceOffset)) / luminanceScale, vec3(0.0)), vec3(1.0 / 2.2));
|
||||
color.rgb *= color.a;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
vec4 nitsToDestinationEncoding(vec4 color) {
|
||||
return nitsToEncoding(color, destinationNamedTransferFunction, destinationReferenceLuminance);
|
||||
return nitsToEncoding(color, destinationNamedTransferFunction, destinationTransferFunctionParams.x, destinationTransferFunctionParams.y);
|
||||
}
|
||||
|
|
|
@ -219,6 +219,8 @@ void GLShader::resolveLocations()
|
|||
m_matrix4Locations[Mat4Uniform::ColorimetryTransformation] = uniformLocation("colorimetryTransform");
|
||||
|
||||
m_vec2Locations[Vec2Uniform::Offset] = uniformLocation("offset");
|
||||
m_vec2Locations[Vec2Uniform::SourceTransferFunctionParams] = uniformLocation("sourceTransferFunctionParams");
|
||||
m_vec2Locations[Vec2Uniform::DestinationTransferFunctionParams] = uniformLocation("destinationTransferFunctionParams");
|
||||
|
||||
m_vec3Locations[Vec3Uniform::PrimaryBrightness] = uniformLocation("primaryBrightness");
|
||||
|
||||
|
@ -470,10 +472,12 @@ QMatrix4x4 GLShader::getUniformMatrix4x4(const char *name)
|
|||
bool GLShader::setColorspaceUniforms(const ColorDescription &src, const ColorDescription &dst)
|
||||
{
|
||||
const auto &srcColorimetry = src.containerColorimetry() == NamedColorimetry::BT709 ? dst.sdrColorimetry() : src.containerColorimetry();
|
||||
return setUniform(GLShader::Mat4Uniform::ColorimetryTransformation, srcColorimetry.toOther(dst.containerColorimetry()))
|
||||
&& setUniform(GLShader::IntUniform::SourceNamedTransferFunction, src.transferFunction().type)
|
||||
&& setUniform(GLShader::IntUniform::DestinationNamedTransferFunction, dst.transferFunction().type)
|
||||
return setUniform(Mat4Uniform::ColorimetryTransformation, srcColorimetry.toOther(dst.containerColorimetry()))
|
||||
&& setUniform(IntUniform::SourceNamedTransferFunction, src.transferFunction().type)
|
||||
&& setUniform(Vec2Uniform::SourceTransferFunctionParams, QVector2D(src.transferFunction().minLuminance, src.transferFunction().maxLuminance - src.transferFunction().minLuminance))
|
||||
&& setUniform(FloatUniform::SourceReferenceLuminance, src.referenceLuminance())
|
||||
&& setUniform(IntUniform::DestinationNamedTransferFunction, dst.transferFunction().type)
|
||||
&& setUniform(Vec2Uniform::DestinationTransferFunctionParams, QVector2D(dst.transferFunction().minLuminance, dst.transferFunction().maxLuminance - dst.transferFunction().minLuminance))
|
||||
&& setUniform(FloatUniform::DestinationReferenceLuminance, dst.referenceLuminance())
|
||||
&& setUniform(FloatUniform::MaxDestinationLuminance, dst.maxHdrLuminance().value_or(10'000));
|
||||
}
|
||||
|
|
|
@ -90,6 +90,8 @@ public:
|
|||
|
||||
enum class Vec2Uniform {
|
||||
Offset,
|
||||
SourceTransferFunctionParams,
|
||||
DestinationTransferFunctionParams,
|
||||
Vec2UniformCount
|
||||
};
|
||||
|
||||
|
|
|
@ -65,10 +65,9 @@ void ColorPickerEffect::paintScreen(const RenderTarget &renderTarget, const Rend
|
|||
std::array<float, 4> data;
|
||||
constexpr GLsizei PIXEL_SIZE = 1;
|
||||
const QPoint texturePosition = viewport.mapToRenderTarget(m_scheduledPosition).toPoint();
|
||||
const ColorDescription sRGBencoding(Colorimetry::fromName(NamedColorimetry::BT709), TransferFunction::gamma22, renderTarget.colorDescription().referenceLuminance(), 0, renderTarget.colorDescription().referenceLuminance(), renderTarget.colorDescription().referenceLuminance());
|
||||
|
||||
glReadPixels(texturePosition.x(), renderTarget.size().height() - texturePosition.y() - PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE, GL_RGBA, GL_FLOAT, data.data());
|
||||
QVector3D sRGB = 255 * renderTarget.colorDescription().mapTo(QVector3D(data[0], data[1], data[2]), sRGBencoding);
|
||||
QVector3D sRGB = 255 * renderTarget.colorDescription().mapTo(QVector3D(data[0], data[1], data[2]), ColorDescription::sRGB);
|
||||
QDBusConnection::sessionBus().send(m_replyMessage.createReply(QColor(sRGB.x(), sRGB.y(), sRGB.z())));
|
||||
setPicking(false);
|
||||
m_scheduledPosition = QPoint(-1, -1);
|
||||
|
|
|
@ -59,8 +59,6 @@ static QtWaylandServer::frog_color_managed_surface::transfer_function kwinToFrog
|
|||
return QtWaylandServer::frog_color_managed_surface::transfer_function_gamma_22;
|
||||
case TransferFunction::PerceptualQuantizer:
|
||||
return QtWaylandServer::frog_color_managed_surface::transfer_function_st2084_pq;
|
||||
case TransferFunction::scRGB:
|
||||
return QtWaylandServer::frog_color_managed_surface::transfer_function_scrgb_linear;
|
||||
case TransferFunction::linear:
|
||||
return QtWaylandServer::frog_color_managed_surface::transfer_function_scrgb_linear;
|
||||
}
|
||||
|
@ -97,7 +95,7 @@ void FrogColorManagementSurfaceV1::frog_color_managed_surface_set_known_transfer
|
|||
m_transferFunction = TransferFunction::PerceptualQuantizer;
|
||||
break;
|
||||
case transfer_function_scrgb_linear:
|
||||
m_transferFunction = TransferFunction::scRGB;
|
||||
m_transferFunction = TransferFunction(TransferFunction::linear, 0.0, 80.0);
|
||||
break;
|
||||
}
|
||||
updateColorDescription();
|
||||
|
|
|
@ -332,8 +332,6 @@ static uint32_t kwinTFtoProtoTF(TransferFunction tf)
|
|||
return xx_color_manager_v4_transfer_function::XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR;
|
||||
case TransferFunction::PerceptualQuantizer:
|
||||
return xx_color_manager_v4_transfer_function::XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ;
|
||||
case TransferFunction::scRGB:
|
||||
return xx_color_manager_v4_transfer_function::XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR;
|
||||
case TransferFunction::gamma22:
|
||||
return xx_color_manager_v4_transfer_function::XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue