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 }