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

0001 /*
0002  *  SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include <cmath>
0008 
0009 #include <QDomDocument>
0010 #include <QVector>
0011 #include <QPointF>
0012 
0013 #include <KoColorSpaceConstants.h>
0014 
0015 #include "kis_antialiasing_fade_maker.h"
0016 #include "kis_brush_mask_applicator_factories.h"
0017 
0018 #include "kis_curve_circle_mask_generator.h"
0019 #include "kis_curve_circle_mask_generator_p.h"
0020 #include "kis_cubic_curve.h"
0021 
0022 
0023 KisCurveCircleMaskGenerator::KisCurveCircleMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, const KisCubicCurve &curve, bool antialiasEdges)
0024     : KisMaskGenerator(diameter, ratio, fh, fv, spikes, antialiasEdges, CIRCLE, SoftId), d(new Private(antialiasEdges))
0025 {
0026     // here we set resolution for the maximum size of the brush!
0027     d->curveResolution = qRound(qMax(width(), height()) * OVERSAMPLING);
0028     d->curveData = curve.floatTransfer(d->curveResolution + 2);
0029     d->curvePoints = curve.points();
0030     setCurveString(curve.toString());
0031     d->dirty = false;
0032 
0033     setScale(1.0, 1.0);
0034 
0035     d->applicator.reset(createOptimizedClass<MaskApplicatorFactory<KisCurveCircleMaskGenerator> >(this));
0036 }
0037 
0038 KisCurveCircleMaskGenerator::KisCurveCircleMaskGenerator(const KisCurveCircleMaskGenerator &rhs)
0039     : KisMaskGenerator(rhs),
0040       d(new Private(*rhs.d))
0041 {
0042     d->applicator.reset(createOptimizedClass<MaskApplicatorFactory<KisCurveCircleMaskGenerator> >(this));
0043 }
0044 
0045 KisCurveCircleMaskGenerator::~KisCurveCircleMaskGenerator()
0046 {
0047 }
0048 
0049 KisMaskGenerator* KisCurveCircleMaskGenerator::clone() const
0050 {
0051     return new KisCurveCircleMaskGenerator(*this);
0052 }
0053 
0054 void KisCurveCircleMaskGenerator::setScale(qreal scaleX, qreal scaleY)
0055 {
0056     KisMaskGenerator::setScale(scaleX, scaleY);
0057 
0058     qreal width = effectiveSrcWidth();
0059     qreal height = effectiveSrcHeight();
0060 
0061     d->xcoef = 2.0 / width;
0062     d->ycoef = 2.0 / height;
0063 
0064     d->fadeMaker.setSquareNormCoeffs(d->xcoef, d->ycoef);
0065 }
0066 
0067 bool KisCurveCircleMaskGenerator::shouldVectorize() const
0068 {
0069     return !shouldSupersample() && spikes() == 2;
0070 }
0071 
0072 KisBrushMaskApplicatorBase* KisCurveCircleMaskGenerator::applicator()
0073 {
0074     return d->applicator.data();
0075 }
0076 
0077 inline quint8 KisCurveCircleMaskGenerator::Private::value(qreal dist) const
0078 {
0079     qreal distance = dist * curveResolution;
0080 
0081     quint16 alphaValue = distance;
0082     qreal alphaValueF = distance - alphaValue;
0083 
0084     qreal alpha = (
0085         (1.0 - alphaValueF) * curveData.at(alphaValue) +
0086         alphaValueF * curveData.at(alphaValue+1));
0087 
0088     return (1.0 - alpha) * 255;
0089 }
0090 
0091 quint8 KisCurveCircleMaskGenerator::valueAt(qreal x, qreal y) const
0092 {
0093     if (isEmpty()) return 255;
0094     qreal xr = x;
0095     qreal yr = qAbs(y);
0096     fixRotation(xr, yr);
0097 
0098     qreal dist = norme(xr * d->xcoef, yr * d->ycoef);
0099 
0100     quint8 value;
0101     if (d->fadeMaker.needFade(dist, &value)) {
0102         return value;
0103     }
0104 
0105     return d->value(dist);
0106 }
0107 
0108 void KisCurveCircleMaskGenerator::toXML(QDomDocument& doc, QDomElement& e) const
0109 {
0110     KisMaskGenerator::toXML(doc, e);
0111     e.setAttribute("softness_curve", curveString());
0112 }
0113 
0114 void KisCurveCircleMaskGenerator::setSoftness(qreal softness)
0115 {
0116     // performance
0117     if (!d->dirty && softness == 1.0) return;
0118 
0119     d->dirty = true;
0120     KisMaskGenerator::setSoftness(softness);
0121     KisCurveCircleMaskGenerator::transformCurveForSoftness(softness,d->curvePoints, d->curveResolution+2, d->curveData);
0122     d->dirty = false;
0123 }
0124 
0125 void KisCurveCircleMaskGenerator::transformCurveForSoftness(qreal softness,const QList<QPointF> &points, int curveResolution, QVector< qreal >& result)
0126 {
0127     QList<QPointF> newList = points;
0128     newList.detach();
0129 
0130     int size = newList.size();
0131     if (size == 2){
0132         // make place for new point in the centre
0133         newList.append(newList.at(1));
0134         newList[1] = (newList.at(0) + newList.at(2)) * 0.5;
0135         // transoform it
0136         newList[1].setY(qBound<qreal>(0.0,newList.at(1).y() * softness,1.0));
0137     }else{
0138         // transform all points except first and last
0139         for (int i = 1; i < size-1; i++){
0140             newList[i].setY(qBound<qreal>(0.0,newList.at(i).y() * softness,1.0));
0141         }
0142     }
0143 
0144     // compute the data
0145     KisCubicCurve curve(newList);
0146     result = curve.floatTransfer( curveResolution );
0147 }
0148 
0149 void KisCurveCircleMaskGenerator::resetMaskApplicator(bool forceScalar)
0150 {
0151     d->applicator.reset(createOptimizedClass<MaskApplicatorFactory<KisCurveCircleMaskGenerator> >(this,forceScalar));
0152 }