File indexing completed on 2024-05-19 04:26:43

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