File indexing completed on 2024-05-12 15:59:29
0001 /* 0002 * SPDX-FileCopyrightText: 2020 Dmitry Kazakov <dimula73@gmail.com> 0003 * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #ifndef KOALPHAMASKAPPLICATOR_H 0009 #define KOALPHAMASKAPPLICATOR_H 0010 0011 #include "KoAlphaMaskApplicatorBase.h" 0012 #include "KoColorSpaceTraits.h" 0013 #include "KoMultiArchBuildSupport.h" 0014 0015 0016 template<typename _channels_type_, 0017 int _channels_nb_, 0018 int _alpha_pos_, 0019 typename _impl, 0020 typename EnableDummyType = void> 0021 struct KoAlphaMaskApplicator : public KoAlphaMaskApplicatorBase 0022 { 0023 void applyInverseNormedFloatMask(quint8 *pixels, 0024 const float *alpha, 0025 qint32 nPixels) const override { 0026 KoColorSpaceTrait< 0027 _channels_type_, 0028 _channels_nb_, 0029 _alpha_pos_>:: 0030 applyInverseAlphaNormedFloatMask(pixels, alpha, nPixels); 0031 } 0032 0033 void fillInverseAlphaNormedFloatMaskWithColor(quint8 * pixels, 0034 const float * alpha, 0035 const quint8 *brushColor, 0036 qint32 nPixels) const override { 0037 KoColorSpaceTrait< 0038 _channels_type_, 0039 _channels_nb_, 0040 _alpha_pos_>:: 0041 fillInverseAlphaNormedFloatMaskWithColor(pixels, alpha, brushColor, nPixels); 0042 } 0043 0044 void fillGrayBrushWithColor(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override { 0045 KoColorSpaceTrait< 0046 _channels_type_, 0047 _channels_nb_, 0048 _alpha_pos_>:: 0049 fillGrayBrushWithColor(dst, brush, brushColor, nPixels); 0050 } 0051 }; 0052 0053 #if defined(HAVE_XSIMD) && !defined(XSIMD_NO_SUPPORTED_ARCHITECTURE) 0054 0055 #include "KoStreamedMath.h" 0056 0057 template<typename _impl> 0058 struct KoAlphaMaskApplicator< 0059 quint8, 4, 3, _impl, 0060 typename std::enable_if<!std::is_same<_impl, xsimd::generic>::value>::type> : public KoAlphaMaskApplicatorBase 0061 { 0062 using uint_v = typename KoStreamedMath<_impl>::uint_v; 0063 using int_v = typename KoStreamedMath<_impl>::int_v; 0064 using float_v = typename KoStreamedMath<_impl>::float_v; 0065 0066 static constexpr int numChannels = 4; 0067 static constexpr int alphaPos = 3; 0068 0069 void applyInverseNormedFloatMask(quint8 *pixels, 0070 const float *alpha, 0071 qint32 nPixels) const override 0072 { 0073 const int block1 = nPixels / static_cast<int>(float_v::size); 0074 const int block2 = nPixels % static_cast<int>(float_v::size); 0075 const int vectorPixelStride = numChannels * static_cast<int>(float_v::size); 0076 0077 for (int i = 0; i < block1; i++) { 0078 const auto maskAlpha = float_v::load_unaligned(alpha); 0079 0080 auto data_i = uint_v::load_unaligned(reinterpret_cast<const quint32 *>(pixels)); 0081 0082 const auto pixelAlpha = xsimd::to_float(xsimd::bitwise_cast<int_v>(data_i >> 24U)) * (float_v(1.0f) - maskAlpha); 0083 0084 const quint32 colorChannelsMask = 0x00FFFFFF; 0085 0086 const uint_v pixelAlpha_i = xsimd::bitwise_cast<uint_v>(xsimd::nearbyint_as_int(pixelAlpha)); 0087 data_i = (data_i & colorChannelsMask) | (pixelAlpha_i << 24); 0088 data_i.store_unaligned(reinterpret_cast<typename uint_v::value_type *>(pixels)); 0089 0090 pixels += vectorPixelStride; 0091 alpha += float_v::size; 0092 } 0093 0094 KoColorSpaceTrait<quint8, 4, 3>:: 0095 applyInverseAlphaNormedFloatMask(pixels, alpha, block2); 0096 } 0097 0098 void fillInverseAlphaNormedFloatMaskWithColor(quint8 * pixels, 0099 const float * alpha, 0100 const quint8 *brushColor, 0101 qint32 nPixels) const override { 0102 const int block1 = nPixels / static_cast<int>(float_v::size); 0103 const int block2 = nPixels % static_cast<int>(float_v::size); 0104 const int vectorPixelStride = numChannels * static_cast<int>(float_v::size); 0105 const uint_v brushColor_i(*reinterpret_cast<const quint32*>(brushColor) & 0x00FFFFFFu); 0106 0107 for (int i = 0; i < block1; i++) { 0108 const auto maskAlpha = float_v::load_unaligned(alpha); 0109 const auto pixelAlpha = float_v(255.0f) * (float_v(1.0f) - maskAlpha); 0110 0111 const uint_v pixelAlpha_i = xsimd::bitwise_cast<uint_v>(xsimd::nearbyint_as_int(pixelAlpha)); 0112 const uint_v data_i = brushColor_i | (pixelAlpha_i << 24); 0113 data_i.store_unaligned(reinterpret_cast<typename uint_v::value_type *>(pixels)); 0114 0115 pixels += vectorPixelStride; 0116 alpha += float_v::size; 0117 } 0118 0119 KoColorSpaceTrait<quint8, 4, 3>:: 0120 fillInverseAlphaNormedFloatMaskWithColor(pixels, alpha, brushColor, block2); 0121 } 0122 0123 static inline uint_v multiply(uint_v a, uint_v b) 0124 { 0125 const uint_v c = a * b + 0x80u; 0126 return ((c >> 8) + c) >> 8; 0127 } 0128 0129 void fillGrayBrushWithColor(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override { 0130 const int block1 = nPixels / static_cast<int>(float_v::size); 0131 const int block2 = nPixels % static_cast<int>(float_v::size); 0132 const int vectorPixelStride = numChannels * static_cast<int>(float_v::size); 0133 const uint_v brushColor_i(*reinterpret_cast<const quint32*>(brushColor) & 0x00FFFFFFu); 0134 0135 const uint_v redChannelMask(0xFF); 0136 0137 for (int i = 0; i < block1; i++) { 0138 const auto maskPixels = uint_v::load_unaligned(reinterpret_cast<const quint32*>(brush)); 0139 0140 const uint_v pixelAlpha = maskPixels >> 24; 0141 const uint_v pixelRed = maskPixels & redChannelMask; 0142 const uint_v pixelAlpha_i = multiply(redChannelMask - pixelRed, pixelAlpha); 0143 0144 const uint_v data_i = brushColor_i | (pixelAlpha_i << 24); 0145 data_i.store_unaligned(reinterpret_cast<typename uint_v::value_type *>(dst)); 0146 0147 dst += vectorPixelStride; 0148 brush += float_v::size; 0149 } 0150 0151 KoColorSpaceTrait<quint8, 4, 3>:: 0152 fillGrayBrushWithColor(dst, brush, brushColor, block2); 0153 } 0154 }; 0155 0156 #endif /* HAVE_XSIMD */ 0157 0158 #endif // KOALPHAMASKAPPLICATOR_H