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