File indexing completed on 2024-05-12 15:59:34

0001 /*
0002  *  SPDX-FileCopyrightText: 2006 Cyrille Berger <cberger@cberger.net>
0003  *  SPDX-FileCopyrightText: 2007 Emanuele Tamponi <emanuele@valinor.it>
0004  *  SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
0005  * SPDX-License-Identifier: LGPL-2.1-or-later
0006  */
0007 
0008 #ifndef KOCOLORSPACEABSTRACT_H
0009 #define KOCOLORSPACEABSTRACT_H
0010 
0011 #include <QBitArray>
0012 #include <klocalizedstring.h>
0013 
0014 #include <KoColorSpace.h>
0015 #include <KoColorProfile.h>
0016 #include <KoColorSpaceMaths.h>
0017 #include <KoColorSpaceRegistry.h>
0018 #include "KoFallBackColorTransformation.h"
0019 #include "KoLabDarkenColorTransformation.h"
0020 #include "KoMixColorsOpImpl.h"
0021 
0022 #include "KoConvolutionOpImpl.h"
0023 #include "KoInvertColorTransformation.h"
0024 #include "KoAlphaMaskApplicatorFactory.h"
0025 #include "KoColorModelStandardIdsUtils.h"
0026 
0027 /**
0028  * This in an implementation of KoColorSpace which can be used as a base for colorspaces with as many
0029  * different channels of the same type.
0030  *
0031  * The template parameters must be a class which inherits KoColorSpaceTrait (or a class with the same signature).
0032  *
0033  * SOMETYPE is the type of the channel for instance (quint8, quint32...),
0034  * SOMENBOFCHANNELS is the number of channels including the alpha channel
0035  * SOMEALPHAPOS is the position of the alpha channel in the pixel (can be equal to -1 if no alpha channel).
0036  */
0037 template<class _CSTrait>
0038 class KoColorSpaceAbstract : public KoColorSpace
0039 {
0040 public:
0041     typedef _CSTrait ColorSpaceTraits;
0042 
0043 public:
0044     KoColorSpaceAbstract(const QString &id, const QString &name)
0045         : KoColorSpace(id, name, new KoMixColorsOpImpl< _CSTrait>(), new KoConvolutionOpImpl< _CSTrait>()),
0046           m_alphaMaskApplicator(KoAlphaMaskApplicatorFactory::create(colorDepthIdForChannelType<typename _CSTrait::channels_type>(), _CSTrait::channels_nb, _CSTrait::alpha_pos))
0047     {
0048     }
0049 
0050     quint32 colorChannelCount() const override {
0051         if (_CSTrait::alpha_pos == -1)
0052             return _CSTrait::channels_nb;
0053         else
0054             return _CSTrait::channels_nb - 1;
0055     }
0056 
0057     quint32 channelCount() const override {
0058         return _CSTrait::channels_nb;
0059     }
0060 
0061     quint32 alphaPos() const override {
0062         return _CSTrait::alpha_pos;
0063     }
0064 
0065 
0066     quint32 pixelSize() const override {
0067         return _CSTrait::pixelSize;
0068     }
0069 
0070     QString channelValueText(const quint8 *pixel, quint32 channelIndex) const override {
0071         return _CSTrait::channelValueText(pixel, channelIndex);
0072     }
0073 
0074     QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) const override {
0075         return _CSTrait::normalisedChannelValueText(pixel, channelIndex);
0076     }
0077 
0078     void normalisedChannelsValue(const quint8 *pixel, QVector<float> &channels) const override {
0079         return _CSTrait::normalisedChannelsValue(pixel, channels);
0080     }
0081 
0082     void fromNormalisedChannelsValue(quint8 *pixel, const QVector<float> &values) const override {
0083         return _CSTrait::fromNormalisedChannelsValue(pixel, values);
0084     }
0085 
0086     quint8 scaleToU8(const quint8 * srcPixel, qint32 channelIndex) const override {
0087         typename _CSTrait::channels_type c = _CSTrait::nativeArray(srcPixel)[channelIndex];
0088         return KoColorSpaceMaths<typename _CSTrait::channels_type, quint8>::scaleToA(c);
0089     }
0090 
0091     void singleChannelPixel(quint8 *dstPixel, const quint8 *srcPixel, quint32 channelIndex) const override {
0092         _CSTrait::singleChannelPixel(dstPixel, srcPixel, channelIndex);
0093     }
0094 
0095     quint8 opacityU8(const quint8 * U8_pixel) const override {
0096         return _CSTrait::opacityU8(U8_pixel);
0097     }
0098 
0099     qreal opacityF(const quint8 * U8_pixel) const override {
0100         return _CSTrait::opacityF(U8_pixel);
0101     }
0102 
0103     void setOpacity(quint8 * pixels, quint8 alpha, qint32 nPixels) const override {
0104         _CSTrait::setOpacity(pixels, alpha, nPixels);
0105     }
0106 
0107     void setOpacity(quint8 * pixels, qreal alpha, qint32 nPixels) const override {
0108         _CSTrait::setOpacity(pixels, alpha, nPixels);
0109     }
0110 
0111     void copyOpacityU8(quint8* src, quint8 *dst, qint32 nPixels) const override {
0112         _CSTrait::copyOpacityU8(src, dst, nPixels);
0113     }
0114 
0115     void multiplyAlpha(quint8 * pixels, quint8 alpha, qint32 nPixels) const override {
0116         _CSTrait::multiplyAlpha(pixels, alpha, nPixels);
0117     }
0118 
0119     void applyAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) const override {
0120         _CSTrait::applyAlphaU8Mask(pixels, alpha, nPixels);
0121     }
0122 
0123     void applyInverseAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) const override {
0124         _CSTrait::applyInverseAlphaU8Mask(pixels, alpha, nPixels);
0125     }
0126 
0127     void applyAlphaNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) const override {
0128         _CSTrait::applyAlphaNormedFloatMask(pixels, alpha, nPixels);
0129     }
0130 
0131     void applyInverseNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) const override {
0132         m_alphaMaskApplicator->applyInverseNormedFloatMask(pixels, alpha, nPixels);
0133     }
0134 
0135     void fillInverseAlphaNormedFloatMaskWithColor(quint8 * pixels, const float * alpha, const quint8 *brushColor, qint32 nPixels) const override {
0136         m_alphaMaskApplicator->fillInverseAlphaNormedFloatMaskWithColor(pixels, alpha, brushColor, nPixels);
0137     }
0138 
0139     void fillGrayBrushWithColor(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override {
0140         m_alphaMaskApplicator->fillGrayBrushWithColor(dst, brush, brushColor, nPixels);
0141     }
0142 
0143     /**
0144      * By default this does the same as toQColor
0145      */
0146     void toQColor16(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const override {
0147         this->toQColor(src, c, profile);
0148     }
0149 
0150     quint8 intensity8(const quint8 * src) const override {
0151         QColor c;
0152         const_cast<KoColorSpaceAbstract<_CSTrait> *>(this)->toQColor(src, &c);
0153         // Integer version of:
0154         //      static_cast<quint8>(qRound(c.red() * 0.30 + c.green() * 0.59 + c.blue() * 0.11))
0155         // The "+ 50" is used for rounding
0156         return static_cast<quint8>((c.red() * 30 + c.green() * 59 + c.blue() * 11 + 50) / 100);
0157     }
0158 
0159     qreal intensityF(const quint8 * src) const override {
0160         QColor c;
0161         const_cast<KoColorSpaceAbstract<_CSTrait> *>(this)->toQColor16(src, &c);
0162         return c.redF() * 0.30 + c.greenF() * 0.59 + c.blueF() * 0.11;
0163     }
0164 
0165     KoColorTransformation* createInvertTransformation() const override {
0166         return KoInvertColorTransformation::getTransformator(this);
0167     }
0168 
0169     KoColorTransformation *createDarkenAdjustment(qint32 shade, bool compensate, qreal compensation) const override {
0170         return new KoFallBackColorTransformation(this, KoColorSpaceRegistry::instance()->lab16(""), new KoLabDarkenColorTransformation<quint16>(shade, compensate, compensation, KoColorSpaceRegistry::instance()->lab16("")));
0171     }
0172 
0173     bool convertPixelsTo(const quint8 *src,
0174                                  quint8 *dst, const KoColorSpace *dstColorSpace,
0175                                  quint32 numPixels,
0176                                  KoColorConversionTransformation::Intent renderingIntent,
0177                                  KoColorConversionTransformation::ConversionFlags conversionFlags) const override
0178     {
0179 
0180         // check whether we have the same profile and color model, but only a different bit
0181         // depth; in that case we don't convert as such, but scale
0182         bool scaleOnly = false;
0183 
0184         // Note: getting the id() is really, really expensive, so only do that if
0185         // we are sure there is a difference between the colorspaces
0186         if (!(*this == *dstColorSpace)) {
0187             scaleOnly = dstColorSpace->colorModelId().id() == colorModelId().id() &&
0188                          dstColorSpace->colorDepthId().id() != colorDepthId().id() &&
0189                          dstColorSpace->profile()->name()   == profile()->name();
0190         }
0191 
0192         if (scaleOnly && dynamic_cast<const KoColorSpaceAbstract*>(dstColorSpace)) {
0193             typedef typename _CSTrait::channels_type channels_type;
0194 
0195             switch(dstColorSpace->channels()[0]->channelValueType())
0196             {
0197             case KoChannelInfo::UINT8:
0198                 scalePixels<_CSTrait::pixelSize, 1, channels_type, quint8>(src, dst, numPixels);
0199                 return true;
0200 //             case KoChannelInfo::INT8:
0201 //                 scalePixels<_CSTrait::pixelSize, 1, channels_type, qint8>(src, dst, numPixels);
0202 //                 return true;
0203             case KoChannelInfo::UINT16:
0204                 scalePixels<_CSTrait::pixelSize, 2, channels_type, quint16>(src, dst, numPixels);
0205                 return true;
0206             case KoChannelInfo::INT16:
0207                 scalePixels<_CSTrait::pixelSize, 2, channels_type, qint16>(src, dst, numPixels);
0208                 return true;
0209             case KoChannelInfo::UINT32:
0210                 scalePixels<_CSTrait::pixelSize, 4, channels_type, quint32>(src, dst, numPixels);
0211                 return true;
0212             default:
0213                 break;
0214             }
0215         }
0216 
0217         return KoColorSpace::convertPixelsTo(src, dst, dstColorSpace, numPixels, renderingIntent, conversionFlags);
0218     }
0219 
0220     void convertChannelToVisualRepresentation(const quint8 *src, quint8 *dst, quint32 nPixels, const qint32 selectedChannelIndex) const override
0221     {
0222         qint32 selectedChannelPos = this->channels()[selectedChannelIndex]->pos();
0223         for (uint pixelIndex = 0; pixelIndex < nPixels; ++pixelIndex) {
0224             for (uint channelIndex = 0; channelIndex < this->channelCount(); ++channelIndex) {
0225                 KoChannelInfo *channel = this->channels().at(channelIndex);
0226                 qint32 channelSize = channel->size();
0227                 if (channel->channelType() == KoChannelInfo::COLOR) {
0228                     memcpy(dst + (pixelIndex * _CSTrait::pixelSize) + (channelIndex * channelSize), src + (pixelIndex * _CSTrait::pixelSize) + selectedChannelPos, channelSize);
0229                 } else if (channel->channelType() == KoChannelInfo::ALPHA) {
0230                     memcpy(dst + (pixelIndex * _CSTrait::pixelSize) + (channelIndex * channelSize), src + (pixelIndex * _CSTrait::pixelSize) + (channelIndex * channelSize), channelSize);
0231                 }
0232             }
0233         }
0234     }
0235 
0236     void convertChannelToVisualRepresentation(const quint8 *src, quint8 *dst, quint32 nPixels, const QBitArray selectedChannels) const override
0237     {
0238         for (uint pixelIndex = 0; pixelIndex < nPixels; ++pixelIndex) {
0239             for (uint channelIndex = 0; channelIndex < this->channelCount(); ++channelIndex) {
0240                 KoChannelInfo *channel = this->channels().at(channelIndex);
0241                 qint32 channelSize = channel->size();
0242                 if (selectedChannels.testBit(channelIndex)) {
0243                     memcpy(dst + (pixelIndex * _CSTrait::pixelSize) + (channelIndex * channelSize), src + (pixelIndex * _CSTrait::pixelSize) + (channelIndex * channelSize), channelSize);
0244                 } else {
0245                     reinterpret_cast<typename _CSTrait::channels_type *>(dst + (pixelIndex * _CSTrait::pixelSize) + (channelIndex * channelSize))[0] = _CSTrait::math_trait::zeroValue;
0246                 }
0247             }
0248         }
0249     }
0250 
0251 private:
0252     template<int srcPixelSize, int dstChannelSize, class TSrcChannel, class TDstChannel>
0253     void scalePixels(const quint8* src, quint8* dst, quint32 numPixels) const {
0254         qint32 dstPixelSize = dstChannelSize * _CSTrait::channels_nb;
0255 
0256         for(quint32 i=0; i<numPixels; ++i) {
0257             const TSrcChannel* srcPixel = reinterpret_cast<const TSrcChannel*>(src + i * srcPixelSize);
0258             TDstChannel*       dstPixel = reinterpret_cast<TDstChannel*>(dst + i * dstPixelSize);
0259 
0260             for(quint32 c=0; c<_CSTrait::channels_nb; ++c)
0261                 dstPixel[c] = Arithmetic::scale<TDstChannel>(srcPixel[c]);
0262         }
0263     }
0264 
0265 private:
0266     QScopedPointer<KoAlphaMaskApplicatorBase> m_alphaMaskApplicator;
0267 };
0268 
0269 #endif // KOCOLORSPACEABSTRACT_H