Krita Source Code Documentation
Loading...
Searching...
No Matches
KoOptimizedCompositeOpAlphaDarken32.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2006 Cyrille Berger <cberger@cberger.net>
3 * SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de>
4 * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me>
5 *
6 * SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8
9#ifndef KOOPTIMIZEDCOMPOSITEOPALPHADARKEN32_H_
10#define KOOPTIMIZEDCOMPOSITEOPALPHADARKEN32_H_
11
12#include "KoCompositeOpBase.h"
14#include "KoStreamedMath.h"
16
17template<typename channels_type, typename pixel_type, typename ParamsWrapperT>
19 using ParamsWrapper = ParamsWrapperT;
20
33 template<bool haveMask, bool src_aligned, typename _impl>
34 static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
35 {
36 using float_v = typename KoStreamedMath<_impl>::float_v;
37 using float_m = typename float_v::batch_bool_type;
38
39 // we don't use directly passed value
40 Q_UNUSED(opacity);
41
42 // instead we use value calculated by ParamsWrapper
43 opacity = oparams.opacity;
44 const float_v opacity_vec(255.0f * opacity);
45
46 const float_v average_opacity_vec(255.0 * oparams.averageOpacity);
47 const float_v flow_norm_vec(oparams.flow);
48
49 const float_v uint8MaxRec2(1.0f / (255.0f * 255.0f));
50 const float_v uint8MaxRec1(1.0f / 255.0f);
51 const float_v uint8Max(255.0f);
52 const float_v zeroValue(0);
53
54 float_v src_alpha = KoStreamedMath<_impl>::template fetch_alpha_32<src_aligned>(src);
55
56 const float_v msk_norm_alpha = [&]() {
57 if (haveMask) {
58 const float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask);
59 return src_alpha * mask_vec * uint8MaxRec2;
60 } else {
61 return src_alpha * uint8MaxRec1;
62 }
63 }();
64
65 float_v dst_alpha = KoStreamedMath<_impl>::template fetch_alpha_32<true>(dst);
66 src_alpha = msk_norm_alpha * opacity_vec;
67
68 const float_m empty_dst_pixels_mask = dst_alpha == zeroValue;
69
70 float_v src_c1;
71 float_v src_c2;
72 float_v src_c3;
73
74 float_v dst_c1;
75 float_v dst_c2;
76 float_v dst_c3;
77
78 KoStreamedMath<_impl>::template fetch_colors_32<src_aligned>(src, src_c1, src_c2, src_c3);
79
80 const bool srcAlphaIsZero = xsimd::all(src_alpha == zeroValue);
81 if (srcAlphaIsZero) return;
82
83 const bool dstAlphaIsZero = xsimd::all(empty_dst_pixels_mask);
84
85 const float_v dst_blend = src_alpha * uint8MaxRec1;
86
87 const bool srcAlphaIsUnit = xsimd::all(src_alpha == uint8Max);
88
89 if (dstAlphaIsZero) {
90 dst_c1 = src_c1;
91 dst_c2 = src_c2;
92 dst_c3 = src_c3;
93 } else if (srcAlphaIsUnit) {
94 const bool dstAlphaIsUnit = xsimd::all(dst_alpha == uint8Max);
95 if (dstAlphaIsUnit) {
96 memcpy(dst, src, 4 * float_v::size);
97 return;
98 } else {
99 dst_c1 = src_c1;
100 dst_c2 = src_c2;
101 dst_c3 = src_c3;
102 }
103 } else if (xsimd::none(empty_dst_pixels_mask)) {
104 KoStreamedMath<_impl>::template fetch_colors_32<true>(dst, dst_c1, dst_c2, dst_c3);
105 dst_c1 = dst_blend * (src_c1 - dst_c1) + dst_c1;
106 dst_c2 = dst_blend * (src_c2 - dst_c2) + dst_c2;
107 dst_c3 = dst_blend * (src_c3 - dst_c3) + dst_c3;
108 } else {
109 KoStreamedMath<_impl>::template fetch_colors_32<true>(dst, dst_c1, dst_c2, dst_c3);
110 dst_c1 = xsimd::select(empty_dst_pixels_mask, src_c1, dst_blend * (src_c1 - dst_c1) + dst_c1);
111 dst_c2 = xsimd::select(empty_dst_pixels_mask, src_c2, dst_blend * (src_c2 - dst_c2) + dst_c2);
112 dst_c3 = xsimd::select(empty_dst_pixels_mask, src_c3, dst_blend * (src_c3 - dst_c3) + dst_c3);
113 }
114
115 const float_v fullFlowAlpha = [&]() {
116 if (oparams.averageOpacity > opacity) {
117 const float_m fullFlowAlpha_mask = average_opacity_vec > dst_alpha;
118
119 if (xsimd::none(fullFlowAlpha_mask)) {
120 return dst_alpha;
121 } else {
122 const float_v reverse_blend = dst_alpha / average_opacity_vec;
123 const float_v opt1 = (average_opacity_vec - src_alpha) * reverse_blend + src_alpha;
124 return xsimd::select(fullFlowAlpha_mask, opt1, dst_alpha);
125 }
126 } else {
127 const float_m fullFlowAlpha_mask = opacity_vec > dst_alpha;
128
129 if (xsimd::none(fullFlowAlpha_mask)) {
130 return dst_alpha;
131 } else {
132 const float_v opt1 = (opacity_vec - dst_alpha) * msk_norm_alpha + dst_alpha;
133 return xsimd::select(fullFlowAlpha_mask, opt1, dst_alpha);
134 }
135 }
136 }();
137
138 dst_alpha = [&]() {
139 if (oparams.flow == 1.0) {
140 return fullFlowAlpha;
141 } else {
142 const float_v zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(src_alpha, dst_alpha, uint8MaxRec1);
143 return (fullFlowAlpha - zeroFlowAlpha) * flow_norm_vec + zeroFlowAlpha;
144 }
145 }();
146
147 KoStreamedMath<_impl>::write_channels_32(dst, dst_alpha, dst_c1, dst_c2, dst_c3);
148 }
149
153 template <bool haveMask, typename _impl>
154 static ALWAYS_INLINE void compositeOnePixelScalar(const channels_type *src, channels_type *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
155 {
156 using namespace Arithmetic;
157 const qint32 alpha_pos = 3;
158
159 const float uint8Rec1 = 1.0f / 255.0f;
160 const float uint8Rec2 = 1.0f / (255.0f * 255.0f);
161 const float uint8Max = 255.0;
162
163 // we don't use directly passed value
164 Q_UNUSED(opacity);
165
166 // instead we use value calculated by ParamsWrapper
167 opacity = oparams.opacity;
168
169 const quint8 dstAlphaInt = dst[alpha_pos];
170 const float dstAlphaNorm = dstAlphaInt ? dstAlphaInt * uint8Rec1 : 0.0;
171 const float mskAlphaNorm = [&]() {
172 if (haveMask) {
173 return float(*mask) * uint8Rec2 * src[alpha_pos];
174 } else {
175 return src[alpha_pos] * uint8Rec1;
176 }
177 }();
178 const float srcAlphaNorm = mskAlphaNorm * opacity;
179
180 if (dstAlphaInt != 0) {
181 dst[0] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[0], src[0], srcAlphaNorm);
182 dst[1] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[1], src[1], srcAlphaNorm);
183 dst[2] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[2], src[2], srcAlphaNorm);
184 } else {
185 const auto *s = reinterpret_cast<const pixel_type*>(src);
186 auto *d = reinterpret_cast<pixel_type*>(dst);
187 *d = *s;
188 }
189
190
191 const float flow = oparams.flow;
192 const float averageOpacity = oparams.averageOpacity;
193
194 const float fullFlowAlpha = [&]() {
195 if (averageOpacity > opacity) {
196 return averageOpacity > dstAlphaNorm ? lerp(srcAlphaNorm, averageOpacity, dstAlphaNorm / averageOpacity) : dstAlphaNorm;
197 } else {
198 return opacity > dstAlphaNorm ? lerp(dstAlphaNorm, opacity, mskAlphaNorm) : dstAlphaNorm;
199 }
200 }();
201
202 const float dstAlpha = [&](){
203 if (flow == 1.0f) {
204 return fullFlowAlpha * uint8Max;
205 } else {
206 const float zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(srcAlphaNorm, dstAlphaNorm);
207 return lerp(zeroFlowAlpha, fullFlowAlpha, flow) * uint8Max;
208 }
209 }();
210
211 dst[alpha_pos] = KoStreamedMath<_impl>::round_float_to_u8(dstAlpha);
212 }
213};
214
220template<typename _impl, class ParamsWrapper>
222{
223public:
226
228
229 void composite(const KoCompositeOp::ParameterInfo& params) const override
230 {
231 if(params.maskRowStart) {
232 KoStreamedMath<_impl>::template genericComposite32<true, true, AlphaDarkenCompositor32<quint8, quint32, ParamsWrapper> >(params);
233 } else {
234 KoStreamedMath<_impl>::template genericComposite32<false, true, AlphaDarkenCompositor32<quint8, quint32, ParamsWrapper> >(params);
235 }
236 }
237};
238
239template<typename _impl = xsimd::current_arch>
248
249template<typename _impl = xsimd::current_arch>
258
259
260#endif // KOOPTIMIZEDCOMPOSITEOPALPHADARKEN32_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
static ALWAYS_INLINE void compositeOnePixelScalar(const channels_type *src, channels_type *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
static ALWAYS_INLINE void compositeVector(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()
static void write_channels_32(void *data, const float_v alpha, const float_v c1, const float_v c2, const float_v c3)
xsimd::batch< float, _impl > float_v
static float_v fetch_mask_8(const quint8 *data)
static quint8 lerp_mixed_u8_float(quint8 a, quint8 b, float alpha)
static quint8 round_float_to_u8(float x)