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 }