File indexing completed on 2024-05-12 15:59:37
0001 /* 0002 * SPDX-FileCopyrightText: 2006-2007 Cyrille Berger <cberger@cberger.net> 0003 * SPDX-FileCopyrightText: 2016, 2017, 2020 L. E. Segovia <amy@amyspark.me> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #ifndef _KO_LAB_COLORSPACE_TRAITS_H_ 0009 #define _KO_LAB_COLORSPACE_TRAITS_H_ 0010 0011 #include <KoLabColorSpaceMaths.h> 0012 0013 /** 0014 * LAB traits, it provides some convenient functions to 0015 * access LAB channels through an explicit API. 0016 * 0017 * Use this class in conjunction with KoColorSpace::toLabA16 and 0018 * KoColorSpace::fromLabA16 data. 0019 * 0020 * Example: 0021 * quint8* p = KoLabU16Traits::allocate(1); 0022 * oneKoColorSpace->toLabA16(somepointertodata, p, 1); 0023 * KoLabU16Traits::setL( p, KoLabU16Traits::L(p) / 10 ); 0024 * oneKoColorSpace->fromLabA16(p, somepointertodata, 1); 0025 */ 0026 template<typename _channels_type_> 0027 struct KoLabTraits : public KoColorSpaceTrait<_channels_type_, 4, 3> { 0028 typedef _channels_type_ channels_type; 0029 typedef KoColorSpaceTrait<_channels_type_, 4, 3> parent; 0030 typedef KoLabColorSpaceMathsTraits<channels_type> math_trait; 0031 static const qint32 L_pos = 0; 0032 static const qint32 a_pos = 1; 0033 static const qint32 b_pos = 2; 0034 0035 /** 0036 * An Lab pixel 0037 */ 0038 struct Pixel { 0039 channels_type L; 0040 channels_type a; 0041 channels_type b; 0042 channels_type alpha; 0043 }; 0044 0045 /// @return the L component 0046 inline static channels_type L(quint8* data) { 0047 channels_type* d = parent::nativeArray(data); 0048 return d[L_pos]; 0049 } 0050 /// Set the L component 0051 inline static void setL(quint8* data, channels_type nv) { 0052 channels_type* d = parent::nativeArray(data); 0053 d[L_pos] = nv; 0054 } 0055 /// @return the a component 0056 inline static channels_type a(quint8* data) { 0057 channels_type* d = parent::nativeArray(data); 0058 return d[a_pos]; 0059 } 0060 /// Set the a component 0061 inline static void setA(quint8* data, channels_type nv) { 0062 channels_type* d = parent::nativeArray(data); 0063 d[a_pos] = nv; 0064 } 0065 /// @return the b component 0066 inline static channels_type b(quint8* data) { 0067 channels_type* d = parent::nativeArray(data); 0068 return d[b_pos]; 0069 } 0070 /// Set the a component 0071 inline static void setB(quint8* data, channels_type nv) { 0072 channels_type* d = parent::nativeArray(data); 0073 d[b_pos] = nv; 0074 } 0075 0076 // Lab has some... particulars 0077 inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) 0078 { 0079 if (channelIndex > parent::channels_nb) 0080 return QString("Error"); 0081 channels_type c = parent::nativeArray(pixel)[channelIndex]; 0082 switch (channelIndex) { 0083 case L_pos: 0084 return QString().setNum(100.0 * qBound((qreal)0, ((qreal)c) / math_trait::unitValueL, (qreal)math_trait::unitValueL)); 0085 case a_pos: 0086 case b_pos: 0087 if (c <= math_trait::halfValueAB) { 0088 return QString().setNum(100.0 * (qreal)((c - math_trait::zeroValueAB) / (2.0 * (math_trait::halfValueAB - math_trait::zeroValueAB)))); 0089 } else { 0090 return QString().setNum(100.0 * (qreal)(0.5 + (c - math_trait::halfValueAB) / (2.0 * (math_trait::unitValueAB - math_trait::halfValueAB)))); 0091 } 0092 case 3: 0093 return QString().setNum(100.0 * qBound((qreal)0, ((qreal)c) / math_trait::unitValue, (qreal)math_trait::unitValue)); 0094 default: 0095 return QString("Error"); 0096 } 0097 } 0098 inline static void normalisedChannelsValue(const quint8 *pixel, QVector<float> &v) 0099 { 0100 Q_ASSERT((int)v.count() >= (int)parent::channels_nb); 0101 channels_type c; 0102 float *channels = v.data(); 0103 for (uint i = 0; i < parent::channels_nb; i++) { 0104 c = parent::nativeArray(pixel)[i]; 0105 switch (i) { 0106 case L_pos: 0107 channels[i] = (qreal)c / math_trait::unitValueL; 0108 break; 0109 case a_pos: 0110 case b_pos: 0111 if (c <= math_trait::halfValueAB) { 0112 channels[i] = ((qreal)c - math_trait::zeroValueAB) / (2.0 * (math_trait::halfValueAB - math_trait::zeroValueAB)); 0113 } else { 0114 channels[i] = 0.5 + ((qreal)c - math_trait::halfValueAB) / (2.0 * (math_trait::unitValueAB - math_trait::halfValueAB)); 0115 } 0116 break; 0117 // As per KoChannelInfo alpha channels are [0..1] 0118 case 3: 0119 default: 0120 channels[i] = (qreal)c / math_trait::unitValue; 0121 break; 0122 } 0123 } 0124 } 0125 inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector<float> &values) 0126 { 0127 Q_ASSERT((int)values.count() >= (int)parent::channels_nb); 0128 channels_type c; 0129 for (uint i = 0; i < parent::channels_nb; i++) { 0130 float b = 0; 0131 switch (i) { 0132 case L_pos: 0133 b = qBound((float)math_trait::zeroValueL, 0134 (float)math_trait::unitValueL * values[i], 0135 (float)math_trait::unitValueL); 0136 break; 0137 case a_pos: 0138 case b_pos: 0139 if (values[i] <= 0.5) { 0140 b = qBound((float)math_trait::zeroValueAB, 0141 (float)(math_trait::zeroValueAB + 2.0 * values[i] * (math_trait::halfValueAB - math_trait::zeroValueAB)), 0142 (float)math_trait::halfValueAB); 0143 } 0144 else { 0145 b = qBound((float)math_trait::halfValueAB, 0146 (float)(math_trait::halfValueAB + 2.0 * (values[i] - 0.5) * (math_trait::unitValueAB - math_trait::halfValueAB)), 0147 (float)math_trait::unitValueAB); 0148 } 0149 break; 0150 case 3: 0151 b = qBound((float)math_trait::min, 0152 (float)math_trait::unitValue * values[i], 0153 (float)math_trait::unitValue); 0154 default: 0155 break; 0156 } 0157 c = (channels_type)b; 0158 parent::nativeArray(pixel)[i] = c; 0159 } 0160 } 0161 }; 0162 0163 //For quint* values must range from 0 to 1 - see KoColorSpaceMaths<double, quint*> 0164 0165 // https://github.com/mm2/Little-CMS/blob/master/src/cmspcs.c 0166 //PCS in Lab2 is encoded as: 0167 // 8 bit Lab PCS: 0168 // L* 0..100 into a 0..ff byte. 0169 // a* t + 128 range is -128.0 +127.0 0170 // b* 0171 // 16 bit Lab PCS: 0172 // L* 0..100 into a 0..ff00 word. 0173 // a* t + 128 range is -128.0 +127.9961 0174 // b* 0175 //Version 4 0176 //--------- 0177 //CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xffff 0178 //CIELAB (16 bit) a* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff 0179 //CIELAB (16 bit) b* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff 0180 0181 struct KoLabU8Traits : public KoLabTraits<quint8> { 0182 0183 }; 0184 0185 struct KoLabU16Traits : public KoLabTraits<quint16> { 0186 0187 }; 0188 0189 // Float values are normalized to [0..100], [-128..+127], [-128..+127] - out of range values are clipped 0190 0191 #include <KoConfig.h> 0192 #ifdef HAVE_OPENEXR 0193 #include <half.h> 0194 0195 struct KoLabF16Traits : public KoLabTraits<half> { 0196 0197 }; 0198 0199 #endif 0200 0201 struct KoLabF32Traits : public KoLabTraits<float> { 0202 0203 }; 0204 0205 struct KoLabF64Traits : public KoLabTraits<double> { 0206 0207 }; 0208 0209 #endif