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