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