File indexing completed on 2024-12-22 04:11:40
0001 /* 0002 * SPDX-FileCopyrightText: 2016 Thorsten Zachmann <zachmann@kde.org> 0003 * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #ifndef KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H 0009 #define KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H 0010 0011 #include "KoCompositeOpBase.h" 0012 #include "KoCompositeOpRegistry.h" 0013 #include "KoStreamedMath.h" 0014 #include "KoAlphaDarkenParamsWrapper.h" 0015 0016 template<typename channels_type, typename ParamsWrapperT> 0017 struct AlphaDarkenCompositor128 { 0018 using ParamsWrapper = ParamsWrapperT; 0019 0020 struct Pixel { 0021 channels_type red; 0022 channels_type green; 0023 channels_type blue; 0024 channels_type alpha; 0025 }; 0026 0027 /** 0028 * This is a vector equivalent of compositeOnePixelScalar(). It is considered 0029 * to process float_v::size pixels in a single pass. 0030 * 0031 * o the \p haveMask parameter points whether the real (non-null) mask 0032 * pointer is passed to the function. 0033 * o the \p src pointer may be aligned to vector boundary or may be 0034 * not. In case not, it must be pointed with a special parameter 0035 * \p src_aligned. 0036 * o the \p dst pointer must always(!) be aligned to the boundary 0037 * of a streaming vector. Unaligned writes are really expensive. 0038 * o This function is *never* used if HAVE_XSIMD is not present 0039 */ 0040 template<bool haveMask, bool src_aligned, typename _impl> 0041 static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams) 0042 { 0043 using float_v = typename KoStreamedMath<_impl>::float_v; 0044 using float_m = typename float_v::batch_bool_type; 0045 0046 float_v src_c1; 0047 float_v src_c2; 0048 float_v src_c3; 0049 float_v src_alpha; 0050 0051 PixelWrapper<channels_type, _impl> dataWrapper; 0052 dataWrapper.read(src, src_c1, src_c2, src_c3, src_alpha); 0053 0054 const float_v msk_norm_alpha = [&](){; 0055 if (haveMask) { 0056 const float_v uint8Rec1(1.0f / 255.0f); 0057 const float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask); 0058 return mask_vec * uint8Rec1 * src_alpha; 0059 } 0060 else { 0061 return src_alpha; 0062 } 0063 }(); 0064 0065 // we don't use directly passed value 0066 Q_UNUSED(opacity); 0067 0068 // instead we use value calculated by ParamsWrapper 0069 opacity = oparams.opacity; 0070 const float_v opacity_vec(opacity); 0071 0072 src_alpha = msk_norm_alpha * opacity_vec; 0073 0074 const float_v zeroValue(static_cast<float>(KoColorSpaceMathsTraits<channels_type>::zeroValue)); 0075 0076 float_v dst_c1; 0077 float_v dst_c2; 0078 float_v dst_c3; 0079 float_v dst_alpha; 0080 0081 dataWrapper.read(dst, dst_c1, dst_c2, dst_c3, dst_alpha); 0082 0083 const float_m empty_dst_pixels_mask = dst_alpha == zeroValue; 0084 0085 if (!xsimd::all(empty_dst_pixels_mask)) { 0086 if (xsimd::none(empty_dst_pixels_mask)) { 0087 dst_c1 = (src_c1 - dst_c1) * src_alpha + dst_c1; 0088 dst_c2 = (src_c2 - dst_c2) * src_alpha + dst_c2; 0089 dst_c3 = (src_c3 - dst_c3) * src_alpha + dst_c3; 0090 } 0091 else { 0092 dst_c1 = xsimd::select(empty_dst_pixels_mask, src_c1, (src_c1 - dst_c1) * src_alpha + dst_c1); 0093 dst_c2 = xsimd::select(empty_dst_pixels_mask, src_c2, (src_c2 - dst_c2) * src_alpha + dst_c2); 0094 dst_c3 = xsimd::select(empty_dst_pixels_mask, src_c3, (src_c3 - dst_c3) * src_alpha + dst_c3); 0095 } 0096 } 0097 else { 0098 dst_c1 = src_c1; 0099 dst_c2 = src_c2; 0100 dst_c3 = src_c3; 0101 } 0102 0103 const float_v fullFlowAlpha = [&]() { 0104 if (oparams.averageOpacity > opacity) { 0105 const float_v average_opacity_vec(oparams.averageOpacity); 0106 const float_m fullFlowAlpha_mask = average_opacity_vec > dst_alpha; 0107 return xsimd::select(fullFlowAlpha_mask, 0108 (average_opacity_vec - src_alpha) 0109 * (dst_alpha / average_opacity_vec) 0110 + src_alpha, 0111 dst_alpha); 0112 } else { 0113 const float_m fullFlowAlpha_mask = opacity_vec > dst_alpha; 0114 return xsimd::select( 0115 fullFlowAlpha_mask, 0116 (opacity_vec - dst_alpha) * msk_norm_alpha + dst_alpha, 0117 dst_alpha); 0118 } 0119 }(); 0120 0121 dst_alpha = [&]() { 0122 if (oparams.flow == 1.0) { 0123 return fullFlowAlpha; 0124 } 0125 else { 0126 const float_v zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(src_alpha, dst_alpha); 0127 const float_v flow_norm_vec(oparams.flow); 0128 return (fullFlowAlpha - zeroFlowAlpha) * flow_norm_vec + zeroFlowAlpha; 0129 } 0130 }(); 0131 0132 dataWrapper.write(dst, dst_c1, dst_c2, dst_c3, dst_alpha); 0133 } 0134 0135 /** 0136 * Composes one pixel of the source into the destination 0137 */ 0138 template <bool haveMask, typename _impl> 0139 static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *s, quint8 *d, const quint8 *mask, float opacity, const ParamsWrapper &oparams) 0140 { 0141 using namespace Arithmetic; 0142 const qint32 alpha_pos = 3; 0143 0144 const auto *src = reinterpret_cast<const channels_type*>(s); 0145 auto *dst = reinterpret_cast<channels_type*>(d); 0146 0147 float dstAlphaNorm = dst[alpha_pos]; 0148 PixelWrapper<channels_type, _impl>::normalizeAlpha(dstAlphaNorm); 0149 0150 const float uint8Rec1 = 1.0f / 255.0f; 0151 float mskAlphaNorm = haveMask ? float(*mask) * uint8Rec1 * src[alpha_pos] : src[alpha_pos]; 0152 PixelWrapper<channels_type, _impl>::normalizeAlpha(mskAlphaNorm); 0153 0154 Q_UNUSED(opacity); 0155 opacity = oparams.opacity; 0156 0157 const float srcAlphaNorm = mskAlphaNorm * opacity; 0158 0159 if (dstAlphaNorm != 0) { 0160 dst[0] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(dst[0], src[0], srcAlphaNorm); 0161 dst[1] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(dst[1], src[1], srcAlphaNorm); 0162 dst[2] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(dst[2], src[2], srcAlphaNorm); 0163 } else { 0164 const auto *s = reinterpret_cast<const Pixel*>(src); 0165 auto *d = reinterpret_cast<Pixel*>(dst); 0166 *d = *s; 0167 } 0168 0169 const float flow = oparams.flow; 0170 const float averageOpacity = oparams.averageOpacity; 0171 0172 const float fullFlowAlpha = [&]() { 0173 if (averageOpacity > opacity) { 0174 return averageOpacity > dstAlphaNorm ? lerp(srcAlphaNorm, averageOpacity, dstAlphaNorm / averageOpacity) : dstAlphaNorm; 0175 } else { 0176 return opacity > dstAlphaNorm ? lerp(dstAlphaNorm, opacity, mskAlphaNorm) : dstAlphaNorm; 0177 } 0178 }(); 0179 0180 dstAlphaNorm = [&]() { 0181 if (flow == 1.0f) { 0182 return fullFlowAlpha; 0183 } else { 0184 const float zeroFlowAlpha = ParamsWrapper::calculateZeroFlowAlpha(srcAlphaNorm, dstAlphaNorm); 0185 return lerp(zeroFlowAlpha, fullFlowAlpha, flow); 0186 } 0187 }(); 0188 0189 PixelWrapper<channels_type, _impl>::denormalizeAlpha(dstAlphaNorm); 0190 dst[alpha_pos] = PixelWrapper<channels_type, _impl>::roundFloatToUint(dstAlphaNorm); 0191 } 0192 }; 0193 0194 /** 0195 * An optimized version of a composite op for the use in 16 byte 0196 * colorspaces with alpha channel placed at the last byte of 0197 * the pixel: C1_C2_C3_A. 0198 */ 0199 template<typename _impl, typename ParamsWrapper> 0200 class KoOptimizedCompositeOpAlphaDarken128Impl : public KoCompositeOp 0201 { 0202 public: 0203 KoOptimizedCompositeOpAlphaDarken128Impl(const KoColorSpace* cs) 0204 : KoCompositeOp(cs, COMPOSITE_ALPHA_DARKEN, KoCompositeOp::categoryMix()) {} 0205 0206 using KoCompositeOp::composite; 0207 0208 void composite(const KoCompositeOp::ParameterInfo& params) const override 0209 { 0210 if(params.maskRowStart) { 0211 KoStreamedMath<_impl>::template genericComposite128<true, true, AlphaDarkenCompositor128<float, ParamsWrapper> >(params); 0212 } else { 0213 KoStreamedMath<_impl>::template genericComposite128<false, true, AlphaDarkenCompositor128<float, ParamsWrapper> >(params); 0214 } 0215 } 0216 }; 0217 0218 template<typename _impl> 0219 class KoOptimizedCompositeOpAlphaDarkenHard128 0220 : public KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperHard> 0221 { 0222 public: 0223 KoOptimizedCompositeOpAlphaDarkenHard128(const KoColorSpace* cs) 0224 : KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperHard>(cs) {} 0225 }; 0226 0227 template<typename _impl> 0228 class KoOptimizedCompositeOpAlphaDarkenCreamy128 0229 : public KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperCreamy> 0230 { 0231 public: 0232 KoOptimizedCompositeOpAlphaDarkenCreamy128(const KoColorSpace* cs) 0233 : KoOptimizedCompositeOpAlphaDarken128Impl<_impl, KoAlphaDarkenParamsWrapperCreamy>(cs) {} 0234 }; 0235 0236 template<typename _impl, typename ParamsWrapper> 0237 class KoOptimizedCompositeOpAlphaDarkenU64Impl : public KoCompositeOp 0238 { 0239 public: 0240 KoOptimizedCompositeOpAlphaDarkenU64Impl(const KoColorSpace* cs) 0241 : KoCompositeOp(cs, COMPOSITE_ALPHA_DARKEN, KoCompositeOp::categoryMix()) {} 0242 0243 using KoCompositeOp::composite; 0244 0245 void composite(const KoCompositeOp::ParameterInfo& params) const override 0246 { 0247 if(params.maskRowStart) { 0248 KoStreamedMath<_impl>::template genericComposite64<true, true, AlphaDarkenCompositor128<quint16, ParamsWrapper> >(params); 0249 } else { 0250 KoStreamedMath<_impl>::template genericComposite64<false, true, AlphaDarkenCompositor128<quint16, ParamsWrapper> >(params); 0251 } 0252 } 0253 }; 0254 0255 template<typename _impl> 0256 class KoOptimizedCompositeOpAlphaDarkenHardU64 0257 : public KoOptimizedCompositeOpAlphaDarkenU64Impl<_impl, KoAlphaDarkenParamsWrapperHard> 0258 { 0259 public: 0260 KoOptimizedCompositeOpAlphaDarkenHardU64(const KoColorSpace* cs) 0261 : KoOptimizedCompositeOpAlphaDarkenU64Impl<_impl, KoAlphaDarkenParamsWrapperHard>(cs) {} 0262 }; 0263 0264 template<typename _impl> 0265 class KoOptimizedCompositeOpAlphaDarkenCreamyU64 0266 : public KoOptimizedCompositeOpAlphaDarkenU64Impl<_impl, KoAlphaDarkenParamsWrapperCreamy> 0267 { 0268 public: 0269 KoOptimizedCompositeOpAlphaDarkenCreamyU64(const KoColorSpace* cs) 0270 : KoOptimizedCompositeOpAlphaDarkenU64Impl<_impl, KoAlphaDarkenParamsWrapperCreamy>(cs) {} 0271 }; 0272 0273 0274 #endif // KOOPTIMIZEDCOMPOSITEOPALPHADARKEN128_H