Krita Source Code Documentation
Loading...
Searching...
No Matches
KoOptimizedCompositeOpAlphaDarken128.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2016 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 KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H
9#define KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H
10
11#include "KoCompositeOpBase.h"
13#include "KoStreamedMath.h"
15
16template<typename channels_type, typename ParamsWrapperT>
18 using ParamsWrapper = ParamsWrapperT;
19
20 struct Pixel {
21 channels_type red;
22 channels_type green;
23 channels_type blue;
24 channels_type alpha;
25 };
26
39 template<bool haveMask, bool src_aligned, typename _impl>
40 static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
41 {
42 using float_v = typename KoStreamedMath<_impl>::float_v;
43 using float_m = typename float_v::batch_bool_type;
44
45 float_v src_c1;
46 float_v src_c2;
47 float_v src_c3;
48 float_v src_alpha;
49
51 dataWrapper.read(src, src_c1, src_c2, src_c3, src_alpha);
52
53 const float_v msk_norm_alpha = [&](){;
54 if (haveMask) {
55 const float_v uint8Rec1(1.0f / 255.0f);
56 const float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask);
57 return mask_vec * uint8Rec1 * src_alpha;
58 }
59 else {
60 return src_alpha;
61 }
62 }();
63
64 // we don't use directly passed value
65 Q_UNUSED(opacity);
66
67 // instead we use value calculated by ParamsWrapper
68 opacity = oparams.opacity;
69 const float_v opacity_vec(opacity);
70
71 src_alpha = msk_norm_alpha * opacity_vec;
72
73 const float_v zeroValue(static_cast<float>(KoColorSpaceMathsTraits<channels_type>::zeroValue));
74
75 float_v dst_c1;
76 float_v dst_c2;
77 float_v dst_c3;
78 float_v dst_alpha;
79
80 dataWrapper.read(dst, dst_c1, dst_c2, dst_c3, dst_alpha);
81
82 const float_m empty_dst_pixels_mask = dst_alpha == zeroValue;
83
84 if (!xsimd::all(empty_dst_pixels_mask)) {
85 if (xsimd::none(empty_dst_pixels_mask)) {
86 dst_c1 = (src_c1 - dst_c1) * src_alpha + dst_c1;
87 dst_c2 = (src_c2 - dst_c2) * src_alpha + dst_c2;
88 dst_c3 = (src_c3 - dst_c3) * src_alpha + dst_c3;
89 }
90 else {
91 dst_c1 = xsimd::select(empty_dst_pixels_mask, src_c1, (src_c1 - dst_c1) * src_alpha + dst_c1);
92 dst_c2 = xsimd::select(empty_dst_pixels_mask, src_c2, (src_c2 - dst_c2) * src_alpha + dst_c2);
93 dst_c3 = xsimd::select(empty_dst_pixels_mask, src_c3, (src_c3 - dst_c3) * src_alpha + dst_c3);
94 }
95 }
96 else {
97 dst_c1 = src_c1;
98 dst_c2 = src_c2;
99 dst_c3 = src_c3;
100 }
101
102 const float_v fullFlowAlpha = [&]() {
103 if (oparams.averageOpacity > opacity) {
104 const float_v average_opacity_vec(oparams.averageOpacity);
105 const float_m fullFlowAlpha_mask = average_opacity_vec > dst_alpha;
106 return xsimd::select(fullFlowAlpha_mask,
107 (average_opacity_vec - src_alpha)
108 * (dst_alpha / average_opacity_vec)
109 + src_alpha,
110 dst_alpha);
111 } else {
112 const float_m fullFlowAlpha_mask = opacity_vec > dst_alpha;
113 return xsimd::select(
114 fullFlowAlpha_mask,
115 (opacity_vec - dst_alpha) * msk_norm_alpha + dst_alpha,
116 dst_alpha);
117 }
118 }();
119
120 dst_alpha = [&]() {
121 if (oparams.flow == 1.0) {
122 return fullFlowAlpha;
123 }
124 else {
125 const float_v zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(src_alpha, dst_alpha);
126 const float_v flow_norm_vec(oparams.flow);
127 return (fullFlowAlpha - zeroFlowAlpha) * flow_norm_vec + zeroFlowAlpha;
128 }
129 }();
130
131 dataWrapper.write(dst, dst_c1, dst_c2, dst_c3, dst_alpha);
132 }
133
137 template <bool haveMask, typename _impl>
138 static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *s, quint8 *d, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
139 {
140 using namespace Arithmetic;
141 const qint32 alpha_pos = 3;
142
143 const auto *src = reinterpret_cast<const channels_type*>(s);
144 auto *dst = reinterpret_cast<channels_type*>(d);
145
146 float dstAlphaNorm = dst[alpha_pos];
148
149 const float uint8Rec1 = 1.0f / 255.0f;
150 float mskAlphaNorm = haveMask ? float(*mask) * uint8Rec1 * src[alpha_pos] : src[alpha_pos];
152
153 Q_UNUSED(opacity);
154 opacity = oparams.opacity;
155
156 const float srcAlphaNorm = mskAlphaNorm * opacity;
157
158 if (dstAlphaNorm != 0) {
159 dst[0] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(dst[0], src[0], srcAlphaNorm);
160 dst[1] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(dst[1], src[1], srcAlphaNorm);
161 dst[2] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(dst[2], src[2], srcAlphaNorm);
162 } else {
163 const auto *s = reinterpret_cast<const Pixel*>(src);
164 auto *d = reinterpret_cast<Pixel*>(dst);
165 *d = *s;
166 }
167
168 const float flow = oparams.flow;
169 const float averageOpacity = oparams.averageOpacity;
170
171 const float fullFlowAlpha = [&]() {
172 if (averageOpacity > opacity) {
173 return averageOpacity > dstAlphaNorm ? lerp(srcAlphaNorm, averageOpacity, dstAlphaNorm / averageOpacity) : dstAlphaNorm;
174 } else {
175 return opacity > dstAlphaNorm ? lerp(dstAlphaNorm, opacity, mskAlphaNorm) : dstAlphaNorm;
176 }
177 }();
178
179 dstAlphaNorm = [&]() {
180 if (flow == 1.0f) {
181 return fullFlowAlpha;
182 } else {
183 const float zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(srcAlphaNorm, dstAlphaNorm);
184 return lerp(zeroFlowAlpha, fullFlowAlpha, flow);
185 }
186 }();
187
189 dst[alpha_pos] = PixelWrapper<channels_type, _impl>::roundFloatToUint(dstAlphaNorm);
190 }
191};
192
198template<typename _impl, typename ParamsWrapper>
200{
201public:
204
206
207 void composite(const KoCompositeOp::ParameterInfo& params) const override
208 {
209 if(params.maskRowStart) {
210 KoStreamedMath<_impl>::template genericComposite128<true, true, AlphaDarkenCompositor128<float, ParamsWrapper> >(params);
211 } else {
212 KoStreamedMath<_impl>::template genericComposite128<false, true, AlphaDarkenCompositor128<float, ParamsWrapper> >(params);
213 }
214 }
215};
216
217template<typename _impl>
225
226template<typename _impl>
234
235template<typename _impl, typename ParamsWrapper>
237{
238public:
241
243
244 void composite(const KoCompositeOp::ParameterInfo& params) const override
245 {
246 if(params.maskRowStart) {
247 KoStreamedMath<_impl>::template genericComposite64<true, true, AlphaDarkenCompositor128<quint16, ParamsWrapper> >(params);
248 } else {
249 KoStreamedMath<_impl>::template genericComposite64<false, true, AlphaDarkenCompositor128<quint16, ParamsWrapper> >(params);
250 }
251 }
252};
253
254template<typename _impl>
262
263template<typename _impl>
271
272
273#endif // KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H
#define ALWAYS_INLINE
const QString COMPOSITE_ALPHA_DARKEN
QPointF lerp(const QPointF &p1, const QPointF &p2, qreal t)
void composite(const KoCompositeOp::ParameterInfo &params) const override
void composite(const KoCompositeOp::ParameterInfo &params) const override
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 *s, quint8 *d, 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)