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