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 */