Krita Source Code Documentation
Loading...
Searching...
No Matches
KoOptimizedCompositeOpCopy128.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com>
3 * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8#ifndef KOOPTIMIZEDCOMPOSITEOPCOPY128_H_
9#define KOOPTIMIZEDCOMPOSITEOPCOPY128_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
18template<typename channels_type, bool alphaLocked, bool allChannelsFlag>
25 const QBitArray &channelFlags;
26 };
27
28 struct Pixel {
29 channels_type red;
30 channels_type green;
31 channels_type blue;
32 channels_type alpha;
33 };
34
35 template<bool haveMask, bool src_aligned, typename _impl>
36 static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
37 {
38 Q_UNUSED(oparams);
39
40 using float_v = typename KoStreamedMath<_impl>::float_v;
41 using float_m = typename float_v::batch_bool_type;
42
43 float_v src_alpha;
44 float_v src_c1;
45 float_v src_c2;
46 float_v src_c3;
47
49 dataWrapper.read(src, src_c1, src_c2, src_c3, src_alpha);
50
51 const float_v opacity_norm_vec = [&]() {
52 float_v o(opacity);
53 if (haveMask) {
54 const float_v uint8MaxRec1(1.0f / 255.0f);
55 const float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask);
56 o *= mask_vec * uint8MaxRec1;
57 }
58 return o;
59 }();
60
61 const float_v zeroValue(0.0f);
62 const float_v oneValue(1.0f);
63
64 const float_m opacity_is_null_mask = opacity_norm_vec == zeroValue;
65
66 // The source cannot change the colors in the destination,
67 // since its fully transparent
68 if (xsimd::all(opacity_is_null_mask)) {
69 // noop
70 } else if (xsimd::all(opacity_norm_vec == oneValue)) {
71 if (xsimd::all(src_alpha == zeroValue)) {
72 dataWrapper.clearPixels(dst);
73 } else {
74 dataWrapper.copyPixels(src, dst);
75 }
76
77 } else {
78 float_v dst_alpha;
79 float_v dst_c1;
80 float_v dst_c2;
81 float_v dst_c3;
82
83 dataWrapper.read(dst, dst_c1, dst_c2, dst_c3, dst_alpha);
84
85 const float_v newAlpha = dst_alpha + opacity_norm_vec * (src_alpha - dst_alpha);
86
87 if (xsimd::all(newAlpha == zeroValue)) {
88 dataWrapper.clearPixels(dst);
89 } else {
90 PixelStateRecoverHelper<channels_type, _impl> pixelRecoverHelper(dst_c1, dst_c2, dst_c3);
91
92 src_c1 *= src_alpha;
93 src_c2 *= src_alpha;
94 src_c3 *= src_alpha;
95
96 dst_c1 *= dst_alpha;
97 dst_c2 *= dst_alpha;
98 dst_c3 *= dst_alpha;
99
100 dst_c1 += opacity_norm_vec * (src_c1 - dst_c1);
101 dst_c2 += opacity_norm_vec * (src_c2 - dst_c2);
102 dst_c3 += opacity_norm_vec * (src_c3 - dst_c3);
103
104 if (!xsimd::all(newAlpha == oneValue)) {
113
114 dst_c1 /= newAlpha;
115 dst_c2 /= newAlpha;
116 dst_c3 /= newAlpha;
117
118
120 dst_c1 = xsimd::select(xsimd::isnan(dst_c1), unitValue, dst_c1);
121 dst_c2 = xsimd::select(xsimd::isnan(dst_c2), unitValue, dst_c2);
122 dst_c3 = xsimd::select(xsimd::isnan(dst_c3), unitValue, dst_c3);
123 dst_c1 = xsimd::min(dst_c1, unitValue);
124 dst_c2 = xsimd::min(dst_c2, unitValue);
125 dst_c3 = xsimd::min(dst_c3, unitValue);
126 }
127
134 pixelRecoverHelper.recoverPixels(opacity_is_null_mask, dst_c1, dst_c2, dst_c3);
135
136 dataWrapper.write(dst, dst_c1, dst_c2, dst_c3, newAlpha);
137 }
138 }
139 }
140
141 template <bool haveMask, typename _impl = xsimd::current_arch>
142 static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
143 {
144 using namespace Arithmetic;
145 const qint32 alpha_pos = 3;
146
147 const auto *s = reinterpret_cast<const channels_type*>(src);
148 auto *d = reinterpret_cast<channels_type*>(dst);
149
150 const channels_type nativeOriginalSrcAlpha = s[alpha_pos];
151
152 // we shouldn't leak undefined color value from under the locked zero alpha
153 // into our destination
154 if (alphaLocked &&
155 nativeOriginalSrcAlpha == KoColorSpaceMathsTraits<channels_type>::zeroValue) {
156
157 return;
158 }
159
160 float srcAlpha = nativeOriginalSrcAlpha;
162
163 if (haveMask) {
164 const float uint8Rec1 = 1.0f / 255.0f;
165 opacity *= float(*mask) * uint8Rec1;
166 }
167
168 if (opacity == 0.0f) {
169 // noop
170 } else if (opacity == 1.0f) {
171 if (allChannelsFlag && !alphaLocked) {
172 if (srcAlpha == 0.0f) {
173 KoStreamedMathFunctions::clearPixel<sizeof(Pixel)>(dst);
174 } else {
175 KoStreamedMathFunctions::copyPixel<sizeof(Pixel)>(src, dst);
176 }
177 } else {
179 alphaLocked) {
180
181 const QBitArray &channelFlags = oparams.channelFlags;
182 if (channelFlags.at(0)) d[0] = s[0];
183 if (channelFlags.at(1)) d[1] = s[1];
184 if (channelFlags.at(2)) d[2] = s[2];
185 if (!alphaLocked) d[3] = s[3];
186
187 } else {
188 // Precondition: d[alpha_pos] == 0 && !alphaLocked
189
190 const QBitArray &channelFlags = oparams.channelFlags;
191 d[0] = channelFlags.at(0) ? s[0] : KoColorSpaceMathsTraits<channels_type>::zeroValue;
192 d[1] = channelFlags.at(1) ? s[1] : KoColorSpaceMathsTraits<channels_type>::zeroValue;
193 d[2] = channelFlags.at(2) ? s[2] : KoColorSpaceMathsTraits<channels_type>::zeroValue;
194 d[3] = s[3];
195 }
196 }
197 } else {
198 float dstAlpha = d[alpha_pos];
200
201 float newAlpha = dstAlpha + opacity * (srcAlpha - dstAlpha);
202
203 if (newAlpha == 0.0f) {
204 if ((allChannelsFlag && !alphaLocked) || dstAlpha == 0.0f) {
205 KoStreamedMathFunctions::clearPixel<sizeof(Pixel)>(dst);
206 } else {
207 const QBitArray &channelFlags = oparams.channelFlags;
208 if (channelFlags.at(0)) d[0] = KoColorSpaceMathsTraits<channels_type>::zeroValue;
209 if (channelFlags.at(1)) d[1] = KoColorSpaceMathsTraits<channels_type>::zeroValue;
210 if (channelFlags.at(2)) d[2] = KoColorSpaceMathsTraits<channels_type>::zeroValue;
212 }
213 } else {
214 float src_c1 = s[0];
215 float src_c2 = s[1];
216 float src_c3 = s[2];
217
218 float dst_c1 = d[0];
219 float dst_c2 = d[1];
220 float dst_c3 = d[2];
221
222 src_c1 *= srcAlpha;
223 src_c2 *= srcAlpha;
224 src_c3 *= srcAlpha;
225
226 dst_c1 *= dstAlpha;
227 dst_c2 *= dstAlpha;
228 dst_c3 *= dstAlpha;
229
230 dst_c1 += opacity * (src_c1 - dst_c1);
231 dst_c2 += opacity * (src_c2 - dst_c2);
232 dst_c3 += opacity * (src_c3 - dst_c3);
233
234 if (newAlpha != 1.0f) {
235 dst_c1 /= newAlpha;
236 dst_c2 /= newAlpha;
237 dst_c3 /= newAlpha;
238
240
241 dst_c1 = std::isnan(dst_c1) ? unitValue : dst_c1;
242 dst_c2 = std::isnan(dst_c2) ? unitValue : dst_c2;
243 dst_c3 = std::isnan(dst_c3) ? unitValue : dst_c3;
244
245 dst_c1 = std::min(dst_c1, unitValue);
246 dst_c2 = std::min(dst_c2, unitValue);
247 dst_c3 = std::min(dst_c3, unitValue);
248 }
249
250 if (allChannelsFlag) {
254 } else {
255 if (dstAlpha != 0.0f || alphaLocked) {
256 const QBitArray &channelFlags = oparams.channelFlags;
257 if (channelFlags.at(0)) d[0] = PixelWrapper<channels_type, _impl>::roundFloatToUint(dst_c1);
258 if (channelFlags.at(1)) d[1] = PixelWrapper<channels_type, _impl>::roundFloatToUint(dst_c2);
259 if (channelFlags.at(2)) d[2] = PixelWrapper<channels_type, _impl>::roundFloatToUint(dst_c3);
260 } else {
261 // Precondition: dstAlpha == 0 && !alphaLocked
262 const QBitArray &channelFlags = oparams.channelFlags;
263 d[0] = channelFlags.at(0) ? dst_c1 : KoColorSpaceMathsTraits<channels_type>::zeroValue;
264 d[1] = channelFlags.at(1) ? dst_c2 : KoColorSpaceMathsTraits<channels_type>::zeroValue;
265 d[2] = channelFlags.at(2) ? dst_c3 : KoColorSpaceMathsTraits<channels_type>::zeroValue;
266 }
267 }
268
269 if (!alphaLocked) {
272 }
273 }
274 }
275 }
276};
277
283template<typename _impl>
285{
286public:
289
291
292 void composite(const KoCompositeOp::ParameterInfo& params) const override
293 {
294 if(params.maskRowStart) {
295 composite<true>(params);
296 } else {
297 composite<false>(params);
298 }
299 }
300
301 template <bool haveMask>
302 inline void composite(const KoCompositeOp::ParameterInfo& params) const {
303 if (params.channelFlags.isEmpty() ||
304 params.channelFlags == QBitArray(4, true)) {
305
306 KoStreamedMath<_impl>::template genericComposite128<haveMask, false, CopyCompositor128<float, false, true> >(params);
307 } else {
308 const bool allChannelsFlag =
309 params.channelFlags.at(0) &&
310 params.channelFlags.at(1) &&
311 params.channelFlags.at(2);
312
313 const bool alphaLocked =
314 !params.channelFlags.at(3);
315
316 if (allChannelsFlag && alphaLocked) {
317 KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, CopyCompositor128<float, true, true> >(params);
318 } else if (!allChannelsFlag && !alphaLocked) {
319 KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, CopyCompositor128<float, false, false> >(params);
320 } else /*if (!allChannelsFlag && alphaLocked) */{
321 KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, CopyCompositor128<float, true, false> >(params);
322 }
323 }
324 }
325};
326
327template<typename _impl>
329{
330public:
333
335
336 void composite(const KoCompositeOp::ParameterInfo& params) const override
337 {
338 if(params.maskRowStart) {
339 composite<true>(params);
340 } else {
341 composite<false>(params);
342 }
343 }
344
345 template <bool haveMask>
346 inline void composite(const KoCompositeOp::ParameterInfo& params) const {
347 if (params.channelFlags.isEmpty() ||
348 params.channelFlags == QBitArray(4, true)) {
349
350 KoStreamedMath<_impl>::template genericComposite64<haveMask, false, CopyCompositor128<quint16, false, true> >(params);
351 } else {
352 const bool allChannelsFlag =
353 params.channelFlags.at(0) &&
354 params.channelFlags.at(1) &&
355 params.channelFlags.at(2);
356
357 const bool alphaLocked =
358 !params.channelFlags.at(3);
359
360 if (allChannelsFlag && alphaLocked) {
361 KoStreamedMath<_impl>::template genericComposite64_novector<haveMask, false, CopyCompositor128<quint16, true, true> >(params);
362 } else if (!allChannelsFlag && !alphaLocked) {
363 KoStreamedMath<_impl>::template genericComposite64_novector<haveMask, false, CopyCompositor128<quint16, false, false> >(params);
364 } else /*if (!allChannelsFlag && alphaLocked) */{
365 KoStreamedMath<_impl>::template genericComposite64_novector<haveMask, false, CopyCompositor128<quint16, true, false> >(params);
366 }
367 }
368 }
369};
370
371
372template<typename _impl>
374{
375public:
378
380
381 void composite(const KoCompositeOp::ParameterInfo& params) const override
382 {
383 if(params.maskRowStart) {
384 composite<true>(params);
385 } else {
386 composite<false>(params);
387 }
388 }
389
390 template <bool haveMask>
391 inline void composite(const KoCompositeOp::ParameterInfo& params) const {
392 if (params.channelFlags.isEmpty() ||
393 params.channelFlags == QBitArray(4, true)) {
394
395 KoStreamedMath<_impl>::template genericComposite32<haveMask, false, CopyCompositor128<quint8, false, true> >(params);
396 } else {
397 const bool allChannelsFlag =
398 params.channelFlags.at(0) &&
399 params.channelFlags.at(1) &&
400 params.channelFlags.at(2);
401
402 const bool alphaLocked =
403 !params.channelFlags.at(3);
404
405 if (allChannelsFlag && alphaLocked) {
406 KoStreamedMath<_impl>::template genericComposite32_novector<haveMask, false, CopyCompositor128<quint8, true, true> >(params);
407 } else if (!allChannelsFlag && !alphaLocked) {
408 KoStreamedMath<_impl>::template genericComposite32_novector<haveMask, false, CopyCompositor128<quint8, false, false> >(params);
409 } else /*if (!allChannelsFlag && alphaLocked) */{
410 KoStreamedMath<_impl>::template genericComposite32_novector<haveMask, false, CopyCompositor128<quint8, true, false> >(params);
411 }
412 }
413 }
414};
415#endif // KOOPTIMIZEDCOMPOSITEOPCOPY128_H_
#define ALWAYS_INLINE
const QString COMPOSITE_COPY
void composite(const KoCompositeOp::ParameterInfo &params) const override
void composite(const KoCompositeOp::ParameterInfo &params) const
KoOptimizedCompositeOpCopy128(const KoColorSpace *cs)
KoOptimizedCompositeOpCopy32(const KoColorSpace *cs)
void composite(const KoCompositeOp::ParameterInfo &params) const
void composite(const KoCompositeOp::ParameterInfo &params) const override
void composite(const KoCompositeOp::ParameterInfo &params) const override
void composite(const KoCompositeOp::ParameterInfo &params) const
KoOptimizedCompositeOpCopyU64(const KoColorSpace *cs)
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)
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)
ALWAYS_INLINE void recoverPixels(const float_m &mask, float_v &c1, float_v &c2, float_v &c3) const