Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_paintop_settings.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org>
3 * SPDX-FileCopyrightText: 2008 Lukáš Tvrdý <lukast.dev@gmail.com>
4 * SPDX-FileCopyrightText: 2014 Mohit Goyal <mohit.bits2011@gmail.com>
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
9
10#include <QColor>
11#include <QDomDocument>
12#include <QDomElement>
13#include <QPainterPath>
14#include <QPointer>
15
16#include <KoPointerEvent.h>
17#include <KoColor.h>
19#include <KoViewConverter.h>
20
21#include "kis_dom_utils.h"
22#include "kis_paintop_preset.h"
23#include "kis_paint_layer.h"
24#include "kis_image.h"
25#include "kis_painter.h"
26#include "kis_paint_device.h"
33#include <time.h>
34#include <kis_types.h>
35#include <kis_signals_blocker.h>
36
39
41#include "kis_algebra_2d.h"
42#include "kis_image_config.h"
47
48#define SANITY_CHECK_CACHE
49
50#ifdef SANITY_CHECK_CACHE
51#include "kis_random_source.h"
52#endif
53
54struct Q_DECL_HIDDEN KisPaintOpSettings::Private {
56 : disableDirtyNotifications(false)
58 , versionRandomSource(int(reinterpret_cast<std::intptr_t>(this)))
59 , versionCookie(versionRandomSource.generate())
60#endif
61 {}
62
63 Private(const Private &rhs)
64 : settingsWidget(0),
65 modelName(rhs.modelName),
66 resourcesInterface(rhs.resourcesInterface),
67 canvasResourcesInterface(rhs.canvasResourcesInterface),
68 resourceCacheInterface(rhs.resourceCacheInterface),
69 disableDirtyNotifications(false)
71 , versionRandomSource(int(reinterpret_cast<std::intptr_t>(this)))
72 , versionCookie(rhs.versionCookie)
73#endif
74 {
78 }
79
81 QString modelName;
87
89
90#ifdef SANITY_CHECK_CACHE
93#endif
94
96 public:
98 : m_d(d),
99 m_oldNotificationsState(d->disableDirtyNotifications)
100 {
101 m_d->disableDirtyNotifications = true;
102 }
103
105 m_d->disableDirtyNotifications = m_oldNotificationsState;
106 }
107
108 private:
111 Q_DISABLE_COPY(DirtyNotificationsLocker)
112 };
113};
114
118
124
128
134
136{
137 d->updateListener = listener;
138}
139
141{
142 return d->updateListener;
143}
144
145bool KisPaintOpSettings::mousePressEvent(const KisPaintInformation &paintInformation, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode)
146{
147 Q_UNUSED(modifiers);
148 Q_UNUSED(currentNode);
149 setRandomOffset(paintInformation);
150 return true; // ignore the event by default
151}
152
154{
155 return true; // ignore the event by default
156}
157
159{
160 bool disableDirtyBefore = d->disableDirtyNotifications;
161 d->disableDirtyNotifications = true;
162 if (getBool("Texture/Pattern/Enabled")) {
163 if (getBool("Texture/Pattern/isRandomOffsetX")) {
164 setProperty("Texture/Pattern/OffsetX",
165 paintInformation.randomSource()->generate(0, KisPropertiesConfiguration::getInt("Texture/Pattern/MaximumOffsetX")));
166 }
167 if (getBool("Texture/Pattern/isRandomOffsetY")) {
168 setProperty("Texture/Pattern/OffsetY",
169 paintInformation.randomSource()->generate(0, KisPropertiesConfiguration::getInt("Texture/Pattern/MaximumOffsetY")));
170
171 }
172 }
173 d->disableDirtyNotifications = disableDirtyBefore;
174}
175
180
182{
184
185 const KoID pixelBrushId(KisPaintOpUtils::MaskingBrushPaintOpId, QString());
186
188 maskingSettings->setCanvasResourcesInterface(canvasResourcesInterface());
191
192 const bool useMasterSize = this->getBool(KisPaintOpUtils::MaskingBrushUseMasterSizeTag, true);
193 if (useMasterSize) {
209 const qreal maxMaskingBrushSize = KisImageConfig(true).maxMaskingBrushSize();
210 const qreal masterSizeCoeff = getDouble(KisPaintOpUtils::MaskingBrushMasterSizeCoeffTag, 1.0);
211 maskingSettings->setPaintOpSize(qMin(maxMaskingBrushSize, masterSizeCoeff * paintOpSize()));
212 }
213
214 if (d->resourceCacheInterface) {
215 maskingSettings->setResourceCacheInterface(
218 d->resourceCacheInterface)));
219 }
220
221 return maskingSettings;
222}
223
225{
226 return false;
227}
228
233
235{
236 return d->canvasResourcesInterface;
237}
238
240{
241 d->canvasResourcesInterface = canvasResourcesInterface;
242}
243
245{
246 d->resourceCacheInterface = cacheInterface;
247}
248
250{
251 return d->resourceCacheInterface;
252}
253
255{
256 if (hasMaskingSettings()) {
258
259 KoResourceCacheInterfaceSP wrappedCacheInterface =
262 cacheInterface));
263
264 maskingSettings->regenerateResourceCache(wrappedCacheInterface);
265 }
266}
267
269{
270#ifdef SANITY_CHECK_CACHE
271 return d->versionCookie;
272#else
273 return 0;
274#endif
275}
276
281
283{
284 return d->resourcesInterface;
285}
286
288{
289 d->resourcesInterface = resourcesInterface;
290}
291
293{
294 QString paintopID = getString("paintop");
295 if (paintopID.isEmpty())
296 return 0;
297
299 QMapIterator<QString, QVariant> i(getProperties());
300 while (i.hasNext()) {
301 i.next();
302 settings->setProperty(i.key(), QVariant(i.value()));
303 }
304
305 settings->setCanvasResourcesInterface(this->canvasResourcesInterface());
306
307#ifdef SANITY_CHECK_CACHE
308 settings->d->versionCookie = this->d->versionCookie;
309#endif
310
311 return settings;
312}
313
314void KisPaintOpSettings::resetSettings(const QStringList &preserveProperties)
315{
316 QStringList allKeys = preserveProperties;
317 allKeys << "paintop";
318
319 QHash<QString, QVariant> preserved;
320 Q_FOREACH (const QString &key, allKeys) {
321 if (hasProperty(key)) {
322 preserved[key] = getProperty(key);
323 }
324 }
325
327
328 for (auto it = preserved.constBegin(); it != preserved.constEnd(); ++it) {
329 setProperty(it.key(), it.value());
330 }
331}
332
336
338{
340 KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
341
342 proxy->setProperty("OpacityValue", value);
343}
344
346{
348 KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
349
350 proxy->setProperty("FlowValue", value);
351}
352
354{
356 KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
357
358 if (!proxy->hasProperty("brush_definition")) return;
359
360 // Setting the Fade value is a bit more complex.
361 QDomDocument doc;
362 doc.setContent(proxy->getString("brush_definition"));
363
364 QDomElement element = doc.documentElement();
365 QDomElement elementChild = element.elementsByTagName("MaskGenerator").item(0).toElement();
366
367 elementChild.attributeNode("hfade").setValue(KisDomUtils::toString(value));
368 elementChild.attributeNode("vfade").setValue(KisDomUtils::toString(value));
369
370 proxy->setProperty("brush_definition", doc.toString());
371}
372
374{
376 KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
377
378 if (!proxy->hasProperty("PressureScatter")) return;
379
380 proxy->setProperty("ScatterValue", value);
381 proxy->setProperty("PressureScatter", !qFuzzyIsNull(value));
382}
383
385{
387 KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
388
389 proxy->setProperty("CompositeOp", value);
390}
391
393{
395
396 return proxy->getDouble("OpacityValue", 1.0);
397}
398
400{
402 KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
403
404 return proxy->getDouble("FlowValue", 1.0);
405}
406
408{
410 KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
411
412 if (!proxy->hasProperty("brush_definition")) return 1.0;
413
414 QDomDocument doc;
415 doc.setContent(proxy->getString("brush_definition"));
416
417 QDomElement element = doc.documentElement();
418 QDomElement elementChild = element.elementsByTagName("MaskGenerator").item(0).toElement();
419
420 if (elementChild.attributeNode("hfade").value().toDouble() >= elementChild.attributeNode("vfade").value().toDouble()) {
421 return elementChild.attributeNode("hfade").value().toDouble();
422 } else {
423 return elementChild.attributeNode("vfade").value().toDouble();
424 }
425
426}
427
429{
431 KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
432
433 return proxy->getDouble("ScatterValue", 0.0);
434}
435
436
438{
440 KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
441
442 return proxy->getDouble("Texture/Pattern/Scale", 0.5);
443}
444
446{
448 KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
449
450 return proxy->getString("CompositeOp", COMPOSITE_OVER);
451}
452
454{
456 KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
457
458 proxy->setProperty("EraserMode", value);
459}
460
462{
464 KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
465
466 return proxy->getBool("EraserMode", false);
467}
468
473
475{
476 return getDouble("SavedEraserSize", 0.0);
477}
478
480{
481 setProperty("SavedEraserSize", value);
482 setPropertyNotSaved("SavedEraserSize");
483}
484
486{
487 return getDouble("SavedBrushSize", 0.0);
488}
489
491{
492 setProperty("SavedBrushSize", value);
493 setPropertyNotSaved("SavedBrushSize");
494}
495
497{
498 return getDouble("SavedEraserOpacity", 0.0);
499}
500
502{
503 setProperty("SavedEraserOpacity", value);
504 setPropertyNotSaved("SavedEraserOpacity");
505}
506
508{
509 return getDouble("SavedBrushOpacity", 0.0);
510}
511
513{
514 setProperty("SavedBrushOpacity", value);
515 setPropertyNotSaved("SavedBrushOpacity");
516}
517
518QString KisPaintOpSettings::modelName() const
519{
520 return d->modelName;
521}
522
523void KisPaintOpSettings::setModelName(const QString & modelName)
524{
525 d->modelName = modelName;
526}
527
529{
530 return true;
531}
532
537
539{
540 return getBool(AIRBRUSH_ENABLED, false);
541}
542
544{
545 qreal rate = getDouble(AIRBRUSH_RATE, 1.0);
546 if (rate == 0.0) {
547 return LONG_TIME;
548 }
549 else {
550 return 1000.0 / rate;
551 }
552}
553
555{
556 return getBool(SPACING_USE_UPDATES, false);
557}
558
560{
561 return false;
562}
563
565{
567 if (mode.isVisible) {
568 path = ellipseOutline(10, 10, 1.0, 0);
569
570 if (mode.showTiltDecoration) {
571 path.addPath(makeTiltIndicator(info, QPointF(0.0, 0.0), 0.0, 2.0));
572 }
573
574 path.translate(KisAlgebra2D::alignForZoom(info.pos(), alignForZoom));
575 }
576
577 return path;
578}
579
580KisOptimizedBrushOutline KisPaintOpSettings::ellipseOutline(qreal width, qreal height, qreal scale, qreal rotation)
581{
582 QPainterPath path;
583 QRectF ellipse(0, 0, width * scale, height * scale);
584 ellipse.translate(-ellipse.center());
585 path.addEllipse(ellipse);
586
587 QTransform m;
588 m.reset();
589 m.rotate(rotation);
590 path = m.map(path);
591 return path;
592}
593
595 QPointF const& start, qreal maxLength, qreal angle)
596{
597 if (maxLength == 0.0) maxLength = 50.0;
598 maxLength = qMax(maxLength, 50.0);
599 qreal const length = maxLength * (1 - info.tiltElevation(info, 60.0, 60.0, true));
600 qreal const baseAngle = 360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0);
601
602 QLineF guideLine = QLineF::fromPolar(length, baseAngle + angle);
603 guideLine.translate(start);
604 QPainterPath ret;
605 ret.moveTo(guideLine.p1());
606 ret.lineTo(guideLine.p2());
607 guideLine.setAngle(baseAngle - angle);
608 ret.lineTo(guideLine.p2());
609 ret.lineTo(guideLine.p1());
610 return ret;
611}
612
613void KisPaintOpSettings::setProperty(const QString & name, const QVariant & value)
614{
615 if (value != KisPropertiesConfiguration::getProperty(name) && !d->disableDirtyNotifications) {
616 UpdateListenerSP updateListener = d->updateListener.toStrongRef();
617
618 if (updateListener) {
619 updateListener->setDirty(true);
620 }
621 }
622
625}
626
627
629{
630 // clear cached data for the resource
631 d->resourceCacheInterface.clear();
632
633#ifdef SANITY_CHECK_CACHE
634 d->versionCookie = d->versionRandomSource.generate();
635#endif
636
637 UpdateListenerSP updateListener = d->updateListener.toStrongRef();
638
639 if (updateListener) {
640 updateListener->notifySettingsChanged();
641 }
642}
643
645{
646 return config->getBool("lodUserAllowed", true);
647}
648
650{
651 config->setProperty("lodUserAllowed", value);
652}
653
655{
656 return true;
657}
658
660{
661 return getDouble("lodSizeThreshold", 100.0);
662}
663
665{
666 setProperty("lodSizeThreshold", value);
667}
668
670
672{
674 listWeakToStrong(d->uniformProperties);
675
676
677 if (props.isEmpty()) {
679
680 props.append(createProperty(opacity, settings, updateProxy));
681 props.append(createProperty(size, settings, updateProxy));
682 props.append(createProperty(flow, settings, updateProxy));
683
684 d->uniformProperties = listStrongToWeak(props);
685 }
686
687 return props;
688}
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
float value(const T *src, size_t ch)
const QString COMPOSITE_OVER
const QString COMPOSITE_MULT
const QString COMPOSITE_ERASE
const QString COMPOSITE_ALPHA_DARKEN
int maxMaskingBrushSize() const
static KisLockedPropertiesServer * instance()
KisLockedPropertiesProxySP createLockedPropertiesProxy(KisPropertiesConfiguration *settings)
KisRandomSourceSP randomSource() const
const QPointF & pos() const
static qreal tiltDirection(const KisPaintInformation &info, bool normalize=true)
static qreal tiltElevation(const KisPaintInformation &info, qreal maxTiltX=60.0, qreal maxTiltY=60.0, bool normalize=true)
static KisPaintOpRegistry * instance()
KisPaintOpSettingsSP createSettings(const KoID &id, KisResourcesInterfaceSP resourcesInterface) const
DirtyNotificationsLocker(KisPaintOpSettings::Private *d)
qint64 generate() const
Definition KoID.h:30
static bool qFuzzyIsNull(half h)
#define SANITY_CHECK_CACHE
const QString AIRBRUSH_ENABLED
const QString AIRBRUSH_RATE
const QString SPACING_USE_UPDATES
QSharedPointer< T > toQShared(T *ptr)
Container< QSharedPointer< T > > listWeakToStrong(const Container< QWeakPointer< T > > &container, bool allOrNothing=true)
Container< QWeakPointer< T > > listStrongToWeak(const Container< QSharedPointer< T > > &container)
const qreal LONG_TIME
KisPinnedSharedPtr< KisPaintOpSettings > KisPaintOpSettingsSP
Definition kis_types.h:242
QPointF alignForZoom(const QPointF &pt, qreal zoom)
QString toString(const QString &value)
const char MaskingBrushEnabledTag[]
const char MaskingBrushUseMasterSizeTag[]
const char MaskingBrushPaintOpId[]
const char MaskingBrushMasterSizeCoeffTag[]
const char MaskingBrushPresetPrefix[]
const char MaskingBrushCompositeOpTag[]
virtual void regenerateResourceCache(KoResourceCacheInterfaceSP cacheInterface)
void setSavedEraserSize(qreal value)
static QPainterPath makeTiltIndicator(KisPaintInformation const &info, QPointF const &start, qreal lengthScale, qreal angle)
virtual bool isValid() const
virtual QString indirectPaintingCompositeOp() const
quint64 sanityVersionCookie() const
virtual void setRandomOffset(const KisPaintInformation &paintInformation)
static void setLodUserAllowed(KisPropertiesConfigurationSP config, bool value)
KisRandomSource versionRandomSource
static bool isLodUserAllowed(const KisPropertiesConfigurationSP config)
virtual bool needsAsynchronousUpdates() const
void setSavedBrushSize(qreal value)
virtual bool hasPatternSettings() const
KisResourcesInterfaceSP resourcesInterface
static KisOptimizedBrushOutline ellipseOutline(qreal width, qreal height, qreal scale, qreal rotation)
void setCanvasResourcesInterface(KoCanvasResourcesInterfaceSP canvasResourcesInterface)
KisPaintOpSettings(KisResourcesInterfaceSP resourcesInterface)
virtual qreal paintOpSize() const =0
void setPaintOpCompositeOp(const QString &value)
QPointer< KisPaintOpConfigWidget > settingsWidget
void setPaintOpFlow(qreal value)
virtual KisOptimizedBrushOutline brushOutline(const KisPaintInformation &info, const OutlineMode &mode, qreal alignForZoom)
virtual KisPaintOpSettingsSP clone() const
virtual qreal airbrushInterval() const
virtual bool lodSizeThresholdSupported() const
void setModelName(const QString &modelName)
virtual void resetSettings(const QStringList &preserveProperties=QStringList())
void setProperty(const QString &name, const QVariant &value) override
virtual void setPaintOpOpacity(qreal value)
virtual qreal paintOpPatternSize()
void setEraserMode(bool value)
void setPaintOpFade(qreal value)
void setResourcesInterface(KisResourcesInterfaceSP resourcesInterface)
virtual void setResourceCacheInterface(KoResourceCacheInterfaceSP cacheInterface)
virtual bool isAirbrushing() const
void setSavedBrushOpacity(qreal value)
void setSavedEraserOpacity(qreal value)
const QScopedPointer< Private > d
virtual bool mousePressEvent(const KisPaintInformation &paintInformation, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode)
KisPaintOpSettingsSP createMaskingSettings() const
KoCanvasResourcesInterfaceSP canvasResourcesInterface
QList< KisUniformPaintOpPropertyWSP > uniformProperties
void setLodSizeThreshold(qreal value)
UpdateListenerWSP updateListener
void setPaintOpScatter(qreal value)
QString maskingBrushCompositeOp() const
void setUpdateListener(UpdateListenerWSP listener)
virtual bool useSpacingUpdates() const
KoResourceCacheInterfaceSP resourceCacheInterface
virtual QList< int > requiredCanvasResources() const
Private(const Private &rhs)
void setPropertyNotSaved(const QString &name)
Marks a property that should not be saved by toXML.
QString getString(const QString &name, const QString &def=QString()) const
void clearProperties()
Clear the map of properties.
virtual bool hasProperty(const QString &name) const
virtual void setProperty(const QString &name, const QVariant &value)
bool getBool(const QString &name, bool def=false) const
int getInt(const QString &name, int def=0) const
double getDouble(const QString &name, double def=0.0) const
void getPrefixedProperties(const QString &prefix, KisPropertiesConfiguration *config) const
virtual bool getProperty(const QString &name, QVariant &value) const
virtual QMap< QString, QVariant > getProperties() const