File indexing completed on 2024-05-12 15:58:51

0001 /*
0002  * KDE. Krita Project.
0003  *
0004  * SPDX-FileCopyrightText: 2021 Deif Lou <ginoba@gmail.com>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include <cmath>
0010 
0011 #include <kis_dom_utils.h>
0012 
0013 #include "KisLevelsCurve.h"
0014 
0015 KisLevelsCurve::KisLevelsCurve()
0016     : KisLevelsCurve(defaultInputBlackPoint(), defaultInputWhitePoint(), defaultInputGamma(),
0017                      defaultOutputBlackPoint(), defaultOutputWhitePoint())
0018 {}
0019 
0020 KisLevelsCurve::KisLevelsCurve(qreal inputBlackPoint, qreal inputWhitePoint, qreal inputGamma,
0021                                qreal outputBlackPoint, qreal outputWhitePoint)
0022     : m_inputBlackPoint(inputBlackPoint)
0023     , m_inputWhitePoint(inputWhitePoint)
0024     , m_inputGamma(inputGamma)
0025     , m_outputBlackPoint(outputBlackPoint)
0026     , m_outputWhitePoint(outputWhitePoint)
0027     , m_inputLevelsDelta(inputWhitePoint - inputBlackPoint)
0028     , m_inverseInputGamma(1.0 / m_inputGamma)
0029     , m_outputLevelsDelta(outputWhitePoint - outputBlackPoint)
0030     , m_mustRecomputeU16Transfer(true)
0031     , m_mustRecomputeFTransfer(true)
0032 {}
0033                 
0034 bool KisLevelsCurve::operator==(const KisLevelsCurve& rhs) const
0035 {
0036     return
0037         &rhs == this ||
0038         (
0039             m_inputBlackPoint == rhs.m_inputBlackPoint &&
0040             m_inputWhitePoint == rhs.m_inputWhitePoint &&
0041             m_inputGamma == rhs.m_inputGamma &&
0042             m_outputBlackPoint == rhs.m_outputBlackPoint &&
0043             m_outputWhitePoint == rhs.m_outputWhitePoint
0044         );
0045 }
0046 
0047 qreal KisLevelsCurve::value(qreal x) const
0048 {
0049     if (x <= m_inputBlackPoint)
0050         return m_outputBlackPoint;
0051     else if (x < m_inputWhitePoint) {
0052         return m_outputBlackPoint + m_outputLevelsDelta * std::pow((x - m_inputBlackPoint) / m_inputLevelsDelta, m_inverseInputGamma);
0053     } else {
0054         return m_outputWhitePoint;
0055     }
0056 }
0057 
0058 qreal KisLevelsCurve::inputBlackPoint() const
0059 {
0060     return m_inputBlackPoint;
0061 }
0062 
0063 qreal KisLevelsCurve::inputWhitePoint() const
0064 {
0065     return m_inputWhitePoint;
0066 }
0067 
0068 qreal KisLevelsCurve::inputGamma() const
0069 {
0070     return m_inputGamma;
0071 }
0072 
0073 qreal KisLevelsCurve::outputBlackPoint() const
0074 {
0075     return m_outputBlackPoint;
0076 }
0077 
0078 qreal KisLevelsCurve::outputWhitePoint() const
0079 {
0080     return m_outputWhitePoint;
0081 }
0082 
0083 void KisLevelsCurve::setInputBlackPoint(qreal newInputBlackPoint)
0084 {
0085     m_inputBlackPoint = newInputBlackPoint;
0086     m_inputLevelsDelta = m_inputWhitePoint - m_inputBlackPoint;
0087     invalidate();
0088 }
0089 
0090 void KisLevelsCurve::setInputWhitePoint(qreal newInputWhitePoint)
0091 {
0092     m_inputWhitePoint = newInputWhitePoint;
0093     m_inputLevelsDelta = m_inputWhitePoint - m_inputBlackPoint;
0094     invalidate();
0095 }
0096 
0097 void KisLevelsCurve::setInputGamma(qreal newInputGamma)
0098 {
0099     m_inputGamma = newInputGamma;
0100     m_inverseInputGamma = 1.0 / m_inputGamma;
0101     invalidate();
0102 }
0103 
0104 void KisLevelsCurve::setOutputBlackPoint(qreal newOutputBlackPoint)
0105 {
0106     m_outputBlackPoint = newOutputBlackPoint;
0107     m_outputLevelsDelta = m_outputWhitePoint - m_outputBlackPoint;
0108     invalidate();
0109 }
0110 
0111 void KisLevelsCurve::setOutputWhitePoint(qreal newOutputWhitePoint)
0112 {
0113     m_outputWhitePoint = newOutputWhitePoint;
0114     m_outputLevelsDelta = m_outputWhitePoint - m_outputBlackPoint;
0115     invalidate();
0116 }
0117 
0118 void KisLevelsCurve::resetAll()
0119 {
0120     m_inputBlackPoint = defaultInputBlackPoint();
0121     m_inputWhitePoint = defaultInputWhitePoint();
0122     m_inputGamma = defaultInputGamma();
0123     m_outputBlackPoint = defaultOutputBlackPoint();
0124     m_outputWhitePoint = defaultOutputWhitePoint();
0125     m_inputLevelsDelta = m_inputWhitePoint - m_inputBlackPoint;
0126     m_inverseInputGamma = 1.0 / m_inputGamma;
0127     m_outputLevelsDelta = m_outputWhitePoint - m_outputBlackPoint;
0128     invalidate();
0129 }
0130 
0131 void KisLevelsCurve::resetInputLevels()
0132 {
0133     m_inputBlackPoint = defaultInputBlackPoint();
0134     m_inputWhitePoint = defaultInputWhitePoint();
0135     m_inputGamma = defaultInputGamma();
0136     m_inputLevelsDelta = m_inputWhitePoint - m_inputBlackPoint;
0137     m_inverseInputGamma = 1.0 / m_inputGamma;
0138     invalidate();
0139 }
0140 
0141 void KisLevelsCurve::resetOutputLevels()
0142 {
0143     m_outputBlackPoint = defaultOutputBlackPoint();
0144     m_outputWhitePoint = defaultOutputWhitePoint();
0145     m_outputLevelsDelta = m_outputWhitePoint - m_outputBlackPoint;
0146     invalidate();
0147 }
0148 
0149 bool KisLevelsCurve::isIdentity() const
0150 {
0151     return 
0152         m_inputBlackPoint == 0.0 &&
0153         m_inputWhitePoint == 1.0 &&
0154         m_inputGamma == 1.0 &&
0155         m_outputBlackPoint == 0.0 &&
0156         m_outputWhitePoint == 1.0;
0157 }
0158 
0159 const QString& KisLevelsCurve::name() const
0160 {
0161     return m_name;
0162 }
0163 
0164 void KisLevelsCurve::setName(const QString &newName)
0165 {
0166     m_name = newName;
0167 }
0168 
0169 const QVector<quint16>& KisLevelsCurve::uint16Transfer(int size) const
0170 {
0171 
0172     if (!m_mustRecomputeU16Transfer && size == m_u16Transfer.size()) {
0173         return m_u16Transfer;
0174     }
0175 
0176     m_u16Transfer.resize(size);
0177 
0178     for (int i = 0; i < size; ++i) {
0179         const qreal x = static_cast<qreal>(i) / static_cast<qreal>(size - 1);
0180         m_u16Transfer[i] = static_cast<quint16>(qRound(value(x) * static_cast<qreal>(0xFFFF)));
0181     }
0182 
0183     m_mustRecomputeU16Transfer = false;
0184     return m_u16Transfer;
0185 }
0186 
0187 const QVector<qreal>& KisLevelsCurve::floatTransfer(int size) const
0188 {
0189     if (!m_mustRecomputeFTransfer && size == m_fTransfer.size()) {
0190         return m_fTransfer;
0191     }
0192 
0193     m_fTransfer.resize(size);
0194 
0195     for (int i = 0; i < size; ++i) {
0196         m_fTransfer[i] = value(static_cast<qreal>(i) / static_cast<qreal>(size - 1));
0197     }
0198 
0199     m_mustRecomputeFTransfer = false;
0200     return m_fTransfer;
0201 }
0202 
0203 QString KisLevelsCurve::toString() const
0204 {
0205     return
0206         KisDomUtils::toString(m_inputBlackPoint) + ";" +
0207         KisDomUtils::toString(m_inputWhitePoint) + ";" +
0208         KisDomUtils::toString(m_inputGamma) + ";" +
0209         KisDomUtils::toString(m_outputBlackPoint) + ";" +
0210         KisDomUtils::toString(m_outputWhitePoint);
0211 }
0212 
0213 void KisLevelsCurve::fromString(const QString &text, bool *ok)
0214 {
0215     const QStringList data = text.split(';');
0216 
0217     if (data.size() != 5) {
0218         if (ok) {
0219             *ok = false;
0220         }
0221         return;
0222     }
0223 
0224     qreal values[5];
0225     bool ok_;
0226     for (int i = 0; i < 5; ++i) {
0227         ok_ = false;
0228         values[i] = KisDomUtils::toDouble(data.at(i), &ok_);
0229         if (!ok_) {
0230             if (ok) {
0231                 *ok = false;
0232             }
0233             return;
0234         }
0235     }
0236 
0237     m_inputBlackPoint = values[0];
0238     m_inputWhitePoint = values[1];
0239     m_inputGamma = values[2];
0240     m_outputBlackPoint = values[3];
0241     m_outputWhitePoint = values[4];
0242     m_inputLevelsDelta = m_inputWhitePoint - m_inputBlackPoint;
0243     m_inverseInputGamma = 1.0 / m_inputGamma;
0244     m_outputLevelsDelta = m_outputWhitePoint - m_outputBlackPoint;
0245     invalidate();
0246 
0247     if (ok) {
0248         *ok = true;
0249     }
0250 }
0251 
0252 void KisLevelsCurve::invalidate()
0253 {
0254     m_mustRecomputeU16Transfer = true;
0255     m_mustRecomputeFTransfer = true;
0256 }