diff --git a/libs/image/generator/kis_generator_layer.cpp b/libs/image/generator/kis_generator_layer.cpp index bd8a3bc2cd..a83515c885 100644 --- a/libs/image/generator/kis_generator_layer.cpp +++ b/libs/image/generator/kis_generator_layer.cpp @@ -1,167 +1,190 @@ /* * Copyright (c) 2008 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 "kis_generator_layer.h" #include #include "kis_debug.h" #include #include #include "kis_selection.h" #include "filter/kis_filter_configuration.h" #include "kis_processing_information.h" #include "generator/kis_generator_registry.h" #include "generator/kis_generator.h" #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_thread_safe_signal_compressor.h" #include "kis_recalculate_generator_layer_job.h" #define UPDATE_DELAY 100 /*ms */ struct Q_DECL_HIDDEN KisGeneratorLayer::Private { Private() : updateSignalCompressor(UPDATE_DELAY, KisSignalCompressor::FIRST_INACTIVE) { } KisThreadSafeSignalCompressor updateSignalCompressor; + QRect preparedRect; + KisFilterConfigurationSP preparedForFilter; }; KisGeneratorLayer::KisGeneratorLayer(KisImageWSP image, const QString &name, KisFilterConfigurationSP kfc, KisSelectionSP selection) : KisSelectionBasedLayer(image, name, selection, kfc, true), m_d(new Private) { connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate())); update(); } KisGeneratorLayer::KisGeneratorLayer(const KisGeneratorLayer& rhs) : KisSelectionBasedLayer(rhs), m_d(new Private) { connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate())); } KisGeneratorLayer::~KisGeneratorLayer() { } void KisGeneratorLayer::setFilter(KisFilterConfigurationSP filterConfig) { KisSelectionBasedLayer::setFilter(filterConfig); + m_d->preparedRect = QRect(); update(); } void KisGeneratorLayer::slotDelayedStaticUpdate() { /** * The mask might have been deleted from the layers stack in the * meanwhile. Just ignore the updates in the case. */ KisLayerSP parentLayer(qobject_cast(parent().data())); if (!parentLayer) return; KisImageSP image = parentLayer->image(); if (image) { image->addSpontaneousJob(new KisRecalculateGeneratorLayerJob(KisGeneratorLayerSP(this))); } } void KisGeneratorLayer::update() { + KisImageSP image = this->image().toStrongRef(); + const QRect updateRect = extent() | image->bounds(); + KisFilterConfigurationSP filterConfig = filter(); + KIS_SAFE_ASSERT_RECOVER_RETURN(filterConfig); - if (!filterConfig) { - warnImage << "BUG: No Filter configuration in KisGeneratorLayer"; - return; + if (filterConfig != m_d->preparedForFilter) { + resetCache(); } + const QRegion processRegion(QRegion(updateRect) - m_d->preparedRect); + if (processRegion.isEmpty()) return; + KisGeneratorSP f = KisGeneratorRegistry::instance()->value(filterConfig->name()); - if (!f) return; + KIS_SAFE_ASSERT_RECOVER_RETURN(f); - QRect processRect = exactBounds(); + KisPaintDeviceSP originalDevice = original(); - resetCache(); + QVector dirtyRegion; - KisPaintDeviceSP originalDevice = original(); + Q_FOREACH (const QRect &rc, processRegion) { + KisProcessingInformation dstCfg(originalDevice, + rc.topLeft(), + KisSelectionSP()); + + f->generate(dstCfg, rc.size(), filterConfig.data()); - KisProcessingInformation dstCfg(originalDevice, - processRect.topLeft(), - KisSelectionSP()); + dirtyRegion << rc; - f->generate(dstCfg, processRect.size(), filterConfig.data()); + } + m_d->preparedRect = updateRect; + m_d->preparedForFilter = filterConfig; // HACK ALERT!!! // this avoids cyclic loop with KisRecalculateGeneratorLayerJob::run() - KisSelectionBasedLayer::setDirty(QVector() << extent()); + KisSelectionBasedLayer::setDirty(dirtyRegion); } bool KisGeneratorLayer::accept(KisNodeVisitor & v) { return v.visit(this); } void KisGeneratorLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } QIcon KisGeneratorLayer::icon() const { return KisIconUtils::loadIcon("fillLayer"); } KisBaseNode::PropertyList KisGeneratorLayer::sectionModelProperties() const { KisFilterConfigurationSP filterConfig = filter(); KisBaseNode::PropertyList l = KisLayer::sectionModelProperties(); l << KisBaseNode::Property(KoID("generator", i18n("Generator")), KisGeneratorRegistry::instance()->value(filterConfig->name())->name()); return l; } void KisGeneratorLayer::setX(qint32 x) { KisSelectionBasedLayer::setX(x); + m_d->preparedRect = QRect(); m_d->updateSignalCompressor.start(); } void KisGeneratorLayer::setY(qint32 y) { KisSelectionBasedLayer::setY(y); + m_d->preparedRect = QRect(); + m_d->updateSignalCompressor.start(); +} + +void KisGeneratorLayer::resetCache() +{ + KisSelectionBasedLayer::resetCache(); + m_d->preparedRect = QRect(); m_d->updateSignalCompressor.start(); } void KisGeneratorLayer::setDirty(const QVector &rects) { KisSelectionBasedLayer::setDirty(rects); m_d->updateSignalCompressor.start(); } diff --git a/libs/image/generator/kis_generator_layer.h b/libs/image/generator/kis_generator_layer.h index 757b464d52..a0ed0545d8 100644 --- a/libs/image/generator/kis_generator_layer.h +++ b/libs/image/generator/kis_generator_layer.h @@ -1,90 +1,92 @@ /* * Copyright (c) 2008 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_GENERATOR_LAYER_H_ #define KIS_GENERATOR_LAYER_H_ #include "kis_selection_based_layer.h" #include #include class KisFilterConfiguration; /** * A generator layer is a special kind of layer that can be prefilled * with some pixel pattern generated by a KisGenerator plugin. * A KisGenerator is similar to a filter, but doesn't take * input pixel data and creates new pixel data. * * It is not possible to destructively paint on a generator layer. * * XXX: what about threadedness? */ class KRITAIMAGE_EXPORT KisGeneratorLayer : public KisSelectionBasedLayer { Q_OBJECT public: /** * Create a new Generator layer with the given configuration * and selection. Note that the selection will be _copied_ * (using COW, though). */ KisGeneratorLayer(KisImageWSP image, const QString &name, KisFilterConfigurationSP kfc, KisSelectionSP selection); KisGeneratorLayer(const KisGeneratorLayer& rhs); ~KisGeneratorLayer() override; KisNodeSP clone() const override { return KisNodeSP(new KisGeneratorLayer(*this)); } void setFilter(KisFilterConfigurationSP filterConfig) override; bool accept(KisNodeVisitor &) override; void accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) override; QIcon icon() const override; KisBaseNode::PropertyList sectionModelProperties() const override; /** * re-run the generator. This happens over the bounds * of the associated selection. */ void update(); using KisSelectionBasedLayer::setDirty; void setDirty(const QVector &rects) override; void setX(qint32 x) override; void setY(qint32 y) override; + void resetCache() override; + private Q_SLOTS: void slotDelayedStaticUpdate(); public: // KisIndirectPaintingSupport KisLayer* layer() { return this; } private: struct Private; const QScopedPointer m_d; }; #endif diff --git a/libs/image/kis_selection_based_layer.h b/libs/image/kis_selection_based_layer.h index aa8a9448d9..cc57a75074 100644 --- a/libs/image/kis_selection_based_layer.h +++ b/libs/image/kis_selection_based_layer.h @@ -1,210 +1,210 @@ /* * Copyright (c) 2006 Boudewijn Rempt * (c) 2009 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_SELECTION_BASED_LAYER_H_ #define KIS_SELECTION_BASED_LAYER_H_ #include #include "kis_types.h" #include "kis_layer.h" #include "kis_indirect_painting_support.h" #include #include "kis_node_filter_interface.h" class KisFilterConfiguration; /** * @class KisSelectionBasedLayer describes base behaviour for * selection base classes like KisAdjustmentLayer and KisGeneratorLayer. * These classes should have a persistent selection that controls * the area where filter/generators are applied. The area outside * this selection is not affected by the layer */ class KRITAIMAGE_EXPORT KisSelectionBasedLayer : public KisLayer, public KisIndirectPaintingSupport, public KisNodeFilterInterface { Q_OBJECT public: /** * creates a new layer with the given selection. * Note that the selection will be _copied_ (with COW, though). * @param image the image to set this layer to * @param name name of the layer * @param selection is a mask used by the layer to know * where to apply the filter/generator. */ KisSelectionBasedLayer(KisImageWSP image, const QString &name, KisSelectionSP selection, KisFilterConfigurationSP filterConfig, bool useGeneratorRegistry = false); KisSelectionBasedLayer(const KisSelectionBasedLayer& rhs); ~KisSelectionBasedLayer() override; /** * tells whether the @node can be a child of this layer * @param node to be connected node * @return tells if to be connected is a child of KisMask */ bool allowAsChild(KisNodeSP node) const override; void setImage(KisImageWSP image) override; KisPaintDeviceSP original() const override; KisPaintDeviceSP paintDevice() const override; bool needProjection() const override; /** * resets cached projection of lower layer to a new device * @return void */ - void resetCache(); + virtual void resetCache(); /** * for KisLayer::setDirty(const QRegion&) */ using KisLayer::setDirty; /** * Mark a layer as dirty. We can't use KisLayer's one * as our extent() function doesn't fit for this */ void setDirty() override; public: /** * Returns the selection of the layer * * Do not mix it with selection() which returns * the currently active selection of the image */ KisSelectionSP internalSelection() const; /** * sets the selection of this layer to a copy of * selection * @param selection the selection to set * @return void */ void setInternalSelection(KisSelectionSP selection); /** * When painted in indirect painting mode, the internal selection * might not contain actual selection, because a part of it is * stored on an indirect painting device. This method returns the * merged copy of the real selection. The area in \p rect only is * guaranteed to be prepared. The content of the rest of the * selection is undefined. */ KisSelectionSP fetchComposedInternalSelection(const QRect &rect) const; /** * gets this layer's x coordinate, taking selection into account * @return x-coordinate value */ qint32 x() const override; /** * gets this layer's y coordinate, taking selection into account * @return y-coordinate value */ qint32 y() const override; /** * sets this layer's y coordinate, taking selection into account * @param x x coordinate */ void setX(qint32 x) override; /** * sets this layer's y coordinate, taking selection into account * @param y y coordinate */ void setY(qint32 y) override; public: /** * gets an approximation of where the bounds on actual data * are in this layer, taking selection into account */ QRect extent() const override; /** * returns the exact bounds of where the actual data resides * in this layer, taking selection into account */ QRect exactBounds() const override; /** * copies the image and reformats it to thumbnail size * and returns the new thumbnail image. * @param w width of the thumbnail to create * @param h height of the thumbnail to create * @return the thumbnail image created. */ QImage createThumbnail(qint32 w, qint32 h) override; protected: // override from KisLayer void copyOriginalToProjection(const KisPaintDeviceSP original, KisPaintDeviceSP projection, const QRect& rect) const override; // override from KisNode QRect needRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const override; protected: void initSelection(); QRect cropChangeRectBySelection(const QRect &rect) const; /** * Sets if the selection should be used in * copyOriginalToProjection() method. * * Default value is 'true'. The descendants should override it to * get desired behaviour. * * Must be called only once in the child's constructor */ void setUseSelectionInProjection(bool value) const; KisKeyframeChannel *requestKeyframeChannel(const QString &id) override; public Q_SLOTS: void slotImageSizeChanged(); /** * gets this layer. Overriddes function in * KisIndirectPaintingSupport * @return this AdjustmentLayer */ KisLayer* layer() { return this; } private: struct Private; Private * const m_d; }; #endif /* KIS_SELECTION_BASED_LAYER_H_ */