Krita Source Code Documentation
Loading...
Searching...
No Matches
KoOptimizedCompositeOpOver128.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2015 Thorsten Zachmann <zachmann@kde.org>
3 * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8#ifndef KOOPTIMIZEDCOMPOSITEOPOVER128_H_
9#define KOOPTIMIZEDCOMPOSITEOPOVER128_H_
10
11#include "KoCompositeOpBase.h"
13#include "KoStreamedMath.h"
14
15#define NATIVE_OPACITY_OPAQUE KoColorSpaceMathsTraits<channels_type>::unitValue
16#define NATIVE_OPACITY_TRANSPARENT KoColorSpaceMathsTraits<channels_type>::zeroValue
17
18#define INFO_DEBUG 0
19
20template<typename channels_type, bool alphaLocked, bool allChannelsFlag>
27 const QBitArray &channelFlags;
28 };
29
30 struct Pixel {
31 channels_type red;
32 channels_type green;
33 channels_type blue;
34 channels_type alpha;
35 };
36
37 // \see docs in AlphaDarkenCompositor32
38 template<bool haveMask, bool src_aligned, typename _impl>
39 static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
40 {
41#if INFO_DEBUG
42 static quint32 countTotal = 0;
43 static quint32 countOne = 0;
44 static quint32 countTwo = 0;
45 static quint32 countThree = 0;
46 static quint32 countFour = 0;
47
48 if (++countTotal % 250000 == 0) {
49 qInfo() << "count" << countOne << countTwo << countThree << countFour << countTotal << opacity;
50 }
51#endif
52 using float_v = typename KoStreamedMath<_impl>::float_v;
53 using float_m = typename float_v::batch_bool_type;
54
55 Q_UNUSED(oparams);
56
57 float_v src_alpha;
58 float_v dst_alpha;
59
60 float_v src_c1;
61 float_v src_c2;
62 float_v src_c3;
63
65 dataWrapper.read(src, src_c1, src_c2, src_c3, src_alpha);
66
67 //bool haveOpacity = opacity != 1.0;
68 const float_v opacity_norm_vec(opacity);
69 src_alpha *= opacity_norm_vec;
70
71 if (haveMask) {
72 const float_v uint8MaxRec1((float)1.0 / 255);
73 float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask);
74 src_alpha *= mask_vec * uint8MaxRec1;
75 }
76
77 const float_v zeroValue(static_cast<float>(NATIVE_OPACITY_TRANSPARENT));
78 // The source cannot change the colors in the destination,
79 // since its fully transparent
80 if (xsimd::all(src_alpha == zeroValue)) {
81#if INFO_DEBUG
82 countFour++;
83#endif
84 return;
85 }
86
87 float_v dst_c1;
88 float_v dst_c2;
89 float_v dst_c3;
90
91 dataWrapper.read(dst, dst_c1, dst_c2, dst_c3, dst_alpha);
92
93 float_v src_blend;
94 float_v new_alpha;
95
96 const float_v oneValue(1.0f);
97 if (xsimd::all(dst_alpha == oneValue)) {
98 new_alpha = dst_alpha;
99 src_blend = src_alpha;
100 } else if (xsimd::all(dst_alpha == zeroValue)) {
101 new_alpha = src_alpha;
102 src_blend = oneValue;
103 } else {
108 new_alpha = dst_alpha + (oneValue - dst_alpha) * src_alpha;
109 const float_m mask = (new_alpha == zeroValue);
110 src_blend = src_alpha / new_alpha;
111 src_blend = xsimd::set_zero(src_blend, mask);
112 }
113
114 if (!xsimd::all(src_blend == oneValue)) {
115#if INFO_DEBUG
116 ++countOne;
117#endif
118
119 dst_c1 = src_blend * (src_c1 - dst_c1) + dst_c1;
120 dst_c2 = src_blend * (src_c2 - dst_c2) + dst_c2;
121 dst_c3 = src_blend * (src_c3 - dst_c3) + dst_c3;
122
123 dataWrapper.write(dst, dst_c1, dst_c2, dst_c3, new_alpha);
124 } else {
125#if INFO_DEBUG
126 ++countTwo;
127#endif
128 dataWrapper.write(dst, src_c1, src_c2, src_c3, new_alpha);
129 }
130 }
131
132 template<bool haveMask, typename _impl>
133 static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *src,
134 quint8 *dst,
135 const quint8 *mask,
136 float opacity,
137 const ParamsWrapper &oparams)
138 {
139 using namespace Arithmetic;
140 const qint32 alpha_pos = 3;
141
142 const auto *s = reinterpret_cast<const channels_type*>(src);
143 auto *d = reinterpret_cast<channels_type*>(dst);
144
145 float srcAlpha = s[alpha_pos];
147 srcAlpha *= opacity;
148
149 if (haveMask) {
150 const float uint8Rec1 = 1.0f / 255.0f;
151 srcAlpha *= float(*mask) * uint8Rec1;
152 }
153
154#if INFO_DEBUG
155 static int xx = 0;
156 bool display = xx > 45 && xx < 50;
157 if (display) {
158 qInfo() << "O" << s[alpha_pos] << srcAlpha << haveMask << opacity;
159 }
160#endif
161
162 if (srcAlpha != 0.0f) {
163
164 float dstAlpha = d[alpha_pos];
166 float srcBlendNorm = 0.0f;
167
168 if (alphaLocked || dstAlpha == 1.0f) {
169 srcBlendNorm = srcAlpha;
170 } else if (dstAlpha == 0.0f) {
171 dstAlpha = srcAlpha;
172 srcBlendNorm = 1.0f;
173
174 if (!allChannelsFlag) {
175 KoStreamedMathFunctions::clearPixel<sizeof(Pixel)>(dst);
176 }
177 } else {
178 dstAlpha += (1.0f - dstAlpha) * srcAlpha;
179 srcBlendNorm = srcAlpha / dstAlpha;
180 }
181
182#if INFO_DEBUG
183 if (display) {
184 qInfo() << "params" << srcBlendNorm << allChannelsFlag << alphaLocked << dstAlpha << haveMask;
185 }
186#endif
187 if(allChannelsFlag) {
188 if (srcBlendNorm == 1.0f) {
189 if (!alphaLocked) {
190 KoStreamedMathFunctions::copyPixel<sizeof(Pixel)>(src, dst);
191 } else {
192 d[0] = s[0];
193 d[1] = s[1];
194 d[2] = s[2];
195 }
196 } else if (srcBlendNorm != 0.0f){
197#if INFO_DEBUG
198 if (display) {
199 qInfo() << "calc" << s[0] << d[0] << srcBlendNorm * (s[0] - d[0]) + d[0] << s[0] - d[0] << srcBlendNorm * (s[0] - d[0]) << srcBlendNorm;
200 }
201#endif
202
203 d[0] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(d[0], s[0], srcBlendNorm);
204 d[1] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(d[1], s[1], srcBlendNorm);
205 d[2] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(d[2], s[2], srcBlendNorm);
206 }
207 } else {
208 const QBitArray &channelFlags = oparams.channelFlags;
209
210 if (srcBlendNorm == 1.0f) {
211 if(channelFlags.at(0)) d[0] = s[0];
212 if(channelFlags.at(1)) d[1] = s[1];
213 if(channelFlags.at(2)) d[2] = s[2];
214 } else if (srcBlendNorm != 0.0f) {
215 if(channelFlags.at(0)) d[0] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(d[0], s[0], srcBlendNorm);
216 if(channelFlags.at(1)) d[1] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(d[1], s[1], srcBlendNorm);;
217 if(channelFlags.at(2)) d[2] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(d[2], s[2], srcBlendNorm);;
218 }
219 }
220
221 if (!alphaLocked) {
224 }
225#if INFO_DEBUG
226 if (display) {
227 qInfo() << "result" << d[0] << d[1] << d[2] << d[3];
228 }
229 ++xx;
230#endif
231 }
232 }
233};
234
240template<typename _impl>
242{
243public:
246
248
249 void composite(const KoCompositeOp::ParameterInfo& params) const override
250 {
251 if(params.maskRowStart) {
252 composite<true>(params);
253 } else {
254 composite<false>(params);
255 }
256 }
257
258 template <bool haveMask>
259 inline void composite(const KoCompositeOp::ParameterInfo& params) const {
260 if (params.channelFlags.isEmpty() ||
261 params.channelFlags == QBitArray(4, true)) {
262
263 KoStreamedMath<_impl>::template genericComposite128<haveMask, false, OverCompositor128<float, false, true> >(params);
264 } else {
265 const bool allChannelsFlag =
266 params.channelFlags.at(0) &&
267 params.channelFlags.at(1) &&
268 params.channelFlags.at(2);
269
270 const bool alphaLocked =
271 !params.channelFlags.at(3);
272
273 if (allChannelsFlag && alphaLocked) {
274 KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, OverCompositor128<float, true, true> >(params);
275 } else if (!allChannelsFlag && !alphaLocked) {
276 KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, OverCompositor128<float, false, false> >(params);
277 } else /*if (!allChannelsFlag && alphaLocked) */{
278 KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, OverCompositor128<float, true, false> >(params);
279 }
280 }
281 }
282};
283
284template<typename _impl>
286{
287public:
290
292
293 void composite(const KoCompositeOp::ParameterInfo& params) const override
294 {
295 if(params.maskRowStart) {
296 composite<true>(params);
297 } else {
298 composite<false>(params);
299 }
300 }
301
302 template <bool haveMask>
303 inline void composite(const KoCompositeOp::ParameterInfo& params) const {
304 if (params.channelFlags.isEmpty() ||
305 params.channelFlags == QBitArray(4, true)) {
306
307 KoStreamedMath<_impl>::template genericComposite64<haveMask, false, OverCompositor128<quint16, false, true> >(params);
308 } else {
309 const bool allChannelsFlag =
310 params.channelFlags.at(0) &&
311 params.channelFlags.at(1) &&
312 params.channelFlags.at(2);
313
314 const bool alphaLocked =
315 !params.channelFlags.at(3);
316
317 if (allChannelsFlag && alphaLocked) {
318 KoStreamedMath<_impl>::template genericComposite64_novector<haveMask, false, OverCompositor128<quint16, true, true> >(params);
319 } else if (!allChannelsFlag && !alphaLocked) {
320 KoStreamedMath<_impl>::template genericComposite64_novector<haveMask, false, OverCompositor128<quint16, false, false> >(params);
321 } else /*if (!allChannelsFlag && alphaLocked) */{
322 KoStreamedMath<_impl>::template genericComposite64_novector<haveMask, false, OverCompositor128<quint16, true, false> >(params);
323 }
324 }
325 }
326};
327
328#endif // KOOPTIMIZEDCOMPOSITEOPOVER128_H_
#define ALWAYS_INLINE
const QString COMPOSITE_OVER
#define NATIVE_OPACITY_TRANSPARENT
void composite(const KoCompositeOp::ParameterInfo &params) const override
void composite(const KoCompositeOp::ParameterInfo &params) const
KoOptimizedCompositeOpOver128(const KoColorSpace *cs)
void composite(const KoCompositeOp::ParameterInfo &params) const
KoOptimizedCompositeOpOverU64(const KoColorSpace *cs)
void composite(const KoCompositeOp::ParameterInfo &params) const override
auto set_zero(const batch< T, A > &src, const batch_bool< T, A > &mask) noexcept
void composite(quint8 *dstRowStart, qint32 dstRowStride, const quint8 *srcRowStart, qint32 srcRowStride, const quint8 *maskRowStart, qint32 maskRowStride, qint32 rows, qint32 numColumns, float opacity, const QBitArray &channelFlags=QBitArray()) const
static QString categoryMix()
xsimd::batch< float, _impl > float_v
static float_v fetch_mask_8(const quint8 *data)
ParamsWrapper(const KoCompositeOp::ParameterInfo &params)
static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)