File indexing completed on 2024-05-12 15:58:11

0001 /*
0002  *  SPDX-FileCopyrightText: 2012 Sven Langkamp <sven.langkamp@gmail.com>
0003  *  SPDX-FileCopyrightText: 2012 Dmitry Kazakov <dimula73@gmail.com>
0004  *  SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me>
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #ifndef KIS_BRUSH_VECTOR_APPLICATOR_H
0010 #define KIS_BRUSH_VECTOR_APPLICATOR_H
0011 
0012 #include <xsimd_extensions/xsimd.hpp>
0013 
0014 #if defined(HAVE_XSIMD) && !defined(XSIMD_NO_SUPPORTED_ARCHITECTURE) && XSIMD_UNIVERSAL_BUILD_PASS
0015 
0016 #include "kis_brush_mask_scalar_applicator.h"
0017 
0018 template<class V>
0019 struct FastRowProcessor {
0020     FastRowProcessor(V *maskGenerator)
0021         : d(maskGenerator->d.data())
0022     {
0023     }
0024 
0025     template<typename _impl>
0026     void process(float *buffer, int width, float y, float cosa, float sina, float centerX, float centerY);
0027 
0028     typename V::Private *d;
0029 };
0030 
0031 template<class MaskGenerator, typename _impl>
0032 struct KisBrushMaskVectorApplicator : public KisBrushMaskScalarApplicator<MaskGenerator, _impl> {
0033     KisBrushMaskVectorApplicator(MaskGenerator *maskGenerator)
0034         : KisBrushMaskScalarApplicator<MaskGenerator, _impl>(maskGenerator)
0035     {
0036     }
0037 
0038     void process(const QRect &rect) override
0039     {
0040         startProcessing(rect, TypeHelper<MaskGenerator, _impl>());
0041     }
0042 
0043 protected:
0044     void processVector(const QRect &rect);
0045 
0046 private:
0047     template<class U, typename V>
0048     struct TypeHelper {
0049     };
0050 
0051 private:
0052     template<class U>
0053     inline void startProcessing(const QRect &rect, TypeHelper<U, xsimd::generic>)
0054     {
0055         KisBrushMaskScalarApplicator<MaskGenerator, _impl>::processScalar(rect);
0056     }
0057 
0058     template<class U, typename V>
0059     inline void startProcessing(const QRect &rect, TypeHelper<U, V>)
0060     {
0061         MaskGenerator *m_maskGenerator = KisBrushMaskScalarApplicator<MaskGenerator, _impl>::m_maskGenerator;
0062 
0063         if (m_maskGenerator->shouldVectorize()) {
0064             processVector(rect);
0065         } else {
0066             KisBrushMaskScalarApplicator<MaskGenerator, _impl>::processScalar(rect);
0067         }
0068     }
0069 };
0070 
0071 template<class MaskGenerator, typename impl>
0072 void KisBrushMaskVectorApplicator<MaskGenerator, impl>::processVector(const QRect &rect)
0073 {
0074     using float_v = xsimd::batch<float, impl>;
0075 
0076     const MaskProcessingData *m_d = KisBrushMaskApplicatorBase::m_d;
0077     MaskGenerator *m_maskGenerator = KisBrushMaskScalarApplicator<MaskGenerator, impl>::m_maskGenerator;
0078 
0079     qreal random = 1.0;
0080     quint8 *dabPointer = m_d->device->data() + rect.y() * rect.width() * m_d->pixelSize;
0081     quint8 alphaValue = OPACITY_TRANSPARENT_U8;
0082     // this offset is needed when brush size is smaller then fixed device size
0083     int offset = (m_d->device->bounds().width() - rect.width()) * m_d->pixelSize;
0084 
0085     int width = rect.width();
0086 
0087     // We need to calculate with a multiple of the width of the simd register
0088     size_t alignOffset = 0;
0089     if (width % float_v::size != 0) {
0090         alignOffset = float_v::size - (width % float_v::size);
0091     }
0092     size_t simdWidth = width + alignOffset;
0093 
0094     auto *buffer =xsimd::vector_aligned_malloc<float>(simdWidth);
0095 
0096     FastRowProcessor<MaskGenerator> processor(m_maskGenerator);
0097 
0098     for (int y = rect.y(); y < rect.y() + rect.height(); y++) {
0099         processor.template process<impl>(buffer, simdWidth, y, m_d->cosa, m_d->sina, m_d->centerX, m_d->centerY);
0100 
0101         if (m_d->randomness != 0.0 || m_d->density != 1.0) {
0102             for (int x = 0; x < width; x++) {
0103                 if (m_d->randomness != 0.0) {
0104                     random = (1.0 - m_d->randomness)
0105                         + m_d->randomness
0106                             * KisBrushMaskScalarApplicator<MaskGenerator, impl>::m_randomSource.generateNormalized();
0107                 }
0108 
0109                 alphaValue = quint8((OPACITY_OPAQUE_U8 - buffer[x] * 255) * random);
0110 
0111                 // avoid computation of random numbers if density is full
0112                 if (m_d->density != 1.0) {
0113                     // compute density only for visible pixels of the mask
0114                     if (alphaValue != OPACITY_TRANSPARENT_U8) {
0115                         if (!(m_d->density >= KisBrushMaskScalarApplicator<MaskGenerator, impl>::m_randomSource
0116                                                   .generateNormalized())) {
0117                             alphaValue = OPACITY_TRANSPARENT_U8;
0118                         }
0119                     }
0120                 }
0121 
0122                 if (m_d->color) {
0123                     memcpy(dabPointer, m_d->color, m_d->pixelSize);
0124                 }
0125 
0126                 m_d->colorSpace->applyAlphaU8Mask(dabPointer, &alphaValue, 1);
0127                 dabPointer += m_d->pixelSize;
0128             }
0129         } else if (m_d->color) {
0130             m_d->colorSpace->fillInverseAlphaNormedFloatMaskWithColor(dabPointer, buffer, m_d->color, width);
0131             dabPointer += width * m_d->pixelSize;
0132         } else {
0133             m_d->colorSpace->applyInverseNormedFloatMask(dabPointer, buffer, width);
0134             dabPointer += width * m_d->pixelSize;
0135         } // endfor x
0136         dabPointer += offset;
0137     } // endfor y
0138     xsimd::vector_aligned_free(buffer);
0139 }
0140 
0141 #endif /* defined HAVE_XSIMD */
0142 
0143 #endif /* KIS_BRUSH_VECTOR_APPLICATOR_H */