File indexing completed on 2024-06-09 04:23:31
0001 /* 0002 * SPDX-FileCopyrightText: 2015 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 KOOPTIMIZEDCOMPOSITEOPOVER128_H_ 0009 #define KOOPTIMIZEDCOMPOSITEOPOVER128_H_ 0010 0011 #include "KoCompositeOpBase.h" 0012 #include "KoCompositeOpRegistry.h" 0013 #include "KoStreamedMath.h" 0014 0015 #define NATIVE_OPACITY_OPAQUE KoColorSpaceMathsTraits<channels_type>::unitValue 0016 #define NATIVE_OPACITY_TRANSPARENT KoColorSpaceMathsTraits<channels_type>::zeroValue 0017 0018 #define INFO_DEBUG 0 0019 0020 template<typename channels_type, bool alphaLocked, bool allChannelsFlag> 0021 struct OverCompositor128 { 0022 struct ParamsWrapper { 0023 ParamsWrapper(const KoCompositeOp::ParameterInfo& params) 0024 : channelFlags(params.channelFlags) 0025 { 0026 } 0027 const QBitArray &channelFlags; 0028 }; 0029 0030 struct Pixel { 0031 channels_type red; 0032 channels_type green; 0033 channels_type blue; 0034 channels_type alpha; 0035 }; 0036 0037 // \see docs in AlphaDarkenCompositor32 0038 template<bool haveMask, bool src_aligned, typename _impl> 0039 static ALWAYS_INLINE void compositeVector(const quint8 *src, quint8 *dst, const quint8 *mask, float opacity, const ParamsWrapper &oparams) 0040 { 0041 #if INFO_DEBUG 0042 static quint32 countTotal = 0; 0043 static quint32 countOne = 0; 0044 static quint32 countTwo = 0; 0045 static quint32 countThree = 0; 0046 static quint32 countFour = 0; 0047 0048 if (++countTotal % 250000 == 0) { 0049 qInfo() << "count" << countOne << countTwo << countThree << countFour << countTotal << opacity; 0050 } 0051 #endif 0052 using float_v = typename KoStreamedMath<_impl>::float_v; 0053 using float_m = typename float_v::batch_bool_type; 0054 0055 Q_UNUSED(oparams); 0056 0057 float_v src_alpha; 0058 float_v dst_alpha; 0059 0060 float_v src_c1; 0061 float_v src_c2; 0062 float_v src_c3; 0063 0064 PixelWrapper<channels_type, _impl> dataWrapper; 0065 dataWrapper.read(src, src_c1, src_c2, src_c3, src_alpha); 0066 0067 //bool haveOpacity = opacity != 1.0; 0068 const float_v opacity_norm_vec(opacity); 0069 src_alpha *= opacity_norm_vec; 0070 0071 if (haveMask) { 0072 const float_v uint8MaxRec1((float)1.0 / 255); 0073 float_v mask_vec = KoStreamedMath<_impl>::fetch_mask_8(mask); 0074 src_alpha *= mask_vec * uint8MaxRec1; 0075 } 0076 0077 const float_v zeroValue(static_cast<float>(NATIVE_OPACITY_TRANSPARENT)); 0078 // The source cannot change the colors in the destination, 0079 // since its fully transparent 0080 if (xsimd::all(src_alpha == zeroValue)) { 0081 #if INFO_DEBUG 0082 countFour++; 0083 #endif 0084 return; 0085 } 0086 0087 float_v dst_c1; 0088 float_v dst_c2; 0089 float_v dst_c3; 0090 0091 dataWrapper.read(dst, dst_c1, dst_c2, dst_c3, dst_alpha); 0092 0093 float_v src_blend; 0094 float_v new_alpha; 0095 0096 const float_v oneValue(1.0f); 0097 if (xsimd::all(dst_alpha == oneValue)) { 0098 new_alpha = dst_alpha; 0099 src_blend = src_alpha; 0100 } else if (xsimd::all(dst_alpha == zeroValue)) { 0101 new_alpha = src_alpha; 0102 src_blend = oneValue; 0103 } else { 0104 /** 0105 * The value of new_alpha can have *some* zero values, 0106 * which will result in NaN values while division. 0107 */ 0108 new_alpha = dst_alpha + (oneValue - dst_alpha) * src_alpha; 0109 const float_m mask = (new_alpha == zeroValue); 0110 src_blend = src_alpha / new_alpha; 0111 src_blend = xsimd::set_zero(src_blend, mask); 0112 } 0113 0114 if (!xsimd::all(src_blend == oneValue)) { 0115 #if INFO_DEBUG 0116 ++countOne; 0117 #endif 0118 0119 dst_c1 = src_blend * (src_c1 - dst_c1) + dst_c1; 0120 dst_c2 = src_blend * (src_c2 - dst_c2) + dst_c2; 0121 dst_c3 = src_blend * (src_c3 - dst_c3) + dst_c3; 0122 0123 dataWrapper.write(dst, dst_c1, dst_c2, dst_c3, new_alpha); 0124 } else { 0125 #if INFO_DEBUG 0126 ++countTwo; 0127 #endif 0128 dataWrapper.write(dst, src_c1, src_c2, src_c3, new_alpha); 0129 } 0130 } 0131 0132 template<bool haveMask, typename _impl> 0133 static ALWAYS_INLINE void compositeOnePixelScalar(const quint8 *src, 0134 quint8 *dst, 0135 const quint8 *mask, 0136 float opacity, 0137 const ParamsWrapper &oparams) 0138 { 0139 using namespace Arithmetic; 0140 const qint32 alpha_pos = 3; 0141 0142 const auto *s = reinterpret_cast<const channels_type*>(src); 0143 auto *d = reinterpret_cast<channels_type*>(dst); 0144 0145 float srcAlpha = s[alpha_pos]; 0146 PixelWrapper<channels_type, _impl>::normalizeAlpha(srcAlpha); 0147 srcAlpha *= opacity; 0148 0149 if (haveMask) { 0150 const float uint8Rec1 = 1.0f / 255.0f; 0151 srcAlpha *= float(*mask) * uint8Rec1; 0152 } 0153 0154 #if INFO_DEBUG 0155 static int xx = 0; 0156 bool display = xx > 45 && xx < 50; 0157 if (display) { 0158 qInfo() << "O" << s[alpha_pos] << srcAlpha << haveMask << opacity; 0159 } 0160 #endif 0161 0162 if (srcAlpha != 0.0f) { 0163 0164 float dstAlpha = d[alpha_pos]; 0165 PixelWrapper<channels_type, _impl>::normalizeAlpha(dstAlpha); 0166 float srcBlendNorm = 0.0f; 0167 0168 if (alphaLocked || dstAlpha == 1.0f) { 0169 srcBlendNorm = srcAlpha; 0170 } else if (dstAlpha == 0.0f) { 0171 dstAlpha = srcAlpha; 0172 srcBlendNorm = 1.0f; 0173 0174 if (!allChannelsFlag) { 0175 KoStreamedMathFunctions::clearPixel<sizeof(Pixel)>(dst); 0176 } 0177 } else { 0178 dstAlpha += (1.0f - dstAlpha) * srcAlpha; 0179 srcBlendNorm = srcAlpha / dstAlpha; 0180 } 0181 0182 #if INFO_DEBUG 0183 if (display) { 0184 qInfo() << "params" << srcBlendNorm << allChannelsFlag << alphaLocked << dstAlpha << haveMask; 0185 } 0186 #endif 0187 if(allChannelsFlag) { 0188 if (srcBlendNorm == 1.0f) { 0189 if (!alphaLocked) { 0190 KoStreamedMathFunctions::copyPixel<sizeof(Pixel)>(src, dst); 0191 } else { 0192 d[0] = s[0]; 0193 d[1] = s[1]; 0194 d[2] = s[2]; 0195 } 0196 } else if (srcBlendNorm != 0.0f){ 0197 #if INFO_DEBUG 0198 if (display) { 0199 qInfo() << "calc" << s[0] << d[0] << srcBlendNorm * (s[0] - d[0]) + d[0] << s[0] - d[0] << srcBlendNorm * (s[0] - d[0]) << srcBlendNorm; 0200 } 0201 #endif 0202 0203 d[0] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(d[0], s[0], srcBlendNorm); 0204 d[1] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(d[1], s[1], srcBlendNorm); 0205 d[2] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(d[2], s[2], srcBlendNorm); 0206 } 0207 } else { 0208 const QBitArray &channelFlags = oparams.channelFlags; 0209 0210 if (srcBlendNorm == 1.0f) { 0211 if(channelFlags.at(0)) d[0] = s[0]; 0212 if(channelFlags.at(1)) d[1] = s[1]; 0213 if(channelFlags.at(2)) d[2] = s[2]; 0214 } else if (srcBlendNorm != 0.0f) { 0215 if(channelFlags.at(0)) d[0] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(d[0], s[0], srcBlendNorm); 0216 if(channelFlags.at(1)) d[1] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(d[1], s[1], srcBlendNorm);; 0217 if(channelFlags.at(2)) d[2] = PixelWrapper<channels_type, _impl>::lerpMixedUintFloat(d[2], s[2], srcBlendNorm);; 0218 } 0219 } 0220 0221 if (!alphaLocked) { 0222 PixelWrapper<channels_type, _impl>::denormalizeAlpha(dstAlpha); 0223 d[alpha_pos] = PixelWrapper<channels_type, _impl>::roundFloatToUint(dstAlpha); 0224 } 0225 #if INFO_DEBUG 0226 if (display) { 0227 qInfo() << "result" << d[0] << d[1] << d[2] << d[3]; 0228 } 0229 ++xx; 0230 #endif 0231 } 0232 } 0233 }; 0234 0235 /** 0236 * An optimized version of a composite op for the use in 16 byte 0237 * colorspaces with alpha channel placed at the last byte of 0238 * the pixel: C1_C2_C3_A. 0239 */ 0240 template<typename _impl> 0241 class KoOptimizedCompositeOpOver128 : public KoCompositeOp 0242 { 0243 public: 0244 KoOptimizedCompositeOpOver128(const KoColorSpace* cs) 0245 : KoCompositeOp(cs, COMPOSITE_OVER, KoCompositeOp::categoryMix()) {} 0246 0247 using KoCompositeOp::composite; 0248 0249 void composite(const KoCompositeOp::ParameterInfo& params) const override 0250 { 0251 if(params.maskRowStart) { 0252 composite<true>(params); 0253 } else { 0254 composite<false>(params); 0255 } 0256 } 0257 0258 template <bool haveMask> 0259 inline void composite(const KoCompositeOp::ParameterInfo& params) const { 0260 if (params.channelFlags.isEmpty() || 0261 params.channelFlags == QBitArray(4, true)) { 0262 0263 KoStreamedMath<_impl>::template genericComposite128<haveMask, false, OverCompositor128<float, false, true> >(params); 0264 } else { 0265 const bool allChannelsFlag = 0266 params.channelFlags.at(0) && 0267 params.channelFlags.at(1) && 0268 params.channelFlags.at(2); 0269 0270 const bool alphaLocked = 0271 !params.channelFlags.at(3); 0272 0273 if (allChannelsFlag && alphaLocked) { 0274 KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, OverCompositor128<float, true, true> >(params); 0275 } else if (!allChannelsFlag && !alphaLocked) { 0276 KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, OverCompositor128<float, false, false> >(params); 0277 } else /*if (!allChannelsFlag && alphaLocked) */{ 0278 KoStreamedMath<_impl>::template genericComposite128_novector<haveMask, false, OverCompositor128<float, true, false> >(params); 0279 } 0280 } 0281 } 0282 }; 0283 0284 template<typename _impl> 0285 class KoOptimizedCompositeOpOverU64 : public KoCompositeOp 0286 { 0287 public: 0288 KoOptimizedCompositeOpOverU64(const KoColorSpace* cs) 0289 : KoCompositeOp(cs, COMPOSITE_OVER, KoCompositeOp::categoryMix()) {} 0290 0291 using KoCompositeOp::composite; 0292 0293 void composite(const KoCompositeOp::ParameterInfo& params) const override 0294 { 0295 if(params.maskRowStart) { 0296 composite<true>(params); 0297 } else { 0298 composite<false>(params); 0299 } 0300 } 0301 0302 template <bool haveMask> 0303 inline void composite(const KoCompositeOp::ParameterInfo& params) const { 0304 if (params.channelFlags.isEmpty() || 0305 params.channelFlags == QBitArray(4, true)) { 0306 0307 KoStreamedMath<_impl>::template genericComposite64<haveMask, false, OverCompositor128<quint16, false, true> >(params); 0308 } else { 0309 const bool allChannelsFlag = 0310 params.channelFlags.at(0) && 0311 params.channelFlags.at(1) && 0312 params.channelFlags.at(2); 0313 0314 const bool alphaLocked = 0315 !params.channelFlags.at(3); 0316 0317 if (allChannelsFlag && alphaLocked) { 0318 KoStreamedMath<_impl>::template genericComposite64_novector<haveMask, false, OverCompositor128<quint16, true, true> >(params); 0319 } else if (!allChannelsFlag && !alphaLocked) { 0320 KoStreamedMath<_impl>::template genericComposite64_novector<haveMask, false, OverCompositor128<quint16, false, false> >(params); 0321 } else /*if (!allChannelsFlag && alphaLocked) */{ 0322 KoStreamedMath<_impl>::template genericComposite64_novector<haveMask, false, OverCompositor128<quint16, true, false> >(params); 0323 } 0324 } 0325 } 0326 }; 0327 0328 #endif // KOOPTIMIZEDCOMPOSITEOPOVER128_H_