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