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 }