File indexing completed on 2024-05-26 04:34:00

0001 /*
0002  *  SPDX-FileCopyrightText: 2010 Sven Langkamp <sven.langkamp@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_brush_based_paintop_settings.h"
0008 
0009 #include <KisPaintingModeOptionData.h>
0010 #include "kis_brush_based_paintop_options_widget.h"
0011 #include <kis_boundary.h>
0012 #include "KisBrushServerProvider.h"
0013 #include <QLineF>
0014 #include "kis_signals_blocker.h"
0015 #include "kis_brush_option.h"
0016 #include <KisPaintopSettingsIds.h>
0017 #include <kis_paintop_preset.h>
0018 #include "KoCanvasResourcesIds.h"
0019 #include "kis_texture_option.h"
0020 #include <KoResourceCacheInterface.h>
0021 #include <KisOptimizedBrushOutline.h>
0022 
0023 struct BrushReader {
0024     BrushReader(const KisBrushBasedPaintOpSettings *parent)
0025         : m_parent(parent)
0026     {
0027         m_option.readOptionSetting(m_parent, parent->resourcesInterface(), parent->canvasResourcesInterface());
0028     }
0029 
0030     KisBrushSP brush() {
0031         return m_option.brush();
0032     }
0033 
0034     const KisBrushBasedPaintOpSettings *m_parent;
0035     KisBrushOptionProperties m_option;
0036 };
0037 
0038 struct BrushWriter {
0039     BrushWriter(KisBrushBasedPaintOpSettings *parent)
0040         : m_parent(parent)
0041     {
0042         m_option.readOptionSetting(m_parent, parent->resourcesInterface(), parent->canvasResourcesInterface());
0043     }
0044 
0045     ~BrushWriter() {
0046         m_option.writeOptionSetting(m_parent);
0047     }
0048 
0049     KisBrushSP brush() {
0050         return m_option.brush();
0051     }
0052 
0053     KisBrushBasedPaintOpSettings *m_parent;
0054     KisBrushOptionProperties m_option;
0055 };
0056 
0057 
0058 KisBrushBasedPaintOpSettings::KisBrushBasedPaintOpSettings(KisResourcesInterfaceSP resourcesInterface)
0059     : KisOutlineGenerationPolicy<KisPaintOpSettings>(KisCurrentOutlineFetcher::SIZE_OPTION |
0060                                                      KisCurrentOutlineFetcher::ROTATION_OPTION |
0061                                                      KisCurrentOutlineFetcher::MIRROR_OPTION |
0062                                                      KisCurrentOutlineFetcher::SHARPNESS_OPTION,
0063                                                      resourcesInterface)
0064 
0065 {
0066 }
0067 
0068 bool KisBrushBasedPaintOpSettings::paintIncremental()
0069 {
0070     KisPaintingModeOptionData data;
0071     data.read(this);
0072     return !data.hasPaintingModeProperty || data.paintingMode == enumPaintingMode::BUILDUP;
0073 }
0074 
0075 KisPaintOpSettingsSP KisBrushBasedPaintOpSettings::clone() const
0076 {
0077     KisPaintOpSettingsSP _settings = KisOutlineGenerationPolicy<KisPaintOpSettings>::clone();
0078     KisBrushBasedPaintOpSettingsSP settings = dynamic_cast<KisBrushBasedPaintOpSettings*>(_settings.data());
0079 
0080     /**
0081      * We don't copy m_savedBrush directly because it can also
0082      * depend on "canvas resources", which should be baked into
0083      * the preset on cloning. This dependency is tracked by
0084      * KisPresetShadowUpdater and reset when needed via
0085      * resetting cache interface.
0086      */
0087     settings->setResourceCacheInterface(resourceCacheInterface());
0088 
0089     return settings;
0090 }
0091 
0092 KisBrushSP KisBrushBasedPaintOpSettings::brush() const
0093 {
0094     KisBrushSP brush = m_savedBrush;
0095 
0096     if (!brush) {
0097         BrushReader w(this);
0098         brush = w.brush();
0099         m_savedBrush = brush;
0100     }
0101 
0102     return brush;
0103 }
0104 
0105 KisOptimizedBrushOutline KisBrushBasedPaintOpSettings::brushOutlineImpl(const KisPaintInformation &info,
0106                                                             const OutlineMode &mode,
0107                                                             qreal alignForZoom,
0108                                                             qreal additionalScale)
0109 {
0110     KisOptimizedBrushOutline path;
0111 
0112     if (mode.isVisible) {
0113         KisBrushSP brush = this->brush();
0114         if (!brush) return path;
0115         qreal finalScale = brush->scale() * additionalScale;
0116 
0117         KisOptimizedBrushOutline realOutline = brush->outline(alignForZoom > 2.0 || qFuzzyCompare(alignForZoom, 2.0));
0118 
0119         if (mode.forceCircle) {
0120 
0121             QPainterPath ellipse;
0122             ellipse.addEllipse(realOutline.boundingRect());
0123             realOutline = ellipse;
0124         }
0125 
0126         path = outlineFetcher()->fetchOutline(info, this, realOutline, mode, alignForZoom, finalScale, brush->angle());
0127 
0128         if (mode.showTiltDecoration) {
0129             const QPainterPath tiltLine = makeTiltIndicator(info,
0130                 realOutline.boundingRect().center(),
0131                 realOutline.boundingRect().width() * 0.5,
0132                 3.0);
0133             path.addPath(outlineFetcher()->fetchOutline(info, this, tiltLine, mode, alignForZoom, finalScale, 0.0, true, realOutline.boundingRect().center().x(), realOutline.boundingRect().center().y()));
0134         }
0135     }
0136 
0137     return path;
0138 }
0139 
0140 KisOptimizedBrushOutline KisBrushBasedPaintOpSettings::brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom)
0141 {
0142     return brushOutlineImpl(info, mode, alignForZoom, 1.0);
0143 }
0144 
0145 void KisBrushBasedPaintOpSettings::setSpacing(qreal value)
0146 {
0147     BrushWriter w(this);
0148     if (!w.brush()) return;
0149     w.brush()->setSpacing(value);
0150 }
0151 
0152 qreal KisBrushBasedPaintOpSettings::spacing()
0153 {
0154     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(this->brush(), 1.0);
0155     return this->brush()->spacing();
0156 }
0157 
0158 void KisBrushBasedPaintOpSettings::setAutoSpacing(bool active, qreal coeff)
0159 {
0160     BrushWriter w(this);
0161     if (!w.brush()) return;
0162     w.brush()->setAutoSpacing(active, coeff);
0163 }
0164 
0165 
0166 bool KisBrushBasedPaintOpSettings::autoSpacingActive()
0167 {
0168     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(this->brush(), false);
0169     return this->brush()->autoSpacingActive();
0170 }
0171 
0172 qreal KisBrushBasedPaintOpSettings::autoSpacingCoeff()
0173 {
0174     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(this->brush(), 1.0);
0175     return this->brush()->autoSpacingCoeff();
0176 }
0177 
0178 void KisBrushBasedPaintOpSettings::setPaintOpSize(qreal value)
0179 {
0180     BrushWriter w(this);
0181     if (!w.brush()) return;
0182 
0183     w.brush()->setUserEffectiveSize(value);
0184 }
0185 
0186 qreal KisBrushBasedPaintOpSettings::paintOpSize() const
0187 {
0188     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(this->brush(), 1.0);
0189     return this->brush()->userEffectiveSize();
0190 }
0191 
0192 void KisBrushBasedPaintOpSettings::setPaintOpAngle(qreal value)
0193 {
0194     BrushWriter w(this);
0195     if (!w.brush()) return;
0196 
0197     value = normalizeAngleDegrees(value);
0198     value = kisDegreesToRadians(value);
0199     w.brush()->setAngle(value);
0200 }
0201 
0202 qreal KisBrushBasedPaintOpSettings::paintOpAngle() const
0203 {
0204     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(this->brush(), 0.0);
0205     const qreal value = kisRadiansToDegrees(this->brush()->angle());
0206     return value;
0207 }
0208 
0209 
0210 #include <brushengine/kis_slider_based_paintop_property.h>
0211 #include "kis_paintop_preset.h"
0212 #include "KisPaintOpPresetUpdateProxy.h"
0213 
0214 QList<KisUniformPaintOpPropertySP> KisBrushBasedPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings, QPointer<KisPaintOpPresetUpdateProxy> updateProxy)
0215 {
0216     QList<KisUniformPaintOpPropertySP> props =
0217         listWeakToStrong(m_uniformProperties);
0218 
0219     if (props.isEmpty()) {
0220         {
0221             KisIntSliderBasedPaintOpPropertyCallback *prop =
0222                 new KisIntSliderBasedPaintOpPropertyCallback(KisIntSliderBasedPaintOpPropertyCallback::Int,
0223                                                              KisIntSliderBasedPaintOpPropertyCallback::SubType_Angle,
0224                                                              KoID("angle", i18n("Angle")),
0225                                                              settings,
0226                                                              0);
0227 
0228             prop->setRange(0, 360);
0229 
0230             prop->setReadCallback(
0231                 [](KisUniformPaintOpProperty *prop) {
0232                     KisBrushBasedPaintOpSettings *s =
0233                         dynamic_cast<KisBrushBasedPaintOpSettings*>(prop->settings().data());
0234 
0235                     prop->setValue(s->paintOpAngle());
0236                 });
0237             prop->setWriteCallback(
0238                 [](KisUniformPaintOpProperty *prop) {
0239                     KisBrushBasedPaintOpSettings *s =
0240                         dynamic_cast<KisBrushBasedPaintOpSettings*>(prop->settings().data());
0241 
0242                     s->setPaintOpAngle(prop->value().toReal());
0243                 });
0244 
0245             QObject::connect(updateProxy, SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
0246             prop->requestReadValue();
0247             props << toQShared(prop);
0248         }
0249         {
0250             KisUniformPaintOpPropertyCallback *prop =
0251                 new KisUniformPaintOpPropertyCallback(KisUniformPaintOpPropertyCallback::Bool, KoID("auto_spacing", i18n("Auto Spacing")), settings, 0);
0252 
0253             prop->setReadCallback(
0254                 [](KisUniformPaintOpProperty *prop) {
0255                     KisBrushBasedPaintOpSettings *s =
0256                         dynamic_cast<KisBrushBasedPaintOpSettings*>(prop->settings().data());
0257 
0258                     prop->setValue(s->autoSpacingActive());
0259                 });
0260             prop->setWriteCallback(
0261                 [](KisUniformPaintOpProperty *prop) {
0262                     KisBrushBasedPaintOpSettings *s =
0263                         dynamic_cast<KisBrushBasedPaintOpSettings*>(prop->settings().data());
0264 
0265                     s->setAutoSpacing(prop->value().toBool(), s->autoSpacingCoeff());
0266                 });
0267 
0268             QObject::connect(updateProxy, SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
0269             prop->requestReadValue();
0270             props << toQShared(prop);
0271         }
0272 
0273         {
0274             KisDoubleSliderBasedPaintOpPropertyCallback *prop =
0275                 new KisDoubleSliderBasedPaintOpPropertyCallback(KisDoubleSliderBasedPaintOpPropertyCallback::Double,
0276                                                                 KoID("spacing", i18n("Spacing")),
0277                                                                 settings,
0278                                                                 0);
0279 
0280             prop->setRange(0.01, 10);
0281             prop->setSingleStep(0.01);
0282             prop->setExponentRatio(3.0);
0283 
0284 
0285             prop->setReadCallback(
0286                 [](KisUniformPaintOpProperty *prop) {
0287                     KisBrushBasedPaintOpSettings *s =
0288                         dynamic_cast<KisBrushBasedPaintOpSettings*>(prop->settings().data());
0289                     if (s) {
0290                         const qreal value = s->autoSpacingActive() ?
0291                             s->autoSpacingCoeff() : s->spacing();
0292                         prop->setValue(value);
0293                     }
0294                 });
0295             prop->setWriteCallback(
0296                 [](KisUniformPaintOpProperty *prop) {
0297                     KisBrushBasedPaintOpSettings *s =
0298                         dynamic_cast<KisBrushBasedPaintOpSettings*>(prop->settings().data());
0299                     if (s) {
0300                         if (s->autoSpacingActive()) {
0301                             s->setAutoSpacing(true, prop->value().toReal());
0302                         } else {
0303                             s->setSpacing(prop->value().toReal());
0304                         }
0305                     }
0306                 });
0307 
0308             QObject::connect(updateProxy, SIGNAL(sigSettingsChanged()), prop, SLOT(requestReadValue()));
0309             prop->requestReadValue();
0310             props << toQShared(prop);
0311         }
0312     }
0313 
0314     return KisPaintOpSettings::uniformProperties(settings, updateProxy) + props;
0315 }
0316 
0317 void KisBrushBasedPaintOpSettings::onPropertyChanged()
0318 {
0319     m_savedBrush.clear();
0320     KisOutlineGenerationPolicy<KisPaintOpSettings>::onPropertyChanged();
0321 }
0322 
0323 bool KisBrushBasedPaintOpSettings::hasPatternSettings() const
0324 {
0325     return true;
0326 }
0327 
0328 QList<int> KisBrushBasedPaintOpSettings::requiredCanvasResources() const
0329 {
0330     QList<int> result;
0331     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(this->brush(), result);
0332 
0333     if (brush()->applyingGradient() || KisTextureOption::applyingGradient(this)) {
0334         result << KoCanvasResource::CurrentGradient;
0335         result << KoCanvasResource::ForegroundColor;
0336         result << KoCanvasResource::BackgroundColor;
0337     }
0338 
0339     return result;
0340 }
0341 
0342 void KisBrushBasedPaintOpSettings::setResourceCacheInterface(KoResourceCacheInterfaceSP cacheInterface)
0343 {
0344     m_savedBrush.clear();
0345 
0346     QVariant brush = cacheInterface ? cacheInterface->fetch("settings/brush") : QVariant();
0347 
0348     if (brush.isValid()) {
0349         KisBrushSP brushPointer = brush.value<KisBrushSP>();
0350         KIS_SAFE_ASSERT_RECOVER_NOOP(brushPointer);
0351 
0352         if (brushPointer) {
0353             m_savedBrush = brushPointer->clone().dynamicCast<KisBrush>();
0354         }
0355     }
0356 
0357     KisOutlineGenerationPolicy<KisPaintOpSettings>::setResourceCacheInterface(cacheInterface);
0358 }
0359 
0360 void KisBrushBasedPaintOpSettings::regenerateResourceCache(KoResourceCacheInterfaceSP cacheInterface)
0361 {
0362     KisOutlineGenerationPolicy<KisPaintOpSettings>::regenerateResourceCache(cacheInterface);
0363 
0364     KisBrushSP brush = this->brush();
0365     KIS_SAFE_ASSERT_RECOVER_RETURN(brush);
0366 
0367     brush->coldInitBrush();
0368 
0369     cacheInterface->put("settings/brush", QVariant::fromValue(brush->clone().dynamicCast<KisBrush>()));
0370 }