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 }