File indexing completed on 2024-05-12 15:59:29
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 #pragma once 0010 0011 #include <type_traits> 0012 0013 #include "DebugPigment.h" 0014 #include "KoConfig.h" 0015 0016 #ifdef HAVE_OPENEXR 0017 #include "half.h" 0018 #endif 0019 0020 #include <KoColorModelStandardIds.h> 0021 #include <KoColorSpace.h> 0022 #include <KoColorSpaceMaths.h> 0023 #include <KoColorSpaceTraits.h> 0024 0025 #include "KisDitherOp.h" 0026 #include "KisDitherMaths.h" 0027 0028 template<typename srcCSTraits, typename dstCSTraits, DitherType dType> class KisDitherOpImpl : public KisDitherOp 0029 { 0030 using srcChannelsType = typename srcCSTraits::channels_type; 0031 using dstChannelsType = typename dstCSTraits::channels_type; 0032 0033 public: 0034 KisDitherOpImpl(const KoID &srcId, const KoID &dstId) 0035 : m_srcDepthId(srcId) 0036 , m_dstDepthId(dstId) 0037 { 0038 } 0039 0040 void dither(const quint8 *src, quint8 *dst, int x, int y) const override 0041 { 0042 ditherImpl(src, dst, x, y); 0043 } 0044 0045 void dither(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int x, int y, int columns, int rows) const override 0046 { 0047 ditherImpl(srcRowStart, srcRowStride, dstRowStart, dstRowStride, x, y, columns, rows); 0048 } 0049 0050 KoID sourceDepthId() const override 0051 { 0052 return m_srcDepthId; 0053 } 0054 0055 KoID destinationDepthId() const override 0056 { 0057 return m_dstDepthId; 0058 } 0059 0060 DitherType type() const override 0061 { 0062 return dType; 0063 } 0064 0065 private: 0066 const KoID m_srcDepthId, m_dstDepthId; 0067 0068 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 0069 { 0070 memcpy(dst, src, srcCSTraits::pixelSize); 0071 } 0072 0073 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 0074 { 0075 const srcChannelsType *nativeSrc = srcCSTraits::nativeArray(src); 0076 dstChannelsType *nativeDst = dstCSTraits::nativeArray(dst); 0077 0078 for (uint channelIndex = 0; channelIndex < srcCSTraits::channels_nb; ++channelIndex) { 0079 nativeDst[channelIndex] = KoColorSpaceMaths<srcChannelsType, dstChannelsType>::scaleToA(nativeSrc[channelIndex]); 0080 } 0081 } 0082 0083 template<DitherType t = dType, typename std::enable_if<t != DITHER_NONE, void>::type * = nullptr> 0084 inline void ditherImpl(const quint8 *src, quint8 *dst, int x, int y) const 0085 { 0086 const srcChannelsType *nativeSrc = srcCSTraits::nativeArray(src); 0087 dstChannelsType *nativeDst = dstCSTraits::nativeArray(dst); 0088 0089 float f = factor(x, y); 0090 float s = scale(); 0091 0092 for (uint channelIndex = 0; channelIndex < srcCSTraits::channels_nb; ++channelIndex) { 0093 float c = KoColorSpaceMaths<srcChannelsType, float>::scaleToA(nativeSrc[channelIndex]); 0094 c = KisDitherMaths::apply_dither(c, f, s); 0095 nativeDst[channelIndex] = KoColorSpaceMaths<float, dstChannelsType>::scaleToA(c); 0096 } 0097 } 0098 0099 template<DitherType t = dType, typename std::enable_if<t == DITHER_NONE && std::is_same<srcCSTraits, dstCSTraits>::value, void>::type * = nullptr> 0100 inline void ditherImpl(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int, int, int columns, int rows) const 0101 { 0102 const quint8 *nativeSrc = srcRowStart; 0103 quint8 *nativeDst = dstRowStart; 0104 0105 for (int y = 0; y < rows; ++y) { 0106 memcpy(nativeDst, nativeSrc, srcCSTraits::pixelSize * columns); 0107 0108 nativeSrc += srcRowStride; 0109 nativeDst += dstRowStride; 0110 } 0111 } 0112 0113 template<DitherType t = dType, typename std::enable_if<t == DITHER_NONE && !std::is_same<srcCSTraits, dstCSTraits>::value, void>::type * = nullptr> 0114 inline void ditherImpl(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int, int, int columns, int rows) const 0115 { 0116 const quint8 *nativeSrc = srcRowStart; 0117 quint8 *nativeDst = dstRowStart; 0118 0119 for (int y = 0; y < rows; ++y) { 0120 const srcChannelsType *srcPtr = srcCSTraits::nativeArray(nativeSrc); 0121 dstChannelsType *dstPtr = dstCSTraits::nativeArray(nativeDst); 0122 0123 for (int x = 0; x < columns; ++x) { 0124 for (uint channelIndex = 0; channelIndex < srcCSTraits::channels_nb; ++channelIndex) { 0125 dstPtr[channelIndex] = KoColorSpaceMaths<srcChannelsType, dstChannelsType>::scaleToA(srcPtr[channelIndex]); 0126 } 0127 0128 srcPtr += srcCSTraits::channels_nb; 0129 dstPtr += dstCSTraits::channels_nb; 0130 } 0131 0132 nativeSrc += srcRowStride; 0133 nativeDst += dstRowStride; 0134 } 0135 } 0136 0137 template<DitherType t = dType, typename std::enable_if<t != DITHER_NONE, void>::type * = nullptr> 0138 inline void ditherImpl(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int x, int y, int columns, int rows) const 0139 { 0140 const quint8 *nativeSrc = srcRowStart; 0141 quint8 *nativeDst = dstRowStart; 0142 0143 float s = scale(); 0144 0145 for (int a = 0; a < rows; ++a) { 0146 const srcChannelsType *srcPtr = srcCSTraits::nativeArray(nativeSrc); 0147 dstChannelsType *dstPtr = dstCSTraits::nativeArray(nativeDst); 0148 0149 for (int b = 0; b < columns; ++b) { 0150 float f = factor(x + b, y + a); 0151 0152 for (uint channelIndex = 0; channelIndex < srcCSTraits::channels_nb; ++channelIndex) { 0153 float c = KoColorSpaceMaths<srcChannelsType, float>::scaleToA(srcPtr[channelIndex]); 0154 c = KisDitherMaths::apply_dither(c, f, s); 0155 dstPtr[channelIndex] = KoColorSpaceMaths<float, dstChannelsType>::scaleToA(c); 0156 } 0157 0158 srcPtr += srcCSTraits::channels_nb; 0159 dstPtr += dstCSTraits::channels_nb; 0160 } 0161 0162 nativeSrc += srcRowStride; 0163 nativeDst += dstRowStride; 0164 } 0165 } 0166 0167 template<typename U = typename dstCSTraits::channels_type, typename std::enable_if<!std::numeric_limits<U>::is_integer, void>::type * = nullptr> constexpr float scale() const 0168 { 0169 return 0.f; // no dithering for floating point 0170 } 0171 0172 template<typename U = typename dstCSTraits::channels_type, typename std::enable_if<std::numeric_limits<U>::is_integer, void>::type * = nullptr> constexpr float scale() const 0173 { 0174 return 1.f / static_cast<float>(1 << dstCSTraits::depth); 0175 } 0176 0177 template<DitherType t = dType, typename std::enable_if<t == DITHER_BAYER, void>::type * = nullptr> inline float factor(int x, int y) const 0178 { 0179 return KisDitherMaths::dither_factor_bayer_8(x, y); 0180 } 0181 0182 template<DitherType t = dType, typename std::enable_if<t == DITHER_BLUE_NOISE, void>::type * = nullptr> inline float factor(int x, int y) const 0183 { 0184 return KisDitherMaths::dither_factor_blue_noise_64(x, y); 0185 } 0186 }; 0187 0188 template<typename srcCSTraits, class dstCSTraits> inline void addDitherOpsByDepth(KoColorSpace *cs, const KoID &dstDepth) 0189 { 0190 const KoID &srcDepth {cs->colorDepthId()}; 0191 cs->addDitherOp(new KisDitherOpImpl<srcCSTraits, dstCSTraits, DITHER_NONE>(srcDepth, dstDepth)); 0192 cs->addDitherOp(new KisDitherOpImpl<srcCSTraits, dstCSTraits, DITHER_BAYER>(srcDepth, dstDepth)); 0193 cs->addDitherOp(new KisDitherOpImpl<srcCSTraits, dstCSTraits, DITHER_BLUE_NOISE>(srcDepth, dstDepth)); 0194 }