File indexing completed on 2024-12-22 04:11:42

0001 /*
0002  * This file is part of Krita
0003  *
0004  * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "KisDitherOpImpl.h"
0010 
0011 /**
0012  * THIS CLASS OVERRIDES THE STANDARD FACTORY.
0013  * Floating point CMYK uses different normalization for the color
0014  * and alpha channels.
0015  */
0016 
0017 template<typename srcCSTraits, typename dstCSTraits, DitherType dType> class KisCmykDitherOpImpl : public KisDitherOpImpl<srcCSTraits, dstCSTraits, dType>
0018 {
0019     using srcChannelsType = typename srcCSTraits::channels_type;
0020     using dstChannelsType = typename dstCSTraits::channels_type;
0021 
0022 public:
0023     KisCmykDitherOpImpl(const KoID &srcId, const KoID &dstId)
0024         : KisDitherOpImpl<srcCSTraits, dstCSTraits, dType>(srcId, dstId)
0025     {
0026     }
0027 
0028     void dither(const quint8 *src, quint8 *dst, int x, int y) const override
0029     {
0030         ditherImpl(src, dst, x, y);
0031     }
0032 
0033     void dither(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int x, int y, int columns, int rows) const override
0034     {
0035         ditherImpl(srcRowStart, srcRowStride, dstRowStart, dstRowStride, x, y, columns, rows);
0036     }
0037 
0038 private:
0039     template<DitherType t = dType, typename std::enable_if<t == DITHER_NONE && std::is_same<srcCSTraits, dstCSTraits>::value, void>::type * = nullptr> inline void ditherImpl(const quint8 *src, quint8 *dst, int, int) const
0040     {
0041         memcpy(dst, src, srcCSTraits::pixelSize);
0042     }
0043 
0044     template<DitherType t = dType, typename std::enable_if<t == DITHER_NONE && !std::is_same<srcCSTraits, dstCSTraits>::value, void>::type * = nullptr> inline void ditherImpl(const quint8 *src, quint8 *dst, int, int) const
0045     {
0046         const srcChannelsType *nativeSrc = srcCSTraits::nativeArray(src);
0047         dstChannelsType *nativeDst = dstCSTraits::nativeArray(dst);
0048 
0049         for (uint channelIndex = 0; channelIndex < srcCSTraits::channels_nb; ++channelIndex) {
0050             if (channelIndex == srcCSTraits::alpha_pos) {
0051                 // The standard normalization.
0052                 nativeDst[channelIndex] = KoColorSpaceMaths<srcChannelsType, dstChannelsType>::scaleToA(nativeSrc[channelIndex]);
0053             } else {
0054                 // Normalize using unitCMYKValue.
0055                 nativeDst[channelIndex] = scaleToA<srcChannelsType, dstChannelsType>(nativeSrc[channelIndex]);
0056             }
0057         }
0058     }
0059 
0060     template<DitherType t = dType, typename std::enable_if<t != DITHER_NONE, void>::type * = nullptr> inline void ditherImpl(const quint8 *src, quint8 *dst, int x, int y) const
0061     {
0062         const srcChannelsType *nativeSrc = srcCSTraits::nativeArray(src);
0063         dstChannelsType *nativeDst = dstCSTraits::nativeArray(dst);
0064 
0065         float f = factor(x, y);
0066         float s = scale();
0067 
0068         // In (non-integer) CMYK, all channels except alpha are normalized
0069         // to a different range, [0, 100].
0070         for (uint channelIndex = 0; channelIndex < srcCSTraits::channels_nb; ++channelIndex) {
0071             if (channelIndex == srcCSTraits::alpha_pos) {
0072                 // The standard normalization.
0073                 float c = KoColorSpaceMaths<srcChannelsType, float>::scaleToA(nativeSrc[channelIndex]);
0074                 c = KisDitherMaths::apply_dither(c, f, s);
0075                 nativeDst[channelIndex] = KoColorSpaceMaths<float, dstChannelsType>::scaleToA(c);
0076                 ;
0077             } else {
0078                 // Normalize using unitCMYKValue.
0079                 float c = normalize<srcChannelsType>(nativeSrc[channelIndex]);
0080                 c = KisDitherMaths::apply_dither(c, f, s);
0081                 nativeDst[channelIndex] = denormalize<dstChannelsType>(c);
0082                 ;
0083             }
0084         }
0085     }
0086 
0087     template<DitherType t = dType, typename std::enable_if<t == DITHER_NONE && std::is_same<srcCSTraits, dstCSTraits>::value, void>::type * = nullptr>
0088     inline void ditherImpl(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int, int, int columns, int rows) const
0089     {
0090         const quint8 *nativeSrc = srcRowStart;
0091         quint8 *nativeDst = dstRowStart;
0092 
0093         for (int y = 0; y < rows; ++y) {
0094             memcpy(nativeDst, nativeSrc, srcCSTraits::pixelSize * columns);
0095 
0096             nativeSrc += srcRowStride;
0097             nativeDst += dstRowStride;
0098         }
0099     }
0100 
0101     template<DitherType t = dType, typename std::enable_if<t == DITHER_NONE && !std::is_same<srcCSTraits, dstCSTraits>::value, void>::type * = nullptr>
0102     inline void ditherImpl(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int, int, int columns, int rows) const
0103     {
0104         const quint8 *nativeSrc = srcRowStart;
0105         quint8 *nativeDst = dstRowStart;
0106 
0107         for (int y = 0; y < rows; ++y) {
0108             const srcChannelsType *srcPtr = srcCSTraits::nativeArray(nativeSrc);
0109             dstChannelsType *dstPtr = dstCSTraits::nativeArray(nativeDst);
0110 
0111             for (int x = 0; x < columns; ++x) {
0112                 for (uint channelIndex = 0; channelIndex < srcCSTraits::channels_nb; ++channelIndex) {
0113                     if (channelIndex == srcCSTraits::alpha_pos) {
0114                         // The standard normalization.
0115                         dstPtr[channelIndex] = KoColorSpaceMaths<srcChannelsType, dstChannelsType>::scaleToA(srcPtr[channelIndex]);
0116                     } else {
0117                         // Normalize using unitCMYKValue.
0118                         dstPtr[channelIndex] = scaleToA<srcChannelsType, dstChannelsType>(srcPtr[channelIndex]);
0119                     }
0120                 }
0121 
0122                 srcPtr += srcCSTraits::channels_nb;
0123                 dstPtr += dstCSTraits::channels_nb;
0124             }
0125 
0126             nativeSrc += srcRowStride;
0127             nativeDst += dstRowStride;
0128         }
0129     }
0130 
0131     template<DitherType t = dType, typename std::enable_if<t != DITHER_NONE, void>::type * = nullptr>
0132     inline void ditherImpl(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int x, int y, int columns, int rows) const
0133     {
0134         const quint8 *nativeSrc = srcRowStart;
0135         quint8 *nativeDst = dstRowStart;
0136 
0137         float s = scale();
0138 
0139         for (int a = 0; a < rows; ++a) {
0140             const srcChannelsType *srcPtr = srcCSTraits::nativeArray(nativeSrc);
0141             dstChannelsType *dstPtr = dstCSTraits::nativeArray(nativeDst);
0142 
0143             for (int b = 0; b < columns; ++b) {
0144                 float f = factor(x + b, y + a);
0145 
0146                 for (uint channelIndex = 0; channelIndex < srcCSTraits::channels_nb; ++channelIndex) {
0147                     if (channelIndex == srcCSTraits::alpha_pos) {
0148                         // The standard normalization.
0149                         float c = KoColorSpaceMaths<srcChannelsType, float>::scaleToA(srcPtr[channelIndex]);
0150                         c = KisDitherMaths::apply_dither(c, f, s);
0151                         dstPtr[channelIndex] = KoColorSpaceMaths<float, dstChannelsType>::scaleToA(c);
0152                         ;
0153                     } else {
0154                         // Normalize using unitCMYKValue.
0155                         float c = normalize<srcChannelsType>(srcPtr[channelIndex]);
0156                         c = KisDitherMaths::apply_dither(c, f, s);
0157                         dstPtr[channelIndex] = denormalize<dstChannelsType>(c);
0158                         ;
0159                     }
0160                 }
0161 
0162                 srcPtr += srcCSTraits::channels_nb;
0163                 dstPtr += dstCSTraits::channels_nb;
0164             }
0165 
0166             nativeSrc += srcRowStride;
0167             nativeDst += dstRowStride;
0168         }
0169     }
0170 
0171     // CMYK-specific normalization bits
0172 
0173     template<typename A, typename U = srcCSTraits, typename std::enable_if<std::numeric_limits<A>::is_integer, void>::type * = nullptr> inline float normalize(A value) const
0174     {
0175         return static_cast<float>(value) / KoColorSpaceMathsTraits<A>::unitValue;
0176     };
0177 
0178     template<typename A, typename std::enable_if<!std::numeric_limits<A>::is_integer, void>::type * = nullptr> inline float normalize(A value) const
0179     {
0180         return static_cast<float>(value) / KoCmykColorSpaceMathsTraits<A>::unitValueCMYK;
0181     };
0182 
0183     template<typename A, typename std::enable_if<std::numeric_limits<A>::is_integer, void>::type * = nullptr> inline A denormalize(float value) const
0184     {
0185         return static_cast<A>(value * static_cast<float>(KoColorSpaceMathsTraits<A>::unitValue));
0186     };
0187 
0188     template<typename A, typename std::enable_if<!std::numeric_limits<A>::is_integer, void>::type * = nullptr> inline A denormalize(float value) const
0189     {
0190         return static_cast<A>(value * static_cast<float>(KoCmykColorSpaceMathsTraits<A>::unitValueCMYK));
0191     };
0192 
0193     template<typename A, typename B> inline B scaleToA(A c) const
0194     {
0195         return denormalize<B>(normalize<A>(c));
0196     }
0197 
0198     template<typename U = typename dstCSTraits::channels_type, typename std::enable_if<!std::numeric_limits<U>::is_integer, void>::type * = nullptr> inline constexpr float scale() const
0199     {
0200         return 0.f; // no dithering for floating point
0201     }
0202 
0203     template<typename U = typename dstCSTraits::channels_type, typename std::enable_if<std::numeric_limits<U>::is_integer, void>::type * = nullptr> inline constexpr float scale() const
0204     {
0205         return 1.f / static_cast<float>(1 << dstCSTraits::depth);
0206     }
0207 
0208     template<DitherType t = dType, typename std::enable_if<t == DITHER_BAYER, void>::type * = nullptr> inline float factor(int x, int y) const
0209     {
0210         return KisDitherMaths::dither_factor_bayer_8(x, y);
0211     }
0212 
0213     template<DitherType t = dType, typename std::enable_if<t == DITHER_BLUE_NOISE, void>::type * = nullptr> inline float factor(int x, int y) const
0214     {
0215         return KisDitherMaths::dither_factor_blue_noise_64(x, y);
0216     }
0217 };
0218 
0219 template<typename srcCSTraits, typename dstCSTraits> inline void addCmykDitherOpsByDepth(KoColorSpace *cs, const KoID &dstDepth)
0220 {
0221     const KoID &srcDepth {cs->colorDepthId()};
0222     cs->addDitherOp(new KisCmykDitherOpImpl<srcCSTraits, dstCSTraits, DITHER_NONE>(srcDepth, dstDepth));
0223     cs->addDitherOp(new KisCmykDitherOpImpl<srcCSTraits, dstCSTraits, DITHER_BAYER>(srcDepth, dstDepth));
0224     cs->addDitherOp(new KisCmykDitherOpImpl<srcCSTraits, dstCSTraits, DITHER_BLUE_NOISE>(srcDepth, dstDepth));
0225 }
0226 
0227 template<class srcCSTraits> inline void addStandardDitherOps(KoColorSpace *cs)
0228 {
0229     static_assert(std::is_same<srcCSTraits, KoCmykU8Traits>::value || std::is_same<srcCSTraits, KoCmykU16Traits>::value ||
0230 #ifdef HAVE_OPENEXR
0231                       std::is_same<srcCSTraits, KoCmykF16Traits>::value ||
0232 #endif
0233                       std::is_same<srcCSTraits, KoCmykF32Traits>::value,
0234                   "Missing colorspace, add a transform case!");
0235 
0236     KIS_ASSERT(cs->pixelSize() == srcCSTraits::pixelSize);
0237 
0238     addCmykDitherOpsByDepth<srcCSTraits, KoCmykU8Traits>(cs, Integer8BitsColorDepthID);
0239     addCmykDitherOpsByDepth<srcCSTraits, KoCmykU16Traits>(cs, Integer16BitsColorDepthID);
0240 #ifdef HAVE_OPENEXR
0241     addCmykDitherOpsByDepth<srcCSTraits, KoCmykF16Traits>(cs, Float16BitsColorDepthID);
0242 #endif
0243     addCmykDitherOpsByDepth<srcCSTraits, KoCmykF32Traits>(cs, Float32BitsColorDepthID);
0244 }