diff --git a/libs/image/brushengine/kis_paintop_preset.cpp b/libs/image/brushengine/kis_paintop_preset.cpp index cb0be94b23..945f523112 100644 --- a/libs/image/brushengine/kis_paintop_preset.cpp +++ b/libs/image/brushengine/kis_paintop_preset.cpp @@ -1,450 +1,474 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * Copyright (C) Sven Langkamp , (C) 2009 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include "kis_paintop_registry.h" #include "kis_painter.h" #include #include "kis_paint_device.h" #include "kis_image.h" #include "kis_paintop_settings_update_proxy.h" #include -#include +#include #include struct Q_DECL_HIDDEN KisPaintOpPreset::Private { Private(KisPaintOpPreset *q) { proxyParent = new ProxyParent(q); } QPointer proxyParent{0}; KisPaintOpSettingsSP settings {0}; QPointer updateProxy {0}; }; KisPaintOpPreset::KisPaintOpPreset() : KoResource(QString()) , d(new Private(this)) { } KisPaintOpPreset::KisPaintOpPreset(const QString & fileName) : KoResource(fileName) , d(new Private(this)) { } KisPaintOpPreset::~KisPaintOpPreset() { delete d; } KisPaintOpPreset::KisPaintOpPreset(const KisPaintOpPreset &rhs) : KoResource(rhs) , d(new Private(this)) { if (rhs.settings()) { setSettings(rhs.settings()); // the settings are cloned inside! } setDirty(isDirty()); // only valid if we could clone the settings setValid(rhs.settings()); setPaintOp(rhs.paintOp()); setName(rhs.name()); setImage(rhs.image()); settings()->setUpdateProxy(rhs.updateProxy()); } KoResourceSP KisPaintOpPreset::clone() const { return KoResourceSP(new KisPaintOpPreset(*this)); } void KisPaintOpPreset::setPaintOp(const KoID & paintOp) { Q_ASSERT(d->settings); d->settings->setProperty("paintop", paintOp.id()); } KoID KisPaintOpPreset::paintOp() const { Q_ASSERT(d->settings); return KoID(d->settings->getString("paintop")); } void KisPaintOpPreset::setOptionsWidget(KisPaintOpConfigWidget* widget) { if (d->settings) { d->settings->setOptionsWidget(widget); if (widget) { widget->setConfigurationSafe(d->settings); } } } void KisPaintOpPreset::setSettings(KisPaintOpSettingsSP settings) { Q_ASSERT(settings); Q_ASSERT(!settings->getString("paintop", QString()).isEmpty()); KisResourceDirtyStateSaver dirtyStateSaver(this); Q_UNUSED(dirtyStateSaver); KisPaintOpConfigWidget *oldOptionsWidget = 0; if (d->settings) { oldOptionsWidget = d->settings->optionsWidget(); d->settings->setOptionsWidget(0); d->settings->setUpdateProxy(updateProxy()); d->settings = 0; } if (settings) { d->settings = settings->clone(); d->settings->setUpdateProxy(updateProxy()); if (oldOptionsWidget) { oldOptionsWidget->setConfigurationSafe(d->settings); d->settings->setOptionsWidget(oldOptionsWidget); } } setValid(d->settings); if (d->updateProxy) { d->updateProxy->notifyUniformPropertiesChanged(); d->updateProxy->notifySettingsChanged(); } } KisPaintOpSettingsSP KisPaintOpPreset::settings() const { Q_ASSERT(d->settings); Q_ASSERT(!d->settings->getString("paintop", QString()).isEmpty()); return d->settings; } bool KisPaintOpPreset::load(KisResourcesInterfaceSP resourcesInterface) { qDebug() << "Load preset " << filename(); setValid(false); if (filename().isEmpty()) { return false; } QIODevice *dev = 0; QByteArray ba; if (filename().startsWith("bundle://")) { QString bn = filename().mid(9); int pos = bn.lastIndexOf(":"); QString fn = bn.right(bn.size() - pos - 1); bn = bn.left(pos); QScopedPointer resourceStore(KoStore::createStore(bn, KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip)); if (!resourceStore || resourceStore->bad()) { warnKrita << "Could not open store on bundle" << bn; return false; } if (resourceStore->isOpen()) resourceStore->close(); if (!resourceStore->open(fn)) { warnKrita << "Could not open preset" << fn << "in bundle" << bn; return false; } ba = resourceStore->device()->readAll(); dev = new QBuffer(&ba); resourceStore->close(); } else { dev = new QFile(filename()); if (dev->size() == 0) { delete dev; return false; } if (!dev->open(QIODevice::ReadOnly)) { warnKrita << "Can't open file " << filename(); delete dev; return false; } } bool res = loadFromDevice(dev, resourcesInterface); delete dev; setValid(res); setDirty(false); return res; } bool KisPaintOpPreset::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) { QImageReader reader(dev, "PNG"); QString version = reader.text("version"); QString preset = reader.text("preset"); dbgImage << version; if (version != "2.2") { return false; } QImage img; if (!reader.read(&img)) { dbgImage << "Fail to decode PNG"; return false; } //Workaround for broken presets //Presets was saved with nested cdata section preset.replace(""); preset.replace("]]>", ""); QDomDocument doc; if (!doc.setContent(preset)) { return false; } fromXML(doc.documentElement(), resourcesInterface); if (!d->settings) { return false; } setValid(true); setImage(img); return true; } bool KisPaintOpPreset::save() { const QString paintopid = d->settings->getString("paintop", QString()); if (paintopid.isEmpty()) return false; return KoResource::save(); } void KisPaintOpPreset::toXML(QDomDocument& doc, QDomElement& elt) const { QString paintopid = d->settings->getString("paintop", QString()); elt.setAttribute("paintopid", paintopid); elt.setAttribute("name", name()); // sanitize the settings bool hasTexture = d->settings->getBool("Texture/Pattern/Enabled"); if (!hasTexture) { Q_FOREACH (const QString & key, d->settings->getProperties().keys()) { if (key.startsWith("Texture") && key != "Texture/Pattern/Enabled") { d->settings->removeProperty(key); } } } d->settings->toXML(doc, elt); } void KisPaintOpPreset::fromXML(const QDomElement& presetElt, KisResourcesInterfaceSP resourcesInterface) { setName(presetElt.attribute("name")); QString paintopid = presetElt.attribute("paintopid"); if (paintopid.isEmpty()) { dbgImage << "No paintopid attribute"; setValid(false); return; } if (KisPaintOpRegistry::instance()->get(paintopid) == 0) { dbgImage << "No paintop " << paintopid; setValid(false); return; } KoID id(paintopid, QString()); KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->createSettings(id, resourcesInterface); if (!settings) { setValid(false); warnKrita << "Could not load settings for preset" << paintopid; return; } settings->fromXML(presetElt); // sanitize the settings bool hasTexture = settings->getBool("Texture/Pattern/Enabled"); if (!hasTexture) { Q_FOREACH (const QString & key, settings->getProperties().keys()) { if (key.startsWith("Texture") && key != "Texture/Pattern/Enabled") { settings->removeProperty(key); } } } setSettings(settings); } bool KisPaintOpPreset::saveToDevice(QIODevice *dev) const { QImageWriter writer(dev, "PNG"); QDomDocument doc; QDomElement root = doc.createElement("Preset"); toXML(doc, root); doc.appendChild(root); writer.setText("version", "2.2"); writer.setText("preset", doc.toString()); QImage img; if (image().isNull()) { img = QImage(1, 1, QImage::Format_RGB32); } else { img = image(); } const_cast(this)->setDirty(false); KoResource::saveToDevice(dev); return writer.write(img); } QPointer KisPaintOpPreset::updateProxy() const { if (!d->updateProxy) { d->updateProxy = new KisPaintopSettingsUpdateProxy(d->proxyParent); } return d->updateProxy; } QPointer KisPaintOpPreset::updateProxyNoCreate() const { return d->updateProxy; } QList KisPaintOpPreset::uniformProperties() { return d->settings->uniformProperties(d->settings); } bool KisPaintOpPreset::hasMaskingPreset() const { return d->settings && d->settings->hasMaskingSettings(); } KisPaintOpPresetSP KisPaintOpPreset::createMaskingPreset() const { KisPaintOpPresetSP result; if (d->settings && d->settings->hasMaskingSettings()) { result.reset(new KisPaintOpPreset()); result->setSettings(d->settings->createMaskingSettings()); if (!result->valid()) { result.clear(); } } return result; } KisResourcesInterfaceSP KisPaintOpPreset::resourcesInterface() const { return d->settings ? d->settings->resourcesInterface() : nullptr; } void KisPaintOpPreset::setResourcesInterface(KisResourcesInterfaceSP resourcesInterface) { KIS_SAFE_ASSERT_RECOVER_RETURN(d->settings); d->settings->setResourcesInterface(resourcesInterface); } +namespace KisLinkedResourcesOperators +{ +template <> +struct ResourceTraits +{ + template + using SharedPointerType = QSharedPointer; + + template + static inline SharedPointerType dynamicCastSP(SharedPointerType src) { + return src.template dynamicCast(); + } +}; +} + void KisPaintOpPreset::createLocalResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface) { - QList resources = linkedResources(globalResourcesInterface); - d->settings->setResourcesInterface(QSharedPointer::create(resources)); + KisLinkedResourcesOperators::createLocalResourcesSnapshot(this, globalResourcesInterface); +} + +bool KisPaintOpPreset::hasLocalResourcesSnapshot() const +{ + return KisLinkedResourcesOperators::hasLocalResourcesSnapshot(this); +} + +KisPaintOpPresetSP KisPaintOpPreset::cloneWithResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface) const +{ + return KisLinkedResourcesOperators::cloneWithResourcesSnapshot(this, globalResourcesInterface); } QList KisPaintOpPreset::linkedResources(KisResourcesInterfaceSP globalResourcesInterface) const { QList resources; KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(d->settings, resources); KisPaintOpFactory* f = KisPaintOpRegistry::instance()->value(paintOp().id()); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(f, resources); resources << f->prepareResources(d->settings, globalResourcesInterface); if (hasMaskingPreset()) { KisPaintOpPresetSP maskingPreset = createMaskingPreset(); KisPaintOpFactory* f = KisPaintOpRegistry::instance()->value(maskingPreset->paintOp().id()); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(f, resources); resources << f->prepareResources(maskingPreset->settings(), globalResourcesInterface); } return resources; } KisPaintOpPreset::UpdatedPostponer::UpdatedPostponer(KisPaintOpPresetSP preset) : m_updateProxy(preset->updateProxyNoCreate()) { if (m_updateProxy) { m_updateProxy->postponeSettingsChanges(); } } KisPaintOpPreset::UpdatedPostponer::~UpdatedPostponer() { if (m_updateProxy) { m_updateProxy->unpostponeSettingsChanges(); } } diff --git a/libs/image/brushengine/kis_paintop_preset.h b/libs/image/brushengine/kis_paintop_preset.h index ba4bd9cf04..590142960f 100644 --- a/libs/image/brushengine/kis_paintop_preset.h +++ b/libs/image/brushengine/kis_paintop_preset.h @@ -1,173 +1,180 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_PAINTOP_PRESET_H #define KIS_PAINTOP_PRESET_H #include #include #include "KoID.h" #include "kis_types.h" #include "kis_shared.h" #include "kritaimage_export.h" #include #include class KisPaintOpConfigWidget; class ProxyParent : public QObject { Q_OBJECT public: ProxyParent(KisPaintOpPreset *preset) { m_preset = preset; } KisPaintOpPreset *m_preset {0}; }; /** * A KisPaintOpPreset contains a particular set of settings * associated with a paintop, like brush, paintopsettings. * A new property in this class is to make it dirty. That means the * user can now temporarily save any tweaks in the Preset throughout * the session. The Dirty Preset setting/unsetting is handled by KisPaintOpPresetSettings */ class KRITAIMAGE_EXPORT KisPaintOpPreset : public KoResource { - public: /** * @brief The UpdatedPostponer class * @see KisPaintopSettingsUpdateProxy::postponeSettingsChanges() */ class KRITAIMAGE_EXPORT UpdatedPostponer{ public: UpdatedPostponer(KisPaintOpPresetSP preset); ~UpdatedPostponer(); private: QPointer m_updateProxy; }; public: KisPaintOpPreset(); KisPaintOpPreset(const QString& filename); ~KisPaintOpPreset() override; KisPaintOpPreset(const KisPaintOpPreset &rhs); KisPaintOpPreset &operator=(const KisPaintOpPreset &rhs) = delete; KoResourceSP clone() const override; /// set the id of the paintop plugin void setPaintOp(const KoID & paintOp); /// return the id of the paintop plugin KoID paintOp() const; /// replace the current settings object with the specified settings void setSettings(KisPaintOpSettingsSP settings); void setOriginalSettings(KisPaintOpSettingsSP originalSettings); /// return the settings that define this paintop preset KisPaintOpSettingsSP settings() const; KisPaintOpSettingsSP originalSettings() const; bool load(KisResourcesInterfaceSP resourcesInterface) override; bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override; bool save() override; bool saveToDevice(QIODevice* dev) const override; QPair resourceType() const override { return QPair(ResourceType::PaintOpPresets, ""); } void toXML(QDomDocument& doc, QDomElement& elt) const; void fromXML(const QDomElement& elt, KisResourcesInterfaceSP resourcesInterface); bool removable() const { return true; } QString defaultFileExtension() const override { return ".kpp"; } void setOptionsWidget(KisPaintOpConfigWidget *widget); QPointer updateProxy() const; QPointer updateProxyNoCreate() const; QList uniformProperties(); /** * @return true if this preset demands a secondary masked brush running * alongside it */ bool hasMaskingPreset() const; /** * @return a newly created preset of the masked brush that should be run * alongside the current brush */ KisPaintOpPresetSP createMaskingPreset() const; /** * @return resource interface that is used by KisPaintOpSettings object for * loading linked resources */ KisResourcesInterfaceSP resourcesInterface() const; /** * Set resource interface that will be used by KisPaintOpSettings object for * loading linked resources */ void setResourcesInterface(KisResourcesInterfaceSP resourcesInterface); /** - * Loads all the linked resources either from \p globalResourcesInterface or - * from embedded data. The preset first tried to fetch the linked resource - * from the global source, and only if it fails, tries to load it from the - * embedded data. + * \see KisLinkedResourcesOperators::createLocalResourcesSnapshot + */ + void createLocalResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface = nullptr); + + /** + * \see KisLinkedResourcesOperators::hasLocalResourcesSnapshot */ - void createLocalResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface); + bool hasLocalResourcesSnapshot() const; + + /** + * \see KisLinkedResourcesOperators::cloneWithResourcesSnapshot + */ + KisPaintOpPresetSP cloneWithResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface = nullptr) const; + QList linkedResources(KisResourcesInterfaceSP globalResourcesInterface) const override; private: struct Private; Private * const d; }; Q_DECLARE_METATYPE(KisPaintOpPresetSP) #endif diff --git a/libs/image/filter/kis_filter_configuration.cc b/libs/image/filter/kis_filter_configuration.cc index 57ad8854aa..a02b500029 100644 --- a/libs/image/filter/kis_filter_configuration.cc +++ b/libs/image/filter/kis_filter_configuration.cc @@ -1,236 +1,238 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "filter/kis_filter_configuration.h" #include "filter/kis_filter.h" #include #include #include #include "filter/kis_filter_registry.h" #include "kis_transaction.h" #include "kis_undo_adapter.h" #include "kis_painter.h" #include "kis_selection.h" #include "KoID.h" #include "kis_types.h" +#include #include "kis_config_widget.h" struct Q_DECL_HIDDEN KisFilterConfiguration::Private { QString name; qint32 version; QBitArray channelFlags; KisCubicCurve curve; QList< KisCubicCurve > curves; KisResourcesInterfaceSP resourcesInterface = 0; Private(const QString & _name, qint32 _version, KisResourcesInterfaceSP _resourcesInterface) : name(_name), version(_version), resourcesInterface(_resourcesInterface) { } Private(const Private &rhs) : name(rhs.name), version(rhs.version), channelFlags(rhs.channelFlags), curve(rhs.curve), curves(rhs.curves), resourcesInterface(rhs.resourcesInterface) { } #ifdef SANITY_CHECK_FILTER_CONFIGURATION_OWNER QAtomicInt sanityUsageCounter; #endif /* SANITY_CHECK_FILTER_CONFIGURATION_OWNER */ }; KisFilterConfiguration::KisFilterConfiguration(const QString & name, qint32 version, KisResourcesInterfaceSP resourcesInterface) : d(new Private(name, version, resourcesInterface)) { } KisFilterConfigurationSP KisFilterConfiguration::clone() const { return new KisFilterConfiguration(*this); } -KisFilterConfigurationSP KisFilterConfiguration::cloneWithResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface) const -{ - KisFilterConfigurationSP config = this->clone(); - - if (!config->hasLocalResourcesSnapshot()) { - config->createLocalResourcesSnapshot(globalResourcesInterface); - KIS_SAFE_ASSERT_RECOVER_NOOP(config->hasLocalResourcesSnapshot()); - } - - return config; -} - KisFilterConfiguration::KisFilterConfiguration(const KisFilterConfiguration & rhs) : KisPropertiesConfiguration(rhs) , d(new Private(*rhs.d)) { } KisFilterConfiguration::~KisFilterConfiguration() { delete d; } void KisFilterConfiguration::fromLegacyXML(const QDomElement& e) { clearProperties(); d->name = e.attribute("name"); d->version = e.attribute("version").toInt(); QDomNode n = e.firstChild(); while (!n.isNull()) { // We don't nest elements in filter configuration. For now... QDomElement e = n.toElement(); QString name; QString type; QString value; if (!e.isNull()) { if (e.tagName() == "property") { name = e.attribute("name"); type = e.attribute("type"); value = e.text(); // XXX Convert the variant pro-actively to the right type? setProperty(name, QVariant(value)); } } n = n.nextSibling(); } } void KisFilterConfiguration::fromXML(const QDomElement& elt) { d->version = elt.attribute("version").toInt(); KisPropertiesConfiguration::fromXML(elt); } void KisFilterConfiguration::toXML(QDomDocument& doc, QDomElement& elt) const { elt.setAttribute("version", d->version); KisPropertiesConfiguration::toXML(doc, elt); } const QString & KisFilterConfiguration::name() const { return d->name; } qint32 KisFilterConfiguration::version() const { return d->version; } void KisFilterConfiguration::setVersion(qint32 version) { d->version = version; } const KisCubicCurve& KisFilterConfiguration::curve() const { return d->curve; } void KisFilterConfiguration::setCurve(const KisCubicCurve& curve) { d->curve = curve; } const QList< KisCubicCurve >& KisFilterConfiguration::curves() const { return d->curves; } -// TODO: move into a separate interface class -#include -#include -#include - KisResourcesInterfaceSP KisFilterConfiguration::resourcesInterface() const { return d->resourcesInterface; } void KisFilterConfiguration::setResourcesInterface(KisResourcesInterfaceSP resourcesInterface) { d->resourcesInterface = resourcesInterface; } +namespace KisLinkedResourcesOperators +{ +template <> +struct ResourceTraits +{ + template + using SharedPointerType = KisPinnedSharedPtr; + + template + static inline SharedPointerType dynamicCastSP(SharedPointerType src) { + return SharedPointerType(dynamic_cast(src.data())); + } +}; +} + void KisFilterConfiguration::createLocalResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface) { - KIS_SAFE_ASSERT_RECOVER_RETURN(QThread::currentThread() == qApp->thread()); - QList resources = linkedResources(globalResourcesInterface ? globalResourcesInterface : resourcesInterface()); - setResourcesInterface(QSharedPointer::create(resources)); + KisLinkedResourcesOperators::createLocalResourcesSnapshot(this, globalResourcesInterface); } bool KisFilterConfiguration::hasLocalResourcesSnapshot() const { - return d->resourcesInterface.dynamicCast(); + return KisLinkedResourcesOperators::hasLocalResourcesSnapshot(this); +} + +KisFilterConfigurationSP KisFilterConfiguration::cloneWithResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface) const +{ + return KisLinkedResourcesOperators::cloneWithResourcesSnapshot(this, globalResourcesInterface); } QList KisFilterConfiguration::linkedResources(KisResourcesInterfaceSP globalResourcesInterface) const { Q_UNUSED(globalResourcesInterface); return {}; } void KisFilterConfiguration::setCurves(QList< KisCubicCurve >& curves) { d->curves = curves; } bool KisFilterConfiguration::isCompatible(const KisPaintDeviceSP) const { return true; } QBitArray KisFilterConfiguration::channelFlags() const { return d->channelFlags; } void KisFilterConfiguration::setChannelFlags(QBitArray channelFlags) { d->channelFlags = channelFlags; } #ifdef SANITY_CHECK_FILTER_CONFIGURATION_OWNER int KisFilterConfiguration::sanityRefUsageCounter() { return d->sanityUsageCounter.ref(); } int KisFilterConfiguration::sanityDerefUsageCounter() { return d->sanityUsageCounter.deref(); } #endif /* SANITY_CHECK_FILTER_CONFIGURATION_OWNER */ diff --git a/libs/image/filter/kis_filter_configuration.h b/libs/image/filter/kis_filter_configuration.h index ffe085389c..4388390466 100644 --- a/libs/image/filter/kis_filter_configuration.h +++ b/libs/image/filter/kis_filter_configuration.h @@ -1,205 +1,186 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_FILTER_CONFIGURATION_H_ #define _KIS_FILTER_CONFIGURATION_H_ #include #include "kis_properties_configuration.h" #include "kis_types.h" #include "kritaimage_export.h" + class KoResource; typedef QSharedPointer KoResourceSP; class KisResourcesInterface; typedef QSharedPointer KisResourcesInterfaceSP; /** * KisFilterConfiguration does inherit neither KisShared or QSharedData * so sometimes there might be problem with broken QSharedPointer counters. * This macro activates debugging routines for such stuff. * * In the future, please either port the entire KisNodeFilterInterface * into KisFilterConfigurationSP or derive filter configuration * interface from QSharedData to handle these cases. */ #define SANITY_CHECK_FILTER_CONFIGURATION_OWNER /** * A KisFilterConfiguration is the serializable representation of * the filter parameters. Filters can subclass this class to implement * direct accessors to properties, but properties not in the map will * not be serialized. * * XXX: Use KoProperties here! */ class KRITAIMAGE_EXPORT KisFilterConfiguration : public KisPropertiesConfiguration { - public: /** * Create a new filter config. */ KisFilterConfiguration(const QString & name, qint32 version, KisResourcesInterfaceSP resourcesInterface); /** * @return an exact copy of the filter configuration. Resources interface is * becomes shared between two configuration objects. */ virtual KisFilterConfigurationSP clone() const; - /** - * @brief creates an exact copy of the filter config object and loads - * all the linked resources into the local storage. - * @param globalResourcesInterface is an optional override for the - * resources interface used for fetching linked resources. If - * \p globalResourcesInterface is null, then this->resourcesInterface() - * is used. - * - * If a filter configuration object already has a resources snapshot, then - * the function just clones the object without reloading anything. - */ - KisFilterConfigurationSP cloneWithResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface = nullptr) const; - protected: /** * Deep copy the filter configFile */ KisFilterConfiguration(const KisFilterConfiguration & rhs); public: ~KisFilterConfiguration() override; public: /** * This function is use to convert from legacy XML as used in .kra file. */ virtual void fromLegacyXML(const QDomElement&); using KisPropertiesConfiguration::fromXML; using KisPropertiesConfiguration::toXML; void fromXML(const QDomElement&) override; void toXML(QDomDocument&, QDomElement&) const override; /** * Get the unique, language independent name of the filter. */ const QString & name() const; /** * Get the version of the filter that has created this config */ qint32 version() const; /** * Check if that configuration is compatible with this paint device. * The default implementation always return true. */ virtual bool isCompatible(const KisPaintDeviceSP) const; /** * @return an array with each colorspace channel a true/false bit * that indicates whether the channel should be filtered or left * alone. It is up to the filter to decide whether channels that * are to be left alone are copied to the dest file or not. */ QBitArray channelFlags() const; /** * Set the channel flags. An empty array is allowed; that means * that all channels are to be filtered. Filters can optimize on * that. The array must be in the order of the pixel layout. */ void setChannelFlags(QBitArray channelFlags); /** * These functions exist solely to allow plugins to reimplement them as * needed, while allowing consumers to implement support for them without * linking directly to the plugin. In particular, the filter management * in Sketch requires this. */ virtual void setCurve(const KisCubicCurve &curve); virtual const KisCubicCurve& curve() const; virtual void setCurves(QList &curves); virtual const QList& curves() const; /** * @return resource interface that is used by KisFilterConfiguration object for * loading linked resources */ KisResourcesInterfaceSP resourcesInterface() const; /** * Set resource interface that will be used by KisFilterConfiguration object for * loading linked resources */ void setResourcesInterface(KisResourcesInterfaceSP resourcesInterface); /** - * Loads all the linked resources either from the current resource interface - * or from the embedded data. The filter first tries to fetch the linked - * resource from the current source, and only if it fails, tries to load - * it from the embedded data. - * - * @param globalResourcesInterface if \p globalResourcesInterface is not null, - * the resources are fetched from there, not from the internally stored resources - * interface + * \see KisLinkedResourcesOperators::createLocalResourcesSnapshot */ void createLocalResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface = nullptr); /** - * @return true if the configuration has all the necessary resources in - * local storage. It mean it can be used in a threaded environment. - * - * @see createLocalResourcesSnapshot() + * \see KisLinkedResourcesOperators::hasLocalResourcesSnapshot */ bool hasLocalResourcesSnapshot() const; + /** + * \see KisLinkedResourcesOperators::cloneWithResourcesSnapshot + */ + KisFilterConfigurationSP cloneWithResourcesSnapshot(KisResourcesInterfaceSP globalResourcesInterface = nullptr) const; + /** * Loads all the linked resources either from \p globalResourcesInterface or * from embedded data. The filter first tries to fetch the linked resource * from the global source, and only if it fails, tries to load it from the * embedded data. One can check if the loaded resource is embedded by checking * its resourceId(). */ virtual QList linkedResources(KisResourcesInterfaceSP globalResourcesInterface) const; #ifdef SANITY_CHECK_FILTER_CONFIGURATION_OWNER private: friend class KisNodeFilterInterface; int sanityRefUsageCounter(); int sanityDerefUsageCounter(); #endif /* SANITY_CHECK_FILTER_CONFIGURATION_OWNER */ protected: void setVersion(qint32 version); private: struct Private; Private* const d; }; Q_DECLARE_METATYPE(KisFilterConfiguration*) - #endif // _KIS_FILTER_CONFIGURATION_H_ diff --git a/libs/resources/CMakeLists.txt b/libs/resources/CMakeLists.txt index 840f4a55d6..c082546749 100644 --- a/libs/resources/CMakeLists.txt +++ b/libs/resources/CMakeLists.txt @@ -1,73 +1,74 @@ include_directories(${QUAZIP_INCLUDE_DIRS}) add_subdirectory(tests) set(kritaresources_LIB_SRCS KisResourceCacheDb.cpp KisResourceLoader.cpp KisResourceLoaderRegistry.cpp KisResourceLocator.cpp KisResourceStorage.cpp KisResourceModel.cpp KisTagFilterResourceProxyModel.cpp KisResourceModelProvider.cpp KisResourceTypeModel.cpp KisStorageModel.cpp KisStorageFilterProxyModel.cpp KisResourceIterator.cpp KisResourceSearchBoxFilter.cpp KisStoragePlugin.cpp KisBundleStorage.cpp KisFolderStorage.cpp KisMemoryStorage.cpp KisTag.cpp KisTagModel.cpp KisTagModelProvider.cpp KisActiveFilterTagProxyModel.cpp KoResource.cpp KoResourceBundle.cpp KoResourceBundleManifest.cpp KoMD5Generator.cpp KoResourcePaths.cpp ResourceDebug.cpp kconfigini.cpp kconfigdata.cpp kconfigbackend.cpp + KisLinkedResourcesOperators.cpp KisResourcesInterface.cpp KisLocalStrokeResources.cpp KisGlobalResourcesInterface.cpp ) qt5_add_resources(kritaresources_LIB_SRCS sql.qrc) add_library(kritaresources SHARED ${kritaresources_LIB_SRCS}) generate_export_header(kritaresources BASE_NAME kritaresources) target_link_libraries(kritaresources PUBLIC Qt5::Core Qt5::Widgets PRIVATE Qt5::Sql kritaversion kritaglobal kritaplugin kritastore KF5::ConfigCore KF5::CoreAddons KF5::I18n ${QUAZIP_LIBRARIES} ) set_target_properties(kritaresources PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaresources ${INSTALL_TARGETS_DEFAULT_ARGS} ) diff --git a/libs/resources/KisLinkedResourcesOperators.cpp b/libs/resources/KisLinkedResourcesOperators.cpp new file mode 100644 index 0000000000..be41170ce1 --- /dev/null +++ b/libs/resources/KisLinkedResourcesOperators.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "KisLinkedResourcesOperators.h" + +#include +#include +#include + + +bool KisLinkedResourcesOperators::detail::isLocalResourcesStorage(KisResourcesInterfaceSP resourcesInterface) +{ + return resourcesInterface.dynamicCast(); +} + +void KisLinkedResourcesOperators::detail::assertInGuiThread() +{ + KIS_SAFE_ASSERT_RECOVER_RETURN(QThread::currentThread() == qApp->thread()); +} + +KisResourcesInterfaceSP KisLinkedResourcesOperators::detail::createLocalResourcesStorage(const QList &resources) +{ + return QSharedPointer::create(resources); +} diff --git a/libs/resources/KisLinkedResourcesOperators.h b/libs/resources/KisLinkedResourcesOperators.h new file mode 100644 index 0000000000..4b2a06b09a --- /dev/null +++ b/libs/resources/KisLinkedResourcesOperators.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2020 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef KISLINKEDRESOURCESOPERATORS_H +#define KISLINKEDRESOURCESOPERATORS_H + +#include "kritaresources_export.h" + +#include +#include "kis_assert.h" + +namespace KisLinkedResourcesOperators +{ + +namespace detail { +bool KRITARESOURCES_EXPORT isLocalResourcesStorage(KisResourcesInterfaceSP resourcesInterface); +void KRITARESOURCES_EXPORT assertInGuiThread(); +KisResourcesInterfaceSP KRITARESOURCES_EXPORT createLocalResourcesStorage(const QList &resources); +} + +template +struct ResourceTraits +{ +}; + +/** + * @return true if the configuration has all the necessary resources in + * local storage. It mean it can be used in a threaded environment. + * + * @see createLocalResourcesSnapshot() + */ +template +bool hasLocalResourcesSnapshot(const T *object) +{ + return detail::isLocalResourcesStorage(object->resourcesInterface()); +} + +/** + * Loads all the linked resources either from the current resource interface + * or from the embedded data. The object first tries to fetch the linked + * resource from the current source, and only if it fails, tries to load + * it from the embedded data. + * + * @param globalResourcesInterface if \p globalResourcesInterface is not null, + * the resources are fetched from there, not from the internally stored resources + * interface + */ +template +void createLocalResourcesSnapshot(T *object, KisResourcesInterfaceSP globalResourcesInterface = nullptr) +{ + detail::assertInGuiThread(); + QList resources = + object->linkedResources(globalResourcesInterface ? + globalResourcesInterface : + object->resourcesInterface()); + object->setResourcesInterface(detail::createLocalResourcesStorage(resources)); +} + +/** + * @brief creates an exact copy of the object and loads all the linked + * resources into the local storage. + * @param globalResourcesInterface is an optional override for the + * resources interface used for fetching linked resources. If + * \p globalResourcesInterface is null, then object->resourcesInterface() + * is used. + * + * If a filter configuration object already has a resources snapshot, then + * the function just clones the object without reloading anything. + */ +template ::template SharedPointerType> +TypeSP cloneWithResourcesSnapshot(const T* object, + KisResourcesInterfaceSP globalResourcesInterface = nullptr) +{ + auto clonedStorage = object->clone(); + TypeSP cloned = ResourceTraits::template dynamicCastSP(clonedStorage); + + if (!hasLocalResourcesSnapshot(cloned.data())) { + createLocalResourcesSnapshot(cloned.data(), globalResourcesInterface); + KIS_SAFE_ASSERT_RECOVER_NOOP(hasLocalResourcesSnapshot(cloned.data())); + } + + return cloned; +} + +} + +#endif // KISLINKEDRESOURCESOPERATORS_H diff --git a/libs/ui/tool/kis_figure_painting_tool_helper.h b/libs/ui/tool/kis_figure_painting_tool_helper.h index 8edd3d1377..5c5348a64e 100644 --- a/libs/ui/tool/kis_figure_painting_tool_helper.h +++ b/libs/ui/tool/kis_figure_painting_tool_helper.h @@ -1,69 +1,67 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_FIGURE_PAINTING_TOOL_HELPER_H #define __KIS_FIGURE_PAINTING_TOOL_HELPER_H #include "kis_types.h" #include "kritaui_export.h" #include #include "strokes/freehand_stroke.h" #include "KisToolShapeUtils.h" class KoCanvasResourceProvider; class KisStrokesFacade; class KRITAUI_EXPORT KisFigurePaintingToolHelper { public: KisFigurePaintingToolHelper(const KUndo2MagicString &name, KisImageWSP image, KisNodeSP currentNode, KoCanvasResourceProvider *resourceManager, KisToolShapeUtils::StrokeStyle strokeStyle, KisToolShapeUtils::FillStyle fillStyle); ~KisFigurePaintingToolHelper(); void paintLine(const KisPaintInformation &pi0, const KisPaintInformation &pi1); void paintPolyline(const vQPointF &points); void paintPolygon(const vQPointF &points); void paintRect(const QRectF &rect); void paintEllipse(const QRectF &rect); void paintPainterPath(const QPainterPath &path); void setFGColorOverride(const KoColor &color); void setBGColorOverride(const KoColor &color); void setSelectionOverride(KisSelectionSP m_selection); - // WARNING: setBrush() should be called with a cloned copy of the brush! The - // helper (reosurce snapshot) will own and modify it! void setBrush(const KisPaintOpPresetSP &brush); void paintPainterPathQPen(const QPainterPath, const QPen &pen, const KoColor &color); void paintPainterPathQPenFill(const QPainterPath, const QPen &pen, const KoColor &color); private: void setupPaintStyles(KisResourcesSnapshotSP resources, KisToolShapeUtils::StrokeStyle strokeStyle, KisToolShapeUtils::FillStyle fillStyle); private: KisStrokeId m_strokeId; KisResourcesSnapshotSP m_resources; KisStrokesFacade *m_strokesFacade; }; #endif /* __KIS_FIGURE_PAINTING_TOOL_HELPER_H */ diff --git a/libs/ui/tool/kis_resources_snapshot.cpp b/libs/ui/tool/kis_resources_snapshot.cpp index 31e7b35b80..6b5bf202fb 100644 --- a/libs/ui/tool/kis_resources_snapshot.cpp +++ b/libs/ui/tool/kis_resources_snapshot.cpp @@ -1,433 +1,427 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_resources_snapshot.h" #include #include #include #include #include #include #include #include #include "kis_canvas_resource_provider.h" #include "filter/kis_filter_configuration.h" #include "kis_image.h" #include "kis_paint_device.h" #include "kis_paint_layer.h" #include "kis_selection.h" #include "kis_selection_mask.h" #include "kis_algebra_2d.h" -#include struct KisResourcesSnapshot::Private { Private() : currentPattern(0) , currentGradient(0) , currentGenerator(0) , compositeOp(0) { } KisImageSP image; KisDefaultBoundsBaseSP bounds; KoColor currentFgColor; KoColor currentBgColor; KoPatternSP currentPattern; KoAbstractGradientSP currentGradient; KisPaintOpPresetSP currentPaintOpPreset; KisNodeSP currentNode; qreal currentExposure; KisFilterConfigurationSP currentGenerator; QPointF axesCenter; bool mirrorMaskHorizontal = false; bool mirrorMaskVertical = false; quint8 opacity = OPACITY_OPAQUE_U8; QString compositeOpId = COMPOSITE_OVER; const KoCompositeOp *compositeOp; KisPainter::StrokeStyle strokeStyle = KisPainter::StrokeStyleBrush; KisPainter::FillStyle fillStyle = KisPainter::FillStyleForegroundColor; bool globalAlphaLock = false; qreal effectiveZoom = 1.0; bool presetAllowsLod = false; KisSelectionSP selectionOverride; bool hasOverrideSelection = false; }; KisResourcesSnapshot::KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KoCanvasResourceProvider *resourceManager, KisDefaultBoundsBaseSP bounds) : m_d(new Private()) { m_d->image = image; if (!bounds) { bounds = new KisDefaultBounds(m_d->image); } m_d->bounds = bounds; m_d->currentFgColor = resourceManager->resource(KoCanvasResourceProvider::ForegroundColor).value(); m_d->currentBgColor = resourceManager->resource(KoCanvasResourceProvider::BackgroundColor).value(); m_d->currentPattern = resourceManager->resource(KisCanvasResourceProvider::CurrentPattern).value(); m_d->currentGradient = resourceManager->resource(KisCanvasResourceProvider::CurrentGradient).value(); /** * We should deep-copy the preset, so that long-running actions * will have correct brush parameters. Theoretically this cloning * can be expensive, but according to measurements, it takes * something like 0.1 ms for an average preset. */ KisPaintOpPresetSP p = resourceManager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); if (p) { - m_d->currentPaintOpPreset = resourceManager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value()->clone().dynamicCast(); - m_d->currentPaintOpPreset->createLocalResourcesSnapshot(KisGlobalResourcesInterface::instance()); + m_d->currentPaintOpPreset = resourceManager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value()->cloneWithResourcesSnapshot(); } #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ m_d->currentExposure = resourceManager->resource(KisCanvasResourceProvider::HdrExposure).toDouble(); m_d->currentGenerator = resourceManager->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value(); if (m_d->currentGenerator) { m_d->currentGenerator = m_d->currentGenerator->cloneWithResourcesSnapshot(); } QPointF relativeAxesCenter(0.5, 0.5); if (m_d->image) { relativeAxesCenter = m_d->image->mirrorAxesCenter(); } m_d->axesCenter = KisAlgebra2D::relativeToAbsolute(relativeAxesCenter, m_d->bounds->bounds()); m_d->mirrorMaskHorizontal = resourceManager->resource(KisCanvasResourceProvider::MirrorHorizontal).toBool(); m_d->mirrorMaskVertical = resourceManager->resource(KisCanvasResourceProvider::MirrorVertical).toBool(); qreal normOpacity = resourceManager->resource(KisCanvasResourceProvider::Opacity).toDouble(); m_d->opacity = quint8(normOpacity * OPACITY_OPAQUE_U8); m_d->compositeOpId = resourceManager->resource(KisCanvasResourceProvider::CurrentEffectiveCompositeOp).toString(); setCurrentNode(currentNode); /** * Fill and Stroke styles are not a part of the resource manager * so the tools should set them manually * TODO: port stroke and fill styles to be a part * of the resource manager */ m_d->strokeStyle = KisPainter::StrokeStyleBrush; m_d->fillStyle = KisPainter::FillStyleNone; m_d->globalAlphaLock = resourceManager->resource(KisCanvasResourceProvider::GlobalAlphaLock).toBool(); m_d->effectiveZoom = resourceManager->resource(KisCanvasResourceProvider::EffectiveZoom).toDouble(); m_d->presetAllowsLod = resourceManager->resource(KisCanvasResourceProvider::EffectiveLodAvailablility).toBool(); } KisResourcesSnapshot::KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KisDefaultBoundsBaseSP bounds) : m_d(new Private()) { m_d->image = image; if (!bounds) { bounds = new KisDefaultBounds(m_d->image); } m_d->bounds = bounds; #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ QPointF relativeAxesCenter(0.5, 0.5); if (m_d->image) { relativeAxesCenter = m_d->image->mirrorAxesCenter(); } m_d->axesCenter = KisAlgebra2D::relativeToAbsolute(relativeAxesCenter, m_d->bounds->bounds()); m_d->opacity = OPACITY_OPAQUE_U8; setCurrentNode(currentNode); /** * Fill and Stroke styles are not a part of the resource manager * so the tools should set them manually * TODO: port stroke and fill styles to be a part * of the resource manager */ m_d->strokeStyle = KisPainter::StrokeStyleBrush; m_d->fillStyle = KisPainter::FillStyleNone; } KisResourcesSnapshot::~KisResourcesSnapshot() { delete m_d; } void KisResourcesSnapshot::setupPainter(KisPainter* painter) { painter->setPaintColor(m_d->currentFgColor); painter->setBackgroundColor(m_d->currentBgColor); painter->setGenerator(m_d->currentGenerator); painter->setPattern(m_d->currentPattern); painter->setGradient(m_d->currentGradient); QBitArray lockflags = channelLockFlags(); if (lockflags.size() > 0) { painter->setChannelFlags(lockflags); } painter->setOpacity(m_d->opacity); painter->setCompositeOp(m_d->compositeOp); painter->setMirrorInformation(m_d->axesCenter, m_d->mirrorMaskHorizontal, m_d->mirrorMaskVertical); painter->setStrokeStyle(m_d->strokeStyle); painter->setFillStyle(m_d->fillStyle); /** * The paintOp should be initialized the last, because it may * ask the painter for some options while initialization */ painter->setPaintOpPreset(m_d->currentPaintOpPreset, m_d->currentNode, m_d->image); } void KisResourcesSnapshot::setupMaskingBrushPainter(KisPainter *painter) { KIS_SAFE_ASSERT_RECOVER_RETURN(painter->device()); KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->currentPaintOpPreset->hasMaskingPreset()); painter->setPaintColor(KoColor(Qt::white, painter->device()->colorSpace())); painter->setBackgroundColor(KoColor(Qt::black, painter->device()->colorSpace())); painter->setOpacity(OPACITY_OPAQUE_U8); painter->setChannelFlags(QBitArray()); // masked brush always paints in indirect mode painter->setCompositeOp(COMPOSITE_ALPHA_DARKEN); painter->setMirrorInformation(m_d->axesCenter, m_d->mirrorMaskHorizontal, m_d->mirrorMaskVertical); painter->setStrokeStyle(m_d->strokeStyle); /** * The paintOp should be initialized the last, because it may * ask the painter for some options while initialization */ painter->setPaintOpPreset(m_d->currentPaintOpPreset->createMaskingPreset(), m_d->currentNode, m_d->image); } KisPostExecutionUndoAdapter* KisResourcesSnapshot::postExecutionUndoAdapter() const { return m_d->image ? m_d->image->postExecutionUndoAdapter() : 0; } void KisResourcesSnapshot::setCurrentNode(KisNodeSP node) { m_d->currentNode = node; KisPaintDeviceSP device; if(m_d->currentNode && (device = m_d->currentNode->paintDevice())) { m_d->compositeOp = device->colorSpace()->compositeOp(m_d->compositeOpId); if(!m_d->compositeOp) { m_d->compositeOp = device->colorSpace()->compositeOp(COMPOSITE_OVER); } } } void KisResourcesSnapshot::setStrokeStyle(KisPainter::StrokeStyle strokeStyle) { m_d->strokeStyle = strokeStyle; } void KisResourcesSnapshot::setFillStyle(KisPainter::FillStyle fillStyle) { m_d->fillStyle = fillStyle; } KisNodeSP KisResourcesSnapshot::currentNode() const { return m_d->currentNode; } KisImageSP KisResourcesSnapshot::image() const { return m_d->image; } bool KisResourcesSnapshot::needsIndirectPainting() const { return !m_d->currentPaintOpPreset->settings()->paintIncremental(); } QString KisResourcesSnapshot::indirectPaintingCompositeOp() const { return m_d->currentPaintOpPreset ? m_d->currentPaintOpPreset->settings()->indirectPaintingCompositeOp() : COMPOSITE_ALPHA_DARKEN; } bool KisResourcesSnapshot::needsMaskingBrushRendering() const { return m_d->currentPaintOpPreset && m_d->currentPaintOpPreset->hasMaskingPreset(); } KisSelectionSP KisResourcesSnapshot::activeSelection() const { /** * It is possible to have/use the snapshot without the image. Such * usecase is present for example in the scratchpad. */ if (m_d->hasOverrideSelection) { return m_d->selectionOverride; } KisSelectionSP selection = m_d->image ? m_d->image->globalSelection() : 0; KisLayerSP layer = qobject_cast(m_d->currentNode.data()); KisSelectionMaskSP mask; if((layer = qobject_cast(m_d->currentNode.data()))) { selection = layer->selection(); } else if ((mask = dynamic_cast(m_d->currentNode.data())) && mask->selection() == selection) { selection = 0; } return selection; } bool KisResourcesSnapshot::needsAirbrushing() const { return m_d->currentPaintOpPreset->settings()->isAirbrushing(); } qreal KisResourcesSnapshot::airbrushingInterval() const { return m_d->currentPaintOpPreset->settings()->airbrushInterval(); } bool KisResourcesSnapshot::needsSpacingUpdates() const { return m_d->currentPaintOpPreset->settings()->useSpacingUpdates(); } void KisResourcesSnapshot::setOpacity(qreal opacity) { m_d->opacity = opacity * OPACITY_OPAQUE_U8; } quint8 KisResourcesSnapshot::opacity() const { return m_d->opacity; } const KoCompositeOp* KisResourcesSnapshot::compositeOp() const { return m_d->compositeOp; } QString KisResourcesSnapshot::compositeOpId() const { return m_d->compositeOpId; } KoPatternSP KisResourcesSnapshot::currentPattern() const { return m_d->currentPattern; } KoColor KisResourcesSnapshot::currentFgColor() const { return m_d->currentFgColor; } KoColor KisResourcesSnapshot::currentBgColor() const { return m_d->currentBgColor; } KisPaintOpPresetSP KisResourcesSnapshot::currentPaintOpPreset() const { return m_d->currentPaintOpPreset; } QBitArray KisResourcesSnapshot::channelLockFlags() const { QBitArray channelFlags; KisPaintLayer *paintLayer; if ((paintLayer = dynamic_cast(m_d->currentNode.data()))) { channelFlags = paintLayer->channelLockFlags(); if (m_d->globalAlphaLock) { if (channelFlags.isEmpty()) { channelFlags = paintLayer->colorSpace()->channelFlags(true, true); } channelFlags &= paintLayer->colorSpace()->channelFlags(true, false); } } return channelFlags; } qreal KisResourcesSnapshot::effectiveZoom() const { return m_d->effectiveZoom; } bool KisResourcesSnapshot::presetAllowsLod() const { return m_d->presetAllowsLod; } bool KisResourcesSnapshot::presetNeedsAsynchronousUpdates() const { return m_d->currentPaintOpPreset && m_d->currentPaintOpPreset->settings()->needsAsynchronousUpdates(); } void KisResourcesSnapshot::setFGColorOverride(const KoColor &color) { m_d->currentFgColor = color; } void KisResourcesSnapshot::setBGColorOverride(const KoColor &color) { m_d->currentBgColor = color; } void KisResourcesSnapshot::setSelectionOverride(KisSelectionSP selection) { m_d->selectionOverride = selection; m_d->hasOverrideSelection = true; // needed if selection passed is null to ignore selection } void KisResourcesSnapshot::setBrush(const KisPaintOpPresetSP &brush) { - m_d->currentPaintOpPreset = brush; + m_d->currentPaintOpPreset = brush->cloneWithResourcesSnapshot(); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ - - if (m_d->currentPaintOpPreset) { - m_d->currentPaintOpPreset->createLocalResourcesSnapshot(KisGlobalResourcesInterface::instance()); - } }