File indexing completed on 2024-05-19 04:26:07
0001 /* 0002 * SPDX-FileCopyrightText: 2004, 2007-2009 Cyrille Berger <cberger@cberger.net> 0003 * SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com> 0004 * SPDX-FileCopyrightText: 2011 Sven Langkamp <sven.langkamp@gmail.com> 0005 * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me> 0006 * 0007 * SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include <KoMultiArchBuildSupport.h> 0011 0012 #include "kis_brush_mask_applicator_factories.h" 0013 #include "kis_mask_generator.h" 0014 #include "kis_brush_mask_applicator_base.h" 0015 0016 #include <cmath> 0017 #include "kis_fast_math.h" 0018 0019 #include <QDomDocument> 0020 0021 #include "kis_circle_mask_generator.h" 0022 #include "kis_rect_mask_generator.h" 0023 #include "kis_gauss_circle_mask_generator.h" 0024 #include "kis_gauss_rect_mask_generator.h" 0025 #include "kis_cubic_curve.h" 0026 #include "kis_curve_circle_mask_generator.h" 0027 #include "kis_curve_rect_mask_generator.h" 0028 #include <kis_dom_utils.h> 0029 0030 struct KisMaskGenerator::Private { 0031 Private() 0032 : diameter(1.0), 0033 ratio(1.0), 0034 softness(1.0), 0035 fh(1.0), 0036 fv(1.0), 0037 cs(0.0), 0038 ss(0.0), 0039 cachedSpikesAngle(0.0), 0040 spikes(2), 0041 empty(true), 0042 antialiasEdges(false), 0043 type(CIRCLE), 0044 scaleX(1.0), 0045 scaleY(1.0) 0046 { 0047 } 0048 0049 Private(const Private &rhs) 0050 : diameter(rhs.diameter), 0051 ratio(rhs.ratio), 0052 softness(rhs.softness), 0053 fh(rhs.fh), 0054 fv(rhs.fv), 0055 cs(rhs.cs), 0056 ss(rhs.ss), 0057 cachedSpikesAngle(rhs.cachedSpikesAngle), 0058 spikes(rhs.spikes), 0059 empty(rhs.empty), 0060 antialiasEdges(rhs.antialiasEdges), 0061 type(rhs.type), 0062 curveString(rhs.curveString), 0063 scaleX(rhs.scaleX), 0064 scaleY(rhs.scaleY) 0065 { 0066 } 0067 0068 qreal diameter, ratio; 0069 qreal softness; 0070 qreal fh, fv; 0071 qreal cs, ss; 0072 qreal cachedSpikesAngle; 0073 int spikes; 0074 bool empty; 0075 bool antialiasEdges; 0076 Type type; 0077 QString curveString; 0078 qreal scaleX; 0079 qreal scaleY; 0080 }; 0081 0082 0083 KisMaskGenerator::KisMaskGenerator(qreal diameter, qreal ratio, qreal fh, qreal fv, int spikes, bool antialiasEdges, Type type, const KoID& id) 0084 : d(new Private), m_id(id) 0085 { 0086 d->diameter = diameter; 0087 d->ratio = ratio; 0088 d->fh = 0.5 * fh; 0089 d->fv = 0.5 * fv; 0090 d->softness = 1.0; // by default don't change fade/softness/hardness 0091 d->spikes = spikes; 0092 d->cachedSpikesAngle = M_PI / d->spikes; 0093 d->type = type; 0094 d->antialiasEdges = antialiasEdges; 0095 d->scaleX = 1.0; 0096 d->scaleY = 1.0; 0097 init(); 0098 } 0099 0100 KisMaskGenerator::~KisMaskGenerator() 0101 { 0102 } 0103 0104 KisMaskGenerator::KisMaskGenerator(const KisMaskGenerator &rhs) 0105 : d(new Private(*rhs.d)), 0106 m_id(rhs.m_id) 0107 { 0108 } 0109 0110 void KisMaskGenerator::init() 0111 { 0112 d->cs = cos(- 2 * M_PI / d->spikes); 0113 d->ss = sin(- 2 * M_PI / d->spikes); 0114 d->empty = (d->ratio == 0.0 || d->diameter == 0.0); 0115 } 0116 0117 bool KisMaskGenerator::shouldSupersample() const 0118 { 0119 return antialiasEdges() && (effectiveSrcWidth() < 10 || effectiveSrcHeight() < 10); 0120 } 0121 0122 bool KisMaskGenerator::shouldSupersample6x6() const 0123 { 0124 return effectiveSrcWidth() < 1 || effectiveSrcHeight() < 1; 0125 } 0126 0127 bool KisMaskGenerator::shouldVectorize() const 0128 { 0129 return false; 0130 } 0131 0132 0133 bool KisMaskGenerator::isEmpty() const 0134 { 0135 return d->empty; 0136 } 0137 0138 void KisMaskGenerator::toXML(QDomDocument& doc, QDomElement& e) const 0139 { 0140 Q_UNUSED(doc); 0141 //e.setAttribute("radius", d->radius); 0142 e.setAttribute("diameter", QString::number(d->diameter)); 0143 e.setAttribute("ratio", QString::number(d->ratio)); 0144 e.setAttribute("hfade", QString::number(horizontalFade())); 0145 e.setAttribute("vfade", QString::number(verticalFade())); 0146 e.setAttribute("spikes", d->spikes); 0147 e.setAttribute("type", d->type == CIRCLE ? "circle" : "rect"); 0148 e.setAttribute("antialiasEdges", d->antialiasEdges); 0149 e.setAttribute("id", id()); 0150 } 0151 0152 KisMaskGenerator* KisMaskGenerator::fromXML(const QDomElement& elt) 0153 { 0154 double diameter = 1.0; 0155 // backward compatibility -- it was mistakenly named radius for 2.2 0156 if (elt.hasAttribute("radius")){ 0157 diameter = KisDomUtils::toDouble(elt.attribute("radius", "1.0")); 0158 } 0159 else /*if (elt.hasAttribute("diameter"))*/{ 0160 diameter = KisDomUtils::toDouble(elt.attribute("diameter", "1.0")); 0161 } 0162 double ratio = KisDomUtils::toDouble(elt.attribute("ratio", "1.0")); 0163 double hfade = KisDomUtils::toDouble(elt.attribute("hfade", "0.0")); 0164 double vfade = KisDomUtils::toDouble(elt.attribute("vfade", "0.0")); 0165 0166 int spikes = elt.attribute("spikes", "2").toInt(); 0167 QString typeShape = elt.attribute("type", "circle"); 0168 QString id = elt.attribute("id", DefaultId.id()); 0169 bool antialiasEdges = elt.attribute("antialiasEdges", "0").toInt(); 0170 0171 if (id == DefaultId.id()) { 0172 if (typeShape == "circle") { 0173 return new KisCircleMaskGenerator(diameter, ratio, hfade, vfade, spikes, antialiasEdges); 0174 } else { 0175 return new KisRectangleMaskGenerator(diameter, ratio, hfade, vfade, spikes, antialiasEdges); 0176 } 0177 } 0178 0179 if (id == SoftId.id()) { 0180 const KisCubicCurve curve(elt.attribute("softness_curve","0,0;1,1")); 0181 0182 if (typeShape == "circle") { 0183 return new KisCurveCircleMaskGenerator(diameter, ratio, hfade, vfade, spikes, curve, antialiasEdges); 0184 } else { 0185 return new KisCurveRectangleMaskGenerator(diameter, ratio, hfade, vfade, spikes, curve, antialiasEdges); 0186 } 0187 } 0188 0189 if (id == GaussId.id()) { 0190 if (typeShape == "circle") { 0191 return new KisGaussCircleMaskGenerator(diameter, ratio, hfade, vfade, spikes, antialiasEdges); 0192 } else { 0193 return new KisGaussRectangleMaskGenerator(diameter, ratio, hfade, vfade, spikes, antialiasEdges); 0194 } 0195 } 0196 0197 // if unknown 0198 return new KisCircleMaskGenerator(diameter, ratio, hfade, vfade, spikes, true); 0199 } 0200 0201 qreal KisMaskGenerator::width() const 0202 { 0203 return d->diameter; 0204 } 0205 0206 qreal KisMaskGenerator::height() const 0207 { 0208 if (d->spikes == 2) { 0209 return d->diameter * d->ratio; 0210 } 0211 return d->diameter; 0212 } 0213 0214 qreal KisMaskGenerator::effectiveSrcWidth() const 0215 { 0216 return d->diameter * d->scaleX; 0217 } 0218 0219 qreal KisMaskGenerator::effectiveSrcHeight() const 0220 { 0221 /** 0222 * This height is related to the source of the brush mask, so we 0223 * don't take spikes into account, they will be generated from 0224 * this data. 0225 */ 0226 return d->diameter * d->ratio * d->scaleY; 0227 } 0228 0229 qreal KisMaskGenerator::diameter() const 0230 { 0231 return d->diameter; 0232 } 0233 0234 void KisMaskGenerator::setDiameter(qreal value) 0235 { 0236 d->diameter = value; 0237 init(); 0238 setScale(d->scaleX, d->scaleY); 0239 } 0240 0241 qreal KisMaskGenerator::ratio() const 0242 { 0243 return d->ratio; 0244 } 0245 0246 qreal KisMaskGenerator::softness() const 0247 { 0248 return d->softness; 0249 } 0250 0251 0252 void KisMaskGenerator::setSoftness(qreal softness) 0253 { 0254 d->softness = softness; 0255 } 0256 0257 0258 qreal KisMaskGenerator::horizontalFade() const 0259 { 0260 return 2.0 * d->fh; // 'cause in init we divide it again 0261 } 0262 0263 qreal KisMaskGenerator::verticalFade() const 0264 { 0265 return 2.0 * d->fv; // 'cause in init we divide it again 0266 } 0267 0268 int KisMaskGenerator::spikes() const 0269 { 0270 return d->spikes; 0271 } 0272 0273 KisMaskGenerator::Type KisMaskGenerator::type() const 0274 { 0275 return d->type; 0276 } 0277 0278 QList< KoID > KisMaskGenerator::maskGeneratorIds() 0279 { 0280 QList<KoID> ids; 0281 ids << DefaultId << SoftId << GaussId; 0282 return ids; 0283 } 0284 0285 QString KisMaskGenerator::curveString() const 0286 { 0287 return d->curveString; 0288 } 0289 0290 void KisMaskGenerator::setCurveString(const QString& curveString) 0291 { 0292 d->curveString = curveString; 0293 } 0294 0295 bool KisMaskGenerator::antialiasEdges() const 0296 { 0297 return d->antialiasEdges; 0298 } 0299 0300 void KisMaskGenerator::setScale(qreal scaleX, qreal scaleY) 0301 { 0302 d->scaleX = scaleX; 0303 d->scaleY = scaleY; 0304 } 0305 0306 void KisMaskGenerator::fixRotation(qreal &xr, qreal &yr) const 0307 { 0308 if (d->spikes > 2) { 0309 double angle = (KisFastMath::atan2(yr, xr)); 0310 0311 while (angle > d->cachedSpikesAngle){ 0312 double sx = xr; 0313 double sy = yr; 0314 0315 xr = d->cs * sx - d->ss * sy; 0316 yr = d->ss * sx + d->cs * sy; 0317 0318 angle -= 2 * d->cachedSpikesAngle; 0319 } 0320 } 0321 }