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 }