File indexing completed on 2024-05-12 15:59:35
0001 /* 0002 * SPDX-FileCopyrightText: 2006-2007 Cyrille Berger <cberger@cberger.net> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #ifndef _KO_COLORSPACE_TRAITS_H_ 0008 #define _KO_COLORSPACE_TRAITS_H_ 0009 0010 #include <QVector> 0011 #include <type_traits> 0012 0013 #include "KoColorSpaceConstants.h" 0014 #include "KoColorSpaceMaths.h" 0015 #include "DebugPigment.h" 0016 #include "kis_global.h" 0017 0018 const int MAX_CHANNELS_TYPE_SIZE = sizeof(double); 0019 const int MAX_CHANNELS_NB = 5; 0020 const int MAX_PIXEL_SIZE = MAX_CHANNELS_NB * MAX_CHANNELS_TYPE_SIZE; 0021 0022 /** 0023 * This class is the base class to define the main characteristics of a colorspace 0024 * which inherits KoColorSpaceAbstract. 0025 * 0026 * - _channels_type_ is the type of the value use for each channel, for example quint8 for 8bits per channel 0027 * color spaces, or quint16 for 16bits integer per channel, float for 32bits per channel 0028 * floating point color spaces 0029 * - _channels_nb_ is the total number of channels in an image (for example RGB is 3 but RGBA is four) 0030 * - _alpha_pos_ is the position of the alpha channel among the channels, if there is no alpha channel, 0031 * then _alpha_pos_ is set to -1 0032 * 0033 * For instance a colorspace of three color channels and alpha channel in 16bits, 0034 * will be defined as KoColorSpaceTrait\<quint16, 4, 3\>. The same without the alpha 0035 * channel is KoColorSpaceTrait\<quint16,3,-1\> 0036 * 0037 */ 0038 template<typename _channels_type_, int _channels_nb_, int _alpha_pos_> 0039 struct KoColorSpaceTrait { 0040 0041 static_assert(sizeof(_channels_type_) <= MAX_CHANNELS_TYPE_SIZE, "MAX_CHANNELS_TYPE_SIZE too small"); 0042 static_assert(_channels_nb_ <= MAX_CHANNELS_NB, "MAX_CHANNELS_NB too small"); 0043 0044 /// the type of the value of the channels of this color space 0045 typedef _channels_type_ channels_type; 0046 0047 /// the number of channels in this color space 0048 static const quint32 channels_nb = _channels_nb_; 0049 0050 /// the position of the alpha channel in the channels of the pixel (or -1 if no alpha 0051 /// channel. 0052 static const qint32 alpha_pos = _alpha_pos_; 0053 0054 /// the number of bit for each channel 0055 static const int depth = KoColorSpaceMathsTraits<_channels_type_>::bits; 0056 0057 /// the associated math class 0058 typedef KoColorSpaceMathsTraits<_channels_type_> math_trait; 0059 0060 /** 0061 * @return the size in byte of one pixel 0062 */ 0063 static const quint32 pixelSize = channels_nb * sizeof(channels_type); 0064 0065 /** 0066 * @return the value of the alpha channel for this pixel in the 0..255 range 0067 */ 0068 inline static quint8 opacityU8(const quint8 * U8_pixel) { 0069 if (alpha_pos < 0) return OPACITY_OPAQUE_U8; 0070 channels_type c = nativeArray(U8_pixel)[alpha_pos]; 0071 return KoColorSpaceMaths<channels_type, quint8>::scaleToA(c); 0072 } 0073 0074 inline static qreal opacityF(const quint8 * U8_pixel) { 0075 if (alpha_pos < 0) return OPACITY_OPAQUE_F; 0076 channels_type c = nativeArray(U8_pixel)[alpha_pos]; 0077 return KoColorSpaceMaths<channels_type, qreal>::scaleToA(c); 0078 } 0079 0080 /** 0081 * Set the alpha channel for this pixel from a value in the 0..255 range 0082 */ 0083 inline static void setOpacity(quint8 * pixels, quint8 alpha, qint32 nPixels) { 0084 if (alpha_pos < 0) return; 0085 qint32 psize = pixelSize; 0086 channels_type valpha = KoColorSpaceMaths<quint8, channels_type>::scaleToA(alpha); 0087 for (; nPixels > 0; --nPixels, pixels += psize) { 0088 nativeArray(pixels)[alpha_pos] = valpha; 0089 } 0090 } 0091 0092 inline static void setOpacity(quint8 * pixels, qreal alpha, qint32 nPixels) { 0093 if (alpha_pos < 0) return; 0094 qint32 psize = pixelSize; 0095 channels_type valpha = KoColorSpaceMaths<qreal, channels_type>::scaleToA(alpha); 0096 for (; nPixels > 0; --nPixels, pixels += psize) { 0097 nativeArray(pixels)[alpha_pos] = valpha; 0098 } 0099 } 0100 0101 /** 0102 * Copy alpha channel of all pixels in src to dst 0103 */ 0104 inline static void copyOpacityU8(quint8* src, quint8* dst, qint32 nPixels) { 0105 if (alpha_pos < 0) return; 0106 qint32 psize = pixelSize; 0107 for (; nPixels > 0; --nPixels, src += psize, dst++) { 0108 channels_type c = nativeArray(src)[alpha_pos]; 0109 *dst = KoColorSpaceMaths<channels_type, quint8>::scaleToA(c); 0110 } 0111 } 0112 0113 /** 0114 * Convenient function for transforming a quint8* array in a pointer of the native channels type 0115 */ 0116 inline static const channels_type* nativeArray(const quint8 * a) { 0117 return reinterpret_cast<const channels_type*>(a); 0118 } 0119 0120 /** 0121 * Convenient function for transforming a quint8* array in a pointer of the native channels type 0122 */ 0123 inline static channels_type* nativeArray(quint8 * a) { 0124 return reinterpret_cast< channels_type*>(a); 0125 } 0126 0127 /** 0128 * Allocate nPixels pixels for this colorspace. 0129 */ 0130 inline static quint8* allocate(quint32 nPixels) { 0131 return new quint8[ nPixels * pixelSize ]; 0132 } 0133 0134 inline static void singleChannelPixel(quint8 *dstPixel, const quint8 *srcPixel, quint32 channelIndex) { 0135 const channels_type* src = nativeArray(srcPixel); 0136 channels_type* dst = nativeArray(dstPixel); 0137 for (uint i = 0; i < channels_nb; i++) { 0138 if (i != channelIndex) { 0139 dst[i] = 0; 0140 } else { 0141 dst[i] = src[i]; 0142 } 0143 } 0144 } 0145 0146 inline static QString channelValueText(const quint8 *pixel, quint32 channelIndex) { 0147 if (channelIndex > channels_nb) return QString("Error"); 0148 channels_type c = nativeArray(pixel)[channelIndex]; 0149 return QString().setNum(c); 0150 } 0151 0152 inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) { 0153 if (channelIndex > channels_nb) return QString("Error"); 0154 channels_type c = nativeArray(pixel)[channelIndex]; 0155 return QString().setNum(100. *((qreal)c) / KoColorSpaceMathsTraits< channels_type>::unitValue); 0156 } 0157 0158 inline static void normalisedChannelsValue(const quint8 *pixel, QVector<float> &v) 0159 { 0160 return normalisedChannelsValueImpl<channels_type>(pixel, v); 0161 } 0162 0163 template<typename I, typename std::enable_if_t<std::numeric_limits<I>::is_integer, int> = 1> 0164 inline static void normalisedChannelsValueImpl(const quint8 *pixel, QVector<float> &v) 0165 { 0166 Q_ASSERT((int)v.count() >= (int)channels_nb); 0167 channels_type c; 0168 float *channels = v.data(); 0169 for (uint i = 0; i < channels_nb; i++) { 0170 c = nativeArray(pixel)[i]; 0171 channels[i] = ((float)c) / KoColorSpaceMathsTraits<channels_type>::unitValue; 0172 } 0173 } 0174 0175 template<typename I, typename std::enable_if_t<!std::numeric_limits<I>::is_integer, int> = 1> 0176 inline static void normalisedChannelsValueImpl(const quint8 *pixel, QVector<float> &v) 0177 { 0178 Q_ASSERT((int)v.count() >= (int)channels_nb); 0179 float *channels = v.data(); 0180 for (uint i = 0; i < channels_nb; i++) { 0181 channels[i] = float(nativeArray(pixel)[i]); 0182 } 0183 } 0184 0185 inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector<float> &values) 0186 { 0187 return fromNormalisedChannelsValueImpl<channels_type>(pixel, values); 0188 } 0189 0190 template<typename I, typename std::enable_if_t<std::numeric_limits<I>::is_integer, int> = 1> 0191 inline static void fromNormalisedChannelsValueImpl(quint8 *pixel, const QVector<float> &values) 0192 { 0193 Q_ASSERT((int)values.count() >= (int)channels_nb); 0194 channels_type c; 0195 const float *v = values.data(); 0196 for (uint i = 0; i < channels_nb; i++) { 0197 float b = qBound( 0198 (float)KoColorSpaceMathsTraits<channels_type>::min, 0199 (float)KoColorSpaceMathsTraits<channels_type>::unitValue * v[i], 0200 (float)KoColorSpaceMathsTraits<channels_type>::max); 0201 c = (channels_type)b; 0202 nativeArray(pixel)[i] = c; 0203 } 0204 } 0205 0206 template<typename I, typename std::enable_if_t<!std::numeric_limits<I>::is_integer, int> = 1> 0207 inline static void fromNormalisedChannelsValueImpl(quint8 *pixel, const QVector<float> &values) 0208 { 0209 Q_ASSERT((int)values.count() >= (int)channels_nb); 0210 const float *v = values.data(); 0211 for (uint i = 0; i < channels_nb; i++) { 0212 nativeArray(pixel)[i] = channels_type(float(KoColorSpaceMathsTraits<channels_type>::unitValue) * v[i]); 0213 } 0214 } 0215 0216 inline static void multiplyAlpha(quint8 * pixels, quint8 alpha, qint32 nPixels) { 0217 if (alpha_pos < 0) return; 0218 0219 channels_type valpha = KoColorSpaceMaths<quint8, channels_type>::scaleToA(alpha); 0220 0221 for (; nPixels > 0; --nPixels, pixels += pixelSize) { 0222 channels_type* alphapixel = nativeArray(pixels) + alpha_pos; 0223 *alphapixel = KoColorSpaceMaths<channels_type>::multiply(*alphapixel, valpha); 0224 } 0225 } 0226 0227 inline static void applyAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) { 0228 if (alpha_pos < 0) return; 0229 0230 for (; nPixels > 0; --nPixels, pixels += pixelSize, ++alpha) { 0231 channels_type valpha = KoColorSpaceMaths<quint8, channels_type>::scaleToA(*alpha); 0232 channels_type* alphapixel = nativeArray(pixels) + alpha_pos; 0233 *alphapixel = KoColorSpaceMaths<channels_type>::multiply(*alphapixel, valpha); 0234 } 0235 } 0236 0237 inline static void applyInverseAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) { 0238 if (alpha_pos < 0) return; 0239 0240 for (; nPixels > 0; --nPixels, pixels += pixelSize, ++alpha) { 0241 channels_type valpha = KoColorSpaceMaths<quint8, channels_type>::scaleToA(OPACITY_OPAQUE_U8 - *alpha); 0242 channels_type* alphapixel = nativeArray(pixels) + alpha_pos; 0243 *alphapixel = KoColorSpaceMaths<channels_type>::multiply(*alphapixel, valpha); 0244 } 0245 } 0246 0247 inline static void applyAlphaNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) { 0248 if (alpha_pos < 0) return; 0249 0250 for (; nPixels > 0; --nPixels, pixels += pixelSize, ++alpha) { 0251 channels_type valpha = channels_type(KoColorSpaceMathsTraits<channels_type>::unitValue * (*alpha)); 0252 channels_type* alphapixel = nativeArray(pixels) + alpha_pos; 0253 *alphapixel = KoColorSpaceMaths<channels_type>::multiply(*alphapixel, valpha); 0254 } 0255 } 0256 0257 inline static void applyInverseAlphaNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) { 0258 if (alpha_pos < 0) return; 0259 0260 for (; nPixels > 0; --nPixels, pixels += pixelSize, ++alpha) { 0261 channels_type valpha = channels_type(KoColorSpaceMathsTraits<channels_type>::unitValue * (1.0f - *alpha)); 0262 channels_type* alphapixel = nativeArray(pixels) + alpha_pos; 0263 *alphapixel = KoColorSpaceMaths<channels_type>::multiply(*alphapixel, valpha); 0264 } 0265 } 0266 0267 inline static void fillInverseAlphaNormedFloatMaskWithColor(quint8 * pixels, const float * alpha, const quint8 *brushColor, qint32 nPixels) { 0268 if (alpha_pos < 0) return; 0269 0270 for (; nPixels > 0; --nPixels, pixels += pixelSize, ++alpha) { 0271 memcpy(pixels, brushColor, pixelSize); 0272 channels_type valpha = channels_type(KoColorSpaceMathsTraits<channels_type>::unitValue * (1.0f - *alpha)); 0273 *(nativeArray(pixels) + alpha_pos) = valpha; 0274 } 0275 } 0276 0277 0278 inline static void fillGrayBrushWithColor(quint8 *pixels, const QRgb *brush, quint8 *brushColor, qint32 nPixels) { 0279 if (alpha_pos >= 0) { 0280 for (; nPixels > 0; --nPixels, pixels += pixelSize, ++brush) { 0281 memcpy(pixels, brushColor, pixelSize); 0282 const quint8 opacity = KoColorSpaceMaths<quint8>::multiply(OPACITY_OPAQUE_U8 - quint8(qRed(*brush)), quint8(qAlpha(*brush))); 0283 *(nativeArray(pixels) + alpha_pos) = KoColorSpaceMaths<quint8, channels_type>::scaleToA(opacity); 0284 } 0285 } else { 0286 for (; nPixels > 0; --nPixels, pixels += pixelSize) { 0287 memcpy(pixels, brushColor, pixelSize); 0288 } 0289 } 0290 } 0291 }; 0292 0293 #include "KoRgbColorSpaceTraits.h" 0294 #include "KoBgrColorSpaceTraits.h" 0295 #include "KoGrayColorSpaceTraits.h" 0296 #include "KoLabColorSpaceTraits.h" 0297 #include "KoXyzColorSpaceTraits.h" 0298 #include "KoYcbcrColorSpaceTraits.h" 0299 #include "KoCmykColorSpaceTraits.h" 0300 0301 #endif