Krita Source Code Documentation
Loading...
Searching...
No Matches
KoMixColorsOpImpl.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2006 Cyrille Berger <cberger@cberger.net>
3 * SPDX-FileCopyrightText: 2007 Emanuele Tamponi <emanuele@valinor.it>
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6*/
7
8#ifndef KOMIXCOLORSOPIMPL_H
9#define KOMIXCOLORSOPIMPL_H
10
11#include "KoMixColorsOp.h"
12
13#include <QtGlobal>
14#include <type_traits>
15#include <KisCppQuirks.h>
16#include <KoColorSpaceMaths.h>
17#include "kis_debug.h"
18#include "kis_global.h"
19
20//#define SANITY_CHECKS
21
22template <typename T>
23static inline T safeDivideWithRound(T dividend,
24 std::enable_if_t<!std::numeric_limits<T>::is_integer, T> divisor) {
25 return dividend / divisor;
26}
27
28template <typename T>
29static inline T safeDivideWithRound(T dividend,
30 std::enable_if_t<std::numeric_limits<T>::is_integer, T> divisor) {
31 return (dividend + divisor / 2) / divisor;
32}
33
34
35
36template<class _CSTrait>
38{
39public:
42 ~KoMixColorsOpImpl() override { }
43
44 Mixer* createMixer() const override;
45
46 void mixColors(const quint8 * const* colors, const qint16 *weights, int nColors, quint8 *dst, int weightSum = 255) const override {
47 mixColorsImpl(ArrayOfPointers(colors), WeightsWrapper(weights, weightSum), nColors, dst);
48 }
49
50 void mixColors(const quint8 *colors, const qint16 *weights, int nColors, quint8 *dst, int weightSum = 255) const override {
51 mixColorsImpl(PointerToArray(colors, _CSTrait::pixelSize), WeightsWrapper(weights, weightSum), nColors, dst);
52 }
53
54 void mixColors(const quint8 * const* colors, int nColors, quint8 *dst) const override {
55 mixColorsImpl(ArrayOfPointers(colors), NoWeightsSurrogate(nColors), nColors, dst);
56 }
57
58 void mixColors(const quint8 *colors, int nColors, quint8 *dst) const override {
59 mixColorsImpl(PointerToArray(colors, _CSTrait::pixelSize), NoWeightsSurrogate(nColors), nColors, dst);
60 }
61
62 void mixTwoColorArrays(const quint8* colorsA, const quint8* colorsB, int nColors, qreal weight, quint8* dst) const override {
63 const quint8* pixelA = colorsA;
64 const quint8* pixelB = colorsB;
65 weight = qBound(0.0, weight, 1.0);
66 for (int i = 0; i < nColors; i++) {
67 const quint8* colors[2];
68 colors[0] = pixelA;
69 colors[1] = pixelB;
70 qint16 weights[2];
71 weights[1] = qRound(weight * 255.0);
72 weights[0] = 255 - weights[1];
73 mixColorsImpl(ArrayOfPointers(colors), WeightsWrapper(weights, 255), 2, dst);
74
75 pixelA += _CSTrait::pixelSize;
76 pixelB += _CSTrait::pixelSize;
77 dst += _CSTrait::pixelSize;
78 }
79 }
80
81 void mixArrayWithColor(const quint8* colorArray, const quint8* color, int nColors, qreal weight, quint8* dst) const override {
82 const quint8* pixelA = colorArray;
83 weight = qBound(0.0, weight, 1.0);
84 for (int i = 0; i < nColors; i++) {
85 const quint8* colors[2];
86 colors[0] = pixelA;
87 colors[1] = color;
88 qint16 weights[2];
89 weights[1] = qRound(weight * 255.0);
90 weights[0] = 255 - weights[1];
91 mixColorsImpl(ArrayOfPointers(colors), WeightsWrapper(weights, 255), 2, dst);
92
93 pixelA += _CSTrait::pixelSize;
94 dst += _CSTrait::pixelSize;
95 }
96 }
97
98private:
99 class MixerImpl;
100
102 ArrayOfPointers(const quint8 * const* colors)
103 : m_colors(colors)
104 {
105 }
106
107 const quint8* getPixel() const {
108 return *m_colors;
109 }
110
111 void nextPixel() {
112 m_colors++;
113 }
114
115 private:
116 const quint8 * const * m_colors;
117 };
118
120 PointerToArray(const quint8 *colors, int pixelSize)
121 : m_colors(colors),
122 m_pixelSize(pixelSize)
123 {
124 }
125
126 const quint8* getPixel() const {
127 return m_colors;
128 }
129
130 void nextPixel() {
132 }
133
134 private:
135 const quint8 *m_colors;
136 const int m_pixelSize;
137 };
138
140 {
142
143 WeightsWrapper(const qint16 *weights, int weightSum)
144 : m_weights(weights), m_sumOfWeights(weightSum)
145 {
146 }
147
148 inline void nextPixel() {
149 m_weights++;
150 }
151
152 inline void premultiplyAlphaWithWeight(mixtype &alpha) const {
153 alpha *= *m_weights;
154 }
155
156 inline int normalizeFactor() const {
157 return m_sumOfWeights;
158 }
159
160 private:
161 const qint16 *m_weights;
163 };
164
166 {
168
169 NoWeightsSurrogate(int numPixels)
170 : m_numPixels(numPixels)
171 {
172 }
173
174 inline void nextPixel() {
175 }
176
177 inline void premultiplyAlphaWithWeight(mixtype &) const {
178 }
179
180 inline int normalizeFactor() const {
181 return m_numPixels;
182 }
183
184 private:
185 const int m_numPixels;
186 };
187
189 using channels_type = typename _CSTrait::channels_type;
192
193 mix_type totals[_CSTrait::channels_nb];
195 qint64 normalizeFactor = 0;
196
197#ifdef SANITY_CHECKS
198 qint64 m_numPixels = 0;
199#endif
200
201 public:
203 memset(totals, 0, sizeof(totals));
204 }
205
206 void computeMixedColor(quint8 *dst) {
207#ifdef SANITY_CHECKS
208
209 const mix_type maxSaneNumPixels =
210 std::numeric_limits<mix_type>::max() / pow2(mix_type(MathsTraits::unitValue));
211
212 if (m_numPixels > maxSaneNumPixels) {
213 qWarning() << "SANITY CHECK FAILED: KoMixColorOp got too many pixels to mix, the containing type may overflow";
214 qWarning() << " " << ppVar(m_numPixels);
215 qWarning() << " " << ppVar(maxSaneNumPixels);
216 }
217#endif
218
219 channels_type* dstColor = _CSTrait::nativeArray(dst);
220
226 if (totalAlpha > 0) {
227
228 for (int i = 0; i < (int)_CSTrait::channels_nb; i++) {
229 if (i != _CSTrait::alpha_pos) {
230
232
233 if (v > MathsTraits::max) {
234 v = MathsTraits::max;
235 }
236 if (v < MathsTraits::min) {
237 v = MathsTraits::min;
238 }
239 dstColor[ i ] = v;
240 }
241 }
242
243 if (_CSTrait::alpha_pos != -1) {
245
246 if (v > MathsTraits::max) {
247 v = MathsTraits::max;
248 }
249 if (v < MathsTraits::min) {
250 v = MathsTraits::min;
251 }
252 dstColor[ _CSTrait::alpha_pos ] = v;
253 }
254 } else {
255 memset(dst, 0, sizeof(channels_type) * _CSTrait::channels_nb);
256 }
257 }
258
259 template<class AbstractSource, class WeightsWrapper>
260 void accumulateColors(AbstractSource source, WeightsWrapper weightsWrapper, int nColors) {
261 // Compute the total for each channel by summing each colors multiplied by the weightlabcache
262
263#ifdef SANITY_CHECKS
264 m_numPixels += nColors;
265#endif
266
267 while (nColors--) {
268 const channels_type* color = _CSTrait::nativeArray(source.getPixel());
269 mix_type alphaTimesWeight;
270
271 if (_CSTrait::alpha_pos != -1) {
272 alphaTimesWeight = color[_CSTrait::alpha_pos];
273 } else {
274 alphaTimesWeight = MathsTraits::unitValue;
275 }
276
277 weightsWrapper.premultiplyAlphaWithWeight(alphaTimesWeight);
278
279 for (int i = 0; i < (int)_CSTrait::channels_nb; i++) {
280 if (i != _CSTrait::alpha_pos) {
281 totals[i] += color[i] * alphaTimesWeight;
282 }
283 }
284
285 totalAlpha += alphaTimesWeight;
286 source.nextPixel();
287 weightsWrapper.nextPixel();
288 }
289
290 normalizeFactor += weightsWrapper.normalizeFactor();
291 }
292
293 qint64 currentWeightsSum() const
294 {
295 return normalizeFactor;
296 }
297 };
298
299 template<class AbstractSource, class WeightsWrapper>
300 void mixColorsImpl(AbstractSource source, WeightsWrapper weightsWrapper, int nColors, quint8 *dst) const {
301 MixDataResult result;
302 result.accumulateColors(source, weightsWrapper, nColors);
303 result.computeMixedColor(dst);
304 }
305
306};
307
308template<class _CSTrait>
310{
311public:
313 {
314 }
315
316 void accumulate(const quint8 *data, const qint16 *weights, int weightSum, int nPixels) override
317 {
318 result.accumulateColors(PointerToArray(data, _CSTrait::pixelSize), WeightsWrapper(weights, weightSum), nPixels);
319 }
320
321 void accumulateAverage(const quint8 *data, int nPixels) override
322 {
323 result.accumulateColors(PointerToArray(data, _CSTrait::pixelSize), NoWeightsSurrogate(nPixels), nPixels);
324 }
325
326 void computeMixedColor(quint8 *data) override
327 {
328 result.computeMixedColor(data);
329 }
330
331 qint64 currentWeightsSum() const override
332 {
333 return result.currentWeightsSum();
334 }
335
336private:
338};
339
340template<class _CSTrait>
345
346#endif
qreal v
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
static T safeDivideWithRound(T dividend, std::enable_if_t<!std::numeric_limits< T >::is_integer, T > divisor)
mix_type totals[_CSTrait::channels_nb]
void accumulateColors(AbstractSource source, WeightsWrapper weightsWrapper, int nColors)
typename _CSTrait::channels_type channels_type
typename KoColorSpaceMathsTraits< channels_type >::mixtype mix_type
qint64 currentWeightsSum() const override
void computeMixedColor(quint8 *data) override
void accumulateAverage(const quint8 *data, int nPixels) override
void accumulate(const quint8 *data, const qint16 *weights, int weightSum, int nPixels) override
void mixColorsImpl(AbstractSource source, WeightsWrapper weightsWrapper, int nColors, quint8 *dst) const
void mixArrayWithColor(const quint8 *colorArray, const quint8 *color, int nColors, qreal weight, quint8 *dst) const override
Mixer * createMixer() const override
void mixColors(const quint8 *colors, const qint16 *weights, int nColors, quint8 *dst, int weightSum=255) const override
~KoMixColorsOpImpl() override
void mixColors(const quint8 *const *colors, const qint16 *weights, int nColors, quint8 *dst, int weightSum=255) const override
void mixColors(const quint8 *const *colors, int nColors, quint8 *dst) const override
void mixColors(const quint8 *colors, int nColors, quint8 *dst) const override
void mixTwoColorArrays(const quint8 *colorsA, const quint8 *colorsB, int nColors, qreal weight, quint8 *dst) const override
#define ppVar(var)
Definition kis_debug.h:155
T pow2(const T &x)
Definition kis_global.h:166
ArrayOfPointers(const quint8 *const *colors)
void premultiplyAlphaWithWeight(mixtype &) const
KoColorSpaceMathsTraits< typename_CSTrait::channels_type >::mixtype mixtype
PointerToArray(const quint8 *colors, int pixelSize)
WeightsWrapper(const qint16 *weights, int weightSum)
void premultiplyAlphaWithWeight(mixtype &alpha) const
KoColorSpaceMathsTraits< typename_CSTrait::channels_type >::mixtype mixtype