Krita Source Code Documentation
Loading...
Searching...
No Matches
KisCmykDitherOpFactory.h
Go to the documentation of this file.
1/*
2 * This file is part of Krita
3 *
4 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9#include "KisDitherOpImpl.h"
10
17template<typename srcCSTraits, typename dstCSTraits, DitherType dType> class KisCmykDitherOpImpl : public KisDitherOpImpl<srcCSTraits, dstCSTraits, dType>
18{
19 using srcChannelsType = typename srcCSTraits::channels_type;
20 using dstChannelsType = typename dstCSTraits::channels_type;
21
22public:
23 KisCmykDitherOpImpl(const KoID &srcId, const KoID &dstId)
24 : KisDitherOpImpl<srcCSTraits, dstCSTraits, dType>(srcId, dstId)
25 {
26 }
27
28 void dither(const quint8 *src, quint8 *dst, int x, int y) const override
29 {
30 ditherImpl(src, dst, x, y);
31 }
32
33 void dither(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int x, int y, int columns, int rows) const override
34 {
35 ditherImpl(srcRowStart, srcRowStride, dstRowStart, dstRowStride, x, y, columns, rows);
36 }
37
38private:
39 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
40 {
41 memcpy(dst, src, srcCSTraits::pixelSize);
42 }
43
44 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
45 {
46 const srcChannelsType *nativeSrc = srcCSTraits::nativeArray(src);
47 dstChannelsType *nativeDst = dstCSTraits::nativeArray(dst);
48
49 for (uint channelIndex = 0; channelIndex < srcCSTraits::channels_nb; ++channelIndex) {
50 if (channelIndex == srcCSTraits::alpha_pos) {
51 // The standard normalization.
52 nativeDst[channelIndex] = KoColorSpaceMaths<srcChannelsType, dstChannelsType>::scaleToA(nativeSrc[channelIndex]);
53 } else {
54 // Normalize using unitCMYKValue.
55 nativeDst[channelIndex] = scaleToA<srcChannelsType, dstChannelsType>(nativeSrc[channelIndex]);
56 }
57 }
58 }
59
60 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
61 {
62 const srcChannelsType *nativeSrc = srcCSTraits::nativeArray(src);
63 dstChannelsType *nativeDst = dstCSTraits::nativeArray(dst);
64
65 float f = factor(x, y);
66 float s = scale();
67
68 // In (non-integer) CMYK, all channels except alpha are normalized
69 // to a different range, [0, 100].
70 for (uint channelIndex = 0; channelIndex < srcCSTraits::channels_nb; ++channelIndex) {
71 if (channelIndex == srcCSTraits::alpha_pos) {
72 // The standard normalization.
73 float c = KoColorSpaceMaths<srcChannelsType, float>::scaleToA(nativeSrc[channelIndex]);
75 nativeDst[channelIndex] = KoColorSpaceMaths<float, dstChannelsType>::scaleToA(c);
76 ;
77 } else {
78 // Normalize using unitCMYKValue.
79 float c = normalize<srcChannelsType>(nativeSrc[channelIndex]);
81 nativeDst[channelIndex] = denormalize<dstChannelsType>(c);
82 ;
83 }
84 }
85 }
86
87 template<DitherType t = dType, typename std::enable_if<t == DITHER_NONE && std::is_same<srcCSTraits, dstCSTraits>::value, void>::type * = nullptr>
88 inline void ditherImpl(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int, int, int columns, int rows) const
89 {
90 const quint8 *nativeSrc = srcRowStart;
91 quint8 *nativeDst = dstRowStart;
92
93 for (int y = 0; y < rows; ++y) {
94 memcpy(nativeDst, nativeSrc, srcCSTraits::pixelSize * columns);
95
96 nativeSrc += srcRowStride;
97 nativeDst += dstRowStride;
98 }
99 }
100
101 template<DitherType t = dType, typename std::enable_if<t == DITHER_NONE && !std::is_same<srcCSTraits, dstCSTraits>::value, void>::type * = nullptr>
102 inline void ditherImpl(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int, int, int columns, int rows) const
103 {
104 const quint8 *nativeSrc = srcRowStart;
105 quint8 *nativeDst = dstRowStart;
106
107 for (int y = 0; y < rows; ++y) {
108 const srcChannelsType *srcPtr = srcCSTraits::nativeArray(nativeSrc);
109 dstChannelsType *dstPtr = dstCSTraits::nativeArray(nativeDst);
110
111 for (int x = 0; x < columns; ++x) {
112 for (uint channelIndex = 0; channelIndex < srcCSTraits::channels_nb; ++channelIndex) {
113 if (channelIndex == srcCSTraits::alpha_pos) {
114 // The standard normalization.
115 dstPtr[channelIndex] = KoColorSpaceMaths<srcChannelsType, dstChannelsType>::scaleToA(srcPtr[channelIndex]);
116 } else {
117 // Normalize using unitCMYKValue.
118 dstPtr[channelIndex] = scaleToA<srcChannelsType, dstChannelsType>(srcPtr[channelIndex]);
119 }
120 }
121
122 srcPtr += srcCSTraits::channels_nb;
123 dstPtr += dstCSTraits::channels_nb;
124 }
125
126 nativeSrc += srcRowStride;
127 nativeDst += dstRowStride;
128 }
129 }
130
131 template<DitherType t = dType, typename std::enable_if<t != DITHER_NONE, void>::type * = nullptr>
132 inline void ditherImpl(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int x, int y, int columns, int rows) const
133 {
134 const quint8 *nativeSrc = srcRowStart;
135 quint8 *nativeDst = dstRowStart;
136
137 float s = scale();
138
139 for (int a = 0; a < rows; ++a) {
140 const srcChannelsType *srcPtr = srcCSTraits::nativeArray(nativeSrc);
141 dstChannelsType *dstPtr = dstCSTraits::nativeArray(nativeDst);
142
143 for (int b = 0; b < columns; ++b) {
144 float f = factor(x + b, y + a);
145
146 for (uint channelIndex = 0; channelIndex < srcCSTraits::channels_nb; ++channelIndex) {
147 if (channelIndex == srcCSTraits::alpha_pos) {
148 // The standard normalization.
149 float c = KoColorSpaceMaths<srcChannelsType, float>::scaleToA(srcPtr[channelIndex]);
150 c = KisDitherMaths::apply_dither(c, f, s);
152 ;
153 } else {
154 // Normalize using unitCMYKValue.
155 float c = normalize<srcChannelsType>(srcPtr[channelIndex]);
156 c = KisDitherMaths::apply_dither(c, f, s);
157 dstPtr[channelIndex] = denormalize<dstChannelsType>(c);
158 ;
159 }
160 }
161
162 srcPtr += srcCSTraits::channels_nb;
163 dstPtr += dstCSTraits::channels_nb;
164 }
165
166 nativeSrc += srcRowStride;
167 nativeDst += dstRowStride;
168 }
169 }
170
171 // CMYK-specific normalization bits
172
173 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
174 {
175 return static_cast<float>(value) / KoColorSpaceMathsTraits<A>::unitValue;
176 };
177
178 template<typename A, typename std::enable_if<!std::numeric_limits<A>::is_integer, void>::type * = nullptr> inline float normalize(A value) const
179 {
180 return static_cast<float>(value) / KoCmykColorSpaceMathsTraits<A>::unitValueCMYK;
181 };
182
183 template<typename A, typename std::enable_if<std::numeric_limits<A>::is_integer, void>::type * = nullptr> inline A denormalize(float value) const
184 {
185 return static_cast<A>(value * static_cast<float>(KoColorSpaceMathsTraits<A>::unitValue));
186 };
187
188 template<typename A, typename std::enable_if<!std::numeric_limits<A>::is_integer, void>::type * = nullptr> inline A denormalize(float value) const
189 {
190 return static_cast<A>(value * static_cast<float>(KoCmykColorSpaceMathsTraits<A>::unitValueCMYK));
191 };
192
193 template<typename A, typename B> inline B scaleToA(A c) const
194 {
195 return denormalize<B>(normalize<A>(c));
196 }
197
198 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
199 {
200 return 0.f; // no dithering for floating point
201 }
202
203 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
204 {
205 return 1.f / static_cast<float>(1 << dstCSTraits::depth);
206 }
207
208 template<DitherType t = dType, typename std::enable_if<t == DITHER_BAYER, void>::type * = nullptr> inline float factor(int x, int y) const
209 {
211 }
212
213 template<DitherType t = dType, typename std::enable_if<t == DITHER_BLUE_NOISE, void>::type * = nullptr> inline float factor(int x, int y) const
214 {
216 }
217};
218
219template<typename srcCSTraits, typename dstCSTraits> inline void addCmykDitherOpsByDepth(KoColorSpace *cs, const KoID &dstDepth)
220{
221 const KoID &srcDepth {cs->colorDepthId()};
225}
226
227template<class srcCSTraits> inline void addStandardDitherOps(KoColorSpace *cs)
228{
229 static_assert(std::is_same<srcCSTraits, KoCmykU8Traits>::value || std::is_same<srcCSTraits, KoCmykU16Traits>::value ||
230#ifdef HAVE_OPENEXR
231 std::is_same<srcCSTraits, KoCmykF16Traits>::value ||
232#endif
233 std::is_same<srcCSTraits, KoCmykF32Traits>::value,
234 "Missing colorspace, add a transform case!");
235
236 KIS_ASSERT(cs->pixelSize() == srcCSTraits::pixelSize);
237
238 addCmykDitherOpsByDepth<srcCSTraits, KoCmykU8Traits>(cs, Integer8BitsColorDepthID);
239 addCmykDitherOpsByDepth<srcCSTraits, KoCmykU16Traits>(cs, Integer16BitsColorDepthID);
240#ifdef HAVE_OPENEXR
241 addCmykDitherOpsByDepth<srcCSTraits, KoCmykF16Traits>(cs, Float16BitsColorDepthID);
242#endif
243 addCmykDitherOpsByDepth<srcCSTraits, KoCmykF32Traits>(cs, Float32BitsColorDepthID);
244}
float value(const T *src, size_t ch)
void addCmykDitherOpsByDepth(KoColorSpace *cs, const KoID &dstDepth)
void addStandardDitherOps(KoColorSpace *cs)
const KoID Float32BitsColorDepthID("F32", ki18n("32-bit float/channel"))
const KoID Float16BitsColorDepthID("F16", ki18n("16-bit float/channel"))
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const KoID Integer16BitsColorDepthID("U16", ki18n("16-bit integer/channel"))
unsigned int uint
float normalize(A value) const
void ditherImpl(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int, int, int columns, int rows) const
void dither(const quint8 *src, quint8 *dst, int x, int y) const override
float factor(int x, int y) const
constexpr float scale() const
KisCmykDitherOpImpl(const KoID &srcId, const KoID &dstId)
typename dstCSTraits::channels_type dstChannelsType
float normalize(A value) const
A denormalize(float value) const
void ditherImpl(const quint8 *src, quint8 *dst, int x, int y) const
void ditherImpl(const quint8 *src, quint8 *dst, int, int) const
void ditherImpl(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int x, int y, int columns, int rows) const
void dither(const quint8 *srcRowStart, int srcRowStride, quint8 *dstRowStart, int dstRowStride, int x, int y, int columns, int rows) const override
typename srcCSTraits::channels_type srcChannelsType
DitherType type() const override
static _Tdst scaleToA(_T a)
virtual quint32 pixelSize() const =0
virtual void addDitherOp(KisDitherOp *op)
virtual KoID colorDepthId() const =0
Definition KoID.h:30
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
float apply_dither(float f, float d, float s)
float dither_factor_bayer_8(int x, int y)
float dither_factor_blue_noise_64(int x, int y)