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