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_SCALAR_APPLICATOR_H 0010 #define KIS_BRUSH_SCALAR_APPLICATOR_H 0011 0012 #include "kis_brush_mask_applicator_base.h" 0013 #include "kis_global.h" 0014 #include "kis_random_source.h" 0015 0016 // 3x3 supersampling 0017 #define SUPERSAMPLING 3 0018 0019 template<class MaskGenerator, typename impl> 0020 struct KisBrushMaskScalarApplicator : public KisBrushMaskApplicatorBase { 0021 KisBrushMaskScalarApplicator(MaskGenerator *maskGenerator) 0022 : m_maskGenerator(maskGenerator) 0023 { 0024 } 0025 0026 void process(const QRect &rect) override 0027 { 0028 processScalar(rect); 0029 } 0030 0031 protected: 0032 void processScalar(const QRect &rect) 0033 { 0034 const MaskProcessingData *m_d = KisBrushMaskApplicatorBase::m_d; 0035 MaskGenerator *m_maskGenerator = KisBrushMaskScalarApplicator<MaskGenerator, impl>::m_maskGenerator; 0036 0037 qreal random = 1.0; 0038 quint8 *dabPointer = m_d->device->data() + rect.y() * rect.width() * m_d->pixelSize; 0039 quint8 alphaValue = OPACITY_TRANSPARENT_U8; 0040 // this offset is needed when brush size is smaller then fixed device size 0041 int offset = (m_d->device->bounds().width() - rect.width()) * m_d->pixelSize; 0042 int supersample = (m_maskGenerator->shouldSupersample() ? SUPERSAMPLING : 1); 0043 double invss = 1.0 / supersample; 0044 int samplearea = pow2(supersample); 0045 for (int y = rect.y(); y < rect.y() + rect.height(); y++) { 0046 for (int x = rect.x(); x < rect.x() + rect.width(); x++) { 0047 int value = 0; 0048 for (int sy = 0; sy < supersample; sy++) { 0049 for (int sx = 0; sx < supersample; sx++) { 0050 double x_ = x + sx * invss - m_d->centerX; 0051 double y_ = y + sy * invss - m_d->centerY; 0052 double maskX = m_d->cosa * x_ - m_d->sina * y_; 0053 double maskY = m_d->sina * x_ + m_d->cosa * y_; 0054 value += m_maskGenerator->valueAt(maskX, maskY); 0055 } 0056 } 0057 if (supersample != 1) 0058 value /= samplearea; 0059 0060 if (m_d->randomness != 0.0) { 0061 random = (1.0 - m_d->randomness) + m_d->randomness * m_randomSource.generateNormalized(); 0062 } 0063 0064 alphaValue = quint8((OPACITY_OPAQUE_U8 - value) * random); 0065 0066 // avoid computation of random numbers if density is full 0067 if (m_d->density != 1.0) { 0068 // compute density only for visible pixels of the mask 0069 if (alphaValue != OPACITY_TRANSPARENT_U8) { 0070 if (!(m_d->density >= m_randomSource.generateNormalized())) { 0071 alphaValue = OPACITY_TRANSPARENT_U8; 0072 } 0073 } 0074 } 0075 0076 if (m_d->color) { 0077 memcpy(dabPointer, m_d->color, static_cast<size_t>(m_d->pixelSize)); 0078 } 0079 0080 m_d->colorSpace->applyAlphaU8Mask(dabPointer, &alphaValue, 1); 0081 dabPointer += m_d->pixelSize; 0082 } // endfor x 0083 dabPointer += offset; 0084 } // endfor y 0085 } 0086 0087 protected: 0088 MaskGenerator *m_maskGenerator; 0089 KisRandomSource m_randomSource; // TODO: make it more deterministic for LoD 0090 }; 0091 0092 #endif /* KIS_BRUSH_SCALAR_APPLICATOR_H */