Krita Source Code Documentation
Loading...
Searching...
No Matches
CopyCompositor128< channels_type, alphaLocked, allChannelsFlag > Struct Template Reference

#include <KoOptimizedCompositeOpCopy128.h>

Classes

struct  ParamsWrapper
 
struct  Pixel
 

Static Public Member Functions

template<bool haveMask, typename _impl = xsimd::current_arch>
static ALWAYS_INLINE void compositeOnePixelScalar (const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
 
template<bool haveMask, bool src_aligned, typename _impl >
static ALWAYS_INLINE void compositeVector (const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
 

Detailed Description

template<typename channels_type, bool alphaLocked, bool allChannelsFlag>
struct CopyCompositor128< channels_type, alphaLocked, allChannelsFlag >

Definition at line 19 of file KoOptimizedCompositeOpCopy128.h.

Member Function Documentation

◆ compositeOnePixelScalar()

template<typename channels_type , bool alphaLocked, bool allChannelsFlag>
template<bool haveMask, typename _impl = xsimd::current_arch>
static ALWAYS_INLINE void CopyCompositor128< channels_type, alphaLocked, allChannelsFlag >::compositeOnePixelScalar ( const quint8 * src,
quint8 * dst,
const quint8 * mask,
float opacity,
const ParamsWrapper & oparams )
inlinestatic

Definition at line 142 of file KoOptimizedCompositeOpCopy128.h.

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 }

References CopyCompositor128< channels_type, alphaLocked, allChannelsFlag >::ParamsWrapper::channelFlags.

◆ compositeVector()

template<typename channels_type , bool alphaLocked, bool allChannelsFlag>
template<bool haveMask, bool src_aligned, typename _impl >
static ALWAYS_INLINE void CopyCompositor128< channels_type, alphaLocked, allChannelsFlag >::compositeVector ( const quint8 * src,
quint8 * dst,
const quint8 * mask,
float opacity,
const ParamsWrapper & oparams )
inlinestatic

This division by newAlpha may be unsafe in case some elements of newAlpha are null. We don't care, because:

1) the value will be clamped by xsimd::min a bit later;

2) even if it doesn't, the new alpha will be null, so the state of the color channels is undefined

Roundtrip dst * dst_alpha / newAlpha may not be possible (it happens when mask (and therefore opacity) is null). In such case we should recover original value of the pixels to be consistent with the scalar version of the algorithm.

Definition at line 36 of file KoOptimizedCompositeOpCopy128.h.

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 }
xsimd::batch< float, _impl > float_v
static float_v fetch_mask_8(const quint8 *data)

References KoStreamedMath< _impl >::fetch_mask_8(), and PixelStateRecoverHelper< channels_type, _impl >::recoverPixels().


The documentation for this struct was generated from the following file: