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

0001 /*
0002  *  SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
0003  *  SPDX-FileCopyrightText: 2011 Geoffry Song <goffrie@gmail.com>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include <cmath>
0009 
0010 #include <QDomDocument>
0011 #include <QVector>
0012 #include <QPointF>
0013 
0014 #include <KoColorSpaceConstants.h>
0015 
0016 #include "kis_fast_math.h"
0017 
0018 #include "kis_base_mask_generator.h"
0019 #include "kis_antialiasing_fade_maker.h"
0020 #include "kis_brush_mask_applicator_factories.h"
0021 #include "kis_brush_mask_applicator_base.h"
0022 #include "kis_gauss_circle_mask_generator.h"
0023 #include "kis_gauss_circle_mask_generator_p.h"
0024 
0025 #define M_SQRT_2 1.41421356237309504880
0026 
0027 #ifdef Q_OS_WIN
0028 // on windows we get our erf() from boost
0029 #include <boost/math/special_functions/erf.hpp>
0030 #define erf(x) boost::math::erf(x)
0031 #endif
0032 
0033 
0034 KisGaussCircleMaskGenerator::KisGaussCircleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges)
0035     : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, CIRCLE, GaussId),
0036       d(new Private(antialiasEdges))
0037 {
0038     d->ycoef = 1.0 / ratio;
0039     d->fade = 1.0 - (fh + fv) / 2.0;
0040 
0041     if (d->fade == 0.0) d->fade = 1e-6;
0042     else if (d->fade == 1.0) d->fade = 1.0 - 1e-6; // would become undefined for fade == 0 or 1
0043 
0044     d->center = (2.5 * (6761.0*d->fade-10000.0))/(M_SQRT_2*6761.0*d->fade);
0045     d->alphafactor = 255.0 / (2.0 * erf(d->center));
0046 
0047     d->applicator.reset(createOptimizedClass<MaskApplicatorFactory<KisGaussCircleMaskGenerator>>(this));
0048 
0049 }
0050 
0051 KisGaussCircleMaskGenerator::KisGaussCircleMaskGenerator(const KisGaussCircleMaskGenerator &rhs)
0052     : KisMaskGenerator(rhs),
0053       d(new Private(*rhs.d))
0054 {
0055     d->applicator.reset(createOptimizedClass<MaskApplicatorFactory<KisGaussCircleMaskGenerator>>(this));
0056 }
0057 
0058 KisMaskGenerator* KisGaussCircleMaskGenerator::clone() const
0059 {
0060     return new KisGaussCircleMaskGenerator(*this);
0061 }
0062 
0063 void KisGaussCircleMaskGenerator::setScale(qreal scaleX, qreal scaleY)
0064 {
0065     KisMaskGenerator::setScale(scaleX, scaleY);
0066     d->ycoef = scaleX / (scaleY * ratio());
0067 
0068     d->distfactor = M_SQRT_2 * 12500.0 / (6761.0 * d->fade * effectiveSrcWidth() / 2.0);
0069     d->fadeMaker.setRadius(0.5 * effectiveSrcWidth());
0070 }
0071 
0072 KisGaussCircleMaskGenerator::~KisGaussCircleMaskGenerator()
0073 {
0074 }
0075 
0076 inline quint8 KisGaussCircleMaskGenerator::Private::value(qreal dist) const
0077 {
0078     dist *= distfactor;
0079     quint8 ret = alphafactor * (erf(dist + center) - erf(dist - center));
0080     return (quint8) 255 - ret;
0081 }
0082 
0083 bool KisGaussCircleMaskGenerator::shouldVectorize() const
0084 {
0085     return !shouldSupersample() && spikes() == 2;
0086 }
0087 
0088 KisBrushMaskApplicatorBase* KisGaussCircleMaskGenerator::applicator()
0089 {
0090     return d->applicator.data();
0091 }
0092 
0093 quint8 KisGaussCircleMaskGenerator::valueAt(qreal x, qreal y) const
0094 {
0095     if (isEmpty()) return 255;
0096     qreal xr = x;
0097     qreal yr = qAbs(y);
0098     fixRotation(xr, yr);
0099 
0100     qreal dist = sqrt(norme(xr, yr * d->ycoef));
0101 
0102     quint8 value;
0103     if (d->fadeMaker.needFade(dist, &value)) {
0104         return value;
0105     }
0106 
0107     return d->value(dist);
0108 }
0109 
0110 void KisGaussCircleMaskGenerator::resetMaskApplicator(bool forceScalar)
0111 {
0112     d->applicator.reset(createOptimizedClass<MaskApplicatorFactory<KisGaussCircleMaskGenerator>>(this,forceScalar));
0113 }