Krita Source Code Documentation
Loading...
Searching...
No Matches
KoOptimizedCompositeOpOver32.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 KOOPTIMIZEDCOMPOSITEOPOVER32_H_
10#define KOOPTIMIZEDCOMPOSITEOPOVER32_H_
11
12#include <math.h>
13
14#include "KoCompositeOpBase.h"
16#include "KoStreamedMath.h"
17
18
19template<typename channels_type, typename pixel_type, bool alphaLocked, bool allChannelsFlag>
26 const QBitArray &channelFlags;
27 };
28
29 // \see docs in AlphaDarkenCompositor32
30 template<bool haveMask, bool src_aligned, typename _impl>
31 static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
32 {
33 Q_UNUSED(oparams);
34
35 using float_v = typename KoStreamedMath<_impl>::float_v;
36
37 float_v src_alpha = KoStreamedMath<_impl>::template fetch_alpha_32<src_aligned>(src);
38 float_v dst_alpha;
39
40 const bool haveOpacity = opacity != 1.0f;
41 const float_v opacity_norm_vec(opacity);
42
43 const float_v uint8Max(255.0f);
44 const float_v uint8MaxRec1(1.0f / 255.0f);
45 const float_v zeroValue(0);
46 const float_v oneValue(1);
47
48 src_alpha *= opacity_norm_vec;
49
50 if (haveMask) {
51 const float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask);
52 src_alpha *= mask_vec * uint8MaxRec1;
53 }
54
55 // The source cannot change the colors in the destination,
56 // since its fully transparent
57 if (xsimd::all(src_alpha == zeroValue)) {
58 return;
59 }
60
61 dst_alpha = KoStreamedMath<_impl>::template fetch_alpha_32<true>(dst);
62
63 float_v src_c1;
64 float_v src_c2;
65 float_v src_c3;
66
67 float_v dst_c1;
68 float_v dst_c2;
69 float_v dst_c3;
70
71
72 KoStreamedMath<_impl>::template fetch_colors_32<src_aligned>(src, src_c1, src_c2, src_c3);
73 float_v src_blend;
74 float_v new_alpha;
75
76 if (xsimd::all(dst_alpha == uint8Max)) {
77 new_alpha = dst_alpha;
78 src_blend = src_alpha * uint8MaxRec1;
79 } else if (xsimd::all(dst_alpha == zeroValue)) {
80 new_alpha = src_alpha;
81 src_blend = oneValue;
82 } else {
89 new_alpha = dst_alpha + (uint8Max - dst_alpha) * src_alpha * uint8MaxRec1;
90
91 // Optimized version of:
92 // src_blend = src_alpha / new_alpha;
93 src_blend = OptiDiv<_impl>::divVector(src_alpha, new_alpha);
94
95 }
96
97 if (!xsimd::all(src_blend == oneValue)) {
98 KoStreamedMath<_impl>::template fetch_colors_32<true>(dst, dst_c1, dst_c2, dst_c3);
99
100 dst_c1 = src_blend * (src_c1 - dst_c1) + dst_c1;
101 dst_c2 = src_blend * (src_c2 - dst_c2) + dst_c2;
102 dst_c3 = src_blend * (src_c3 - dst_c3) + dst_c3;
103
104 } else {
105 if (!haveMask && !haveOpacity) {
106 memcpy(dst, src, 4 * float_v::size);
107 return;
108 } else {
109 // opacity has changed the alpha of the source,
110 // so we can't just memcpy the bytes
111 dst_c1 = src_c1;
112 dst_c2 = src_c2;
113 dst_c3 = src_c3;
114 }
115 }
116
117 KoStreamedMath<_impl>::write_channels_32(dst, new_alpha, dst_c1, dst_c2, dst_c3);
118 }
119
120 template <bool haveMask, typename _impl>
121 static ALWAYS_INLINE void compositeOnePixelScalar(const channels_type *src, channels_type *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams)
122 {
123 using namespace Arithmetic;
124 const qint32 alpha_pos = 3;
125
126 const float uint8Rec1 = 1.0f / 255.0f;
127 const float uint8Max = 255.0f;
128
129 float srcAlpha = src[alpha_pos];
130 srcAlpha *= opacity;
131
132 if (haveMask) {
133 srcAlpha *= float(*mask) * uint8Rec1;
134 }
135
136 if (srcAlpha != 0.0f) {
137
138 float dstAlpha = dst[alpha_pos];
139 float srcBlendNorm = NAN;
140
141 if (alphaLocked || dstAlpha == uint8Max) {
142 srcBlendNorm = srcAlpha * uint8Rec1;
143 } else if (dstAlpha == 0.0f) {
144 dstAlpha = srcAlpha;
145 srcBlendNorm = 1.0f;
146
147 if (!allChannelsFlag) {
148 auto *d = reinterpret_cast<pixel_type*>(dst);
149 *d = 0; // dstAlpha is already null
150 }
151 } else {
152 dstAlpha += (uint8Max - dstAlpha) * srcAlpha * uint8Rec1;
153 // Optimized version of:
154 // srcBlendNorm = srcAlpha / dstAlpha);
155 srcBlendNorm = OptiDiv<_impl>::divScalar(srcAlpha, dstAlpha);
156 }
157
158 if(allChannelsFlag) {
159 if (srcBlendNorm == 1.0f) {
160 if (!alphaLocked) {
161 const auto *s = reinterpret_cast<const pixel_type*>(src);
162 auto *d = reinterpret_cast<pixel_type*>(dst);
163 *d = *s;
164 } else {
165 dst[0] = src[0];
166 dst[1] = src[1];
167 dst[2] = src[2];
168 }
169 } else if (srcBlendNorm != 0.0f){
170 dst[0] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[0], src[0], srcBlendNorm);
171 dst[1] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[1], src[1], srcBlendNorm);
172 dst[2] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[2], src[2], srcBlendNorm);
173 }
174 } else {
175 const QBitArray &channelFlags = oparams.channelFlags;
176
177 if (srcBlendNorm == 1.0f) {
178 if(channelFlags.at(0)) dst[0] = src[0];
179 if(channelFlags.at(1)) dst[1] = src[1];
180 if(channelFlags.at(2)) dst[2] = src[2];
181 } else if (srcBlendNorm != 0.0f) {
182 if(channelFlags.at(0)) dst[0] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[0], src[0], srcBlendNorm);
183 if(channelFlags.at(1)) dst[1] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[1], src[1], srcBlendNorm);
184 if(channelFlags.at(2)) dst[2] = KoStreamedMath<_impl>::lerp_mixed_u8_float(dst[2], src[2], srcBlendNorm);
185 }
186 }
187
188 if (!alphaLocked) {
189 dst[alpha_pos] = KoStreamedMath<_impl>::round_float_to_u8(dstAlpha);
190 }
191 }
192 }
193};
194
200template<typename _impl>
202{
203public:
206
208
209 void composite(const KoCompositeOp::ParameterInfo& params) const override
210 {
211 if(params.maskRowStart) {
212 composite<true>(params);
213 } else {
214 composite<false>(params);
215 }
216 }
217
218 template <bool haveMask>
219 inline void composite(const KoCompositeOp::ParameterInfo& params) const {
220 if (params.channelFlags.isEmpty() ||
221 params.channelFlags == QBitArray(4, true)) {
222
223 KoStreamedMath<_impl>::template genericComposite32<haveMask, false, OverCompositor32<quint8, quint32, false, true> >(params);
224 } else {
225 const bool allChannelsFlag =
226 params.channelFlags.at(0) &&
227 params.channelFlags.at(1) &&
228 params.channelFlags.at(2);
229
230 const bool alphaLocked =
231 !params.channelFlags.at(3);
232
233 if (allChannelsFlag && alphaLocked) {
234 KoStreamedMath<_impl>::template genericComposite32_novector<haveMask, false, OverCompositor32<quint8, quint32, true, true> >(params);
235 } else if (!allChannelsFlag && !alphaLocked) {
236 KoStreamedMath<_impl>::template genericComposite32_novector<haveMask, false, OverCompositor32<quint8, quint32, false, false> >(params);
237 } else /*if (!allChannelsFlag && alphaLocked) */{
238 KoStreamedMath<_impl>::template genericComposite32_novector<haveMask, false, OverCompositor32<quint8, quint32, true, false> >(params);
239 }
240 }
241 }
242};
243
244#endif // KOOPTIMIZEDCOMPOSITEOPOVER32_H_
#define ALWAYS_INLINE
const QString COMPOSITE_OVER
void composite(const KoCompositeOp::ParameterInfo &params) const override
KoOptimizedCompositeOpOver32(const KoColorSpace *cs)
void composite(const KoCompositeOp::ParameterInfo &params) const
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)
static ALWAYS_INLINE float divScalar(const float &divident, const float &divisor)
static ALWAYS_INLINE float_v divVector(const float_v &divident, const float_v &divisor)
ParamsWrapper(const KoCompositeOp::ParameterInfo &params)
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)