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

0001 /*
0002  *  SPDX-FileCopyrightText: 2008 Boudewijn Rempt <boud@valdyas.org>
0003  *  SPDX-FileCopyrightText: 2020 L. E. Segovia <amy@amyspark.me>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include <QMutex>
0009 #include <QMutexLocker>
0010 
0011 #include "kis_generator_layer.h"
0012 
0013 #include <klocalizedstring.h>
0014 #include "kis_debug.h"
0015 
0016 #include <KoIcon.h>
0017 #include <kis_icon.h>
0018 #include "kis_selection.h"
0019 #include "filter/kis_filter_configuration.h"
0020 #include "kis_processing_information.h"
0021 #include <kis_processing_visitor.h>
0022 #include "generator/kis_generator_registry.h"
0023 #include "generator/kis_generator.h"
0024 #include "kis_node_visitor.h"
0025 #include "kis_thread_safe_signal_compressor.h"
0026 #include <kis_generator_stroke_strategy.h>
0027 #include <KisRunnableStrokeJobData.h>
0028 
0029 
0030 #define UPDATE_DELAY 100 /*ms */
0031 
0032 struct Q_DECL_HIDDEN KisGeneratorLayer::Private
0033 {
0034     Private()
0035         : updateSignalCompressor(UPDATE_DELAY, KisSignalCompressor::FIRST_INACTIVE)
0036     {
0037     }
0038 
0039     KisThreadSafeSignalCompressor updateSignalCompressor;
0040     QRect preparedRect;
0041     QRect preparedImageBounds;
0042     KisFilterConfigurationSP preparedForFilter;
0043     QWeakPointer<boost::none_t> updateCookie;
0044     QMutex mutex;
0045 };
0046 
0047 
0048 KisGeneratorLayer::KisGeneratorLayer(KisImageWSP image,
0049                                      const QString &name,
0050                                      KisFilterConfigurationSP kfc,
0051                                      KisSelectionSP selection)
0052     : KisSelectionBasedLayer(image, name, selection, kfc),
0053       m_d(new Private)
0054 {
0055     connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate()));
0056 }
0057 
0058 KisGeneratorLayer::KisGeneratorLayer(const KisGeneratorLayer& rhs)
0059     : KisSelectionBasedLayer(rhs),
0060       m_d(new Private)
0061 {
0062     connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDelayedStaticUpdate()));
0063 }
0064 
0065 KisGeneratorLayer::~KisGeneratorLayer()
0066 {
0067 }
0068 
0069 void KisGeneratorLayer::setFilter(KisFilterConfigurationSP filterConfig, bool checkCompareConfig)
0070 {
0071     setFilterWithoutUpdate(filterConfig, checkCompareConfig);
0072     m_d->updateSignalCompressor.start();
0073 }
0074 
0075 void KisGeneratorLayer::setFilterWithoutUpdate(KisFilterConfigurationSP filterConfig, bool checkCompareConfig)
0076 {
0077     if (filter().isNull() || (!checkCompareConfig || !filter()->compareTo(filterConfig.constData()))) {
0078         KisSelectionBasedLayer::setFilter(filterConfig);
0079         {
0080             QMutexLocker locker(&m_d->mutex);
0081             m_d->preparedRect = QRect();
0082         }
0083     }
0084 }
0085 
0086 void KisGeneratorLayer::slotDelayedStaticUpdate()
0087 {
0088     /**
0089      * Don't try to start a regeneration stroke while image
0090      * is locked. It may happen on loading, when all necessary
0091      * conversions are not yet finished.
0092      */
0093     if (KisImageSP image = this->image(); image && image->locked()) {
0094         m_d->updateSignalCompressor.start();
0095         return;
0096     }
0097 
0098     /**
0099      * The mask might have been deleted from the layers stack in the
0100      * meanwhile. Just ignore the updates in the case.
0101      */
0102 
0103     KisLayerSP parentLayer(qobject_cast<KisLayer*>(parent().data()));
0104     if (!parentLayer) return;
0105 
0106     KisImageSP image = parentLayer->image();
0107 
0108     if (image) {
0109         if (!m_d->updateCookie) {
0110             this->update();
0111         } else {
0112             m_d->updateSignalCompressor.start();
0113         }
0114     }
0115 }
0116 
0117 void KisGeneratorLayer::requestUpdateJobsWithStroke(KisStrokeId strokeId, KisFilterConfigurationSP filterConfig)
0118 {
0119     QMutexLocker locker(&m_d->mutex);
0120     
0121     KisImageSP image = this->image().toStrongRef();
0122     const QRect updateRect = extent() | image->bounds();
0123 
0124     if (filterConfig != m_d->preparedForFilter) {
0125         locker.unlock();
0126         resetCacheWithoutUpdate();
0127         locker.relock();
0128     }
0129 
0130     if (m_d->preparedImageBounds != image->bounds()) {
0131         m_d->preparedRect = QRect();
0132     }
0133 
0134     const QRegion processRegion(QRegion(updateRect) - m_d->preparedRect);
0135     if (processRegion.isEmpty())
0136         return;
0137 
0138     KisGeneratorSP f = KisGeneratorRegistry::instance()->value(filterConfig->name());
0139     KIS_SAFE_ASSERT_RECOVER_RETURN(f);
0140 
0141     KisPaintDeviceSP originalDevice = original();
0142 
0143     QSharedPointer<boost::none_t> cookie(new boost::none_t(boost::none));
0144 
0145     auto jobs = KisGeneratorStrokeStrategy::createJobsData(this, cookie, f, originalDevice, processRegion, filterConfig);
0146 
0147     Q_FOREACH (auto job, jobs) {
0148         image->addJob(strokeId, job);
0149     }
0150 
0151     m_d->updateCookie = cookie;
0152     m_d->preparedRect = updateRect;
0153     m_d->preparedImageBounds = image->bounds();
0154     m_d->preparedForFilter = filterConfig;
0155 }
0156 
0157 QWeakPointer<boost::none_t> KisGeneratorLayer::previewWithStroke(const KisStrokeId strokeId)
0158 {
0159     KisFilterConfigurationSP filterConfig = filter();
0160     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(filterConfig, QWeakPointer<boost::none_t>());
0161 
0162     requestUpdateJobsWithStroke(strokeId, filterConfig);
0163     return m_d->updateCookie;
0164 }
0165 
0166 void KisGeneratorLayer::update()
0167 {
0168     KisImageSP image = this->image().toStrongRef();
0169 
0170     KisFilterConfigurationSP filterConfig = filter();
0171     KIS_SAFE_ASSERT_RECOVER_RETURN(filterConfig);
0172 
0173     KisGeneratorStrokeStrategy *stroke = new KisGeneratorStrokeStrategy();
0174 
0175     KisStrokeId strokeId = image->startStroke(stroke);
0176 
0177     requestUpdateJobsWithStroke(strokeId, filterConfig);
0178 
0179     image->endStroke(strokeId);
0180 }
0181 
0182 bool KisGeneratorLayer::accept(KisNodeVisitor & v)
0183 {
0184     return v.visit(this);
0185 }
0186 
0187 void KisGeneratorLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
0188 {
0189     return visitor.visit(this, undoAdapter);
0190 }
0191 
0192 QIcon KisGeneratorLayer::icon() const
0193 {
0194     return KisIconUtils::loadIcon("fillLayer");
0195 }
0196 
0197 KisBaseNode::PropertyList KisGeneratorLayer::sectionModelProperties() const
0198 {
0199     KisFilterConfigurationSP filterConfig = filter();
0200 
0201     KisBaseNode::PropertyList l = KisLayer::sectionModelProperties();
0202     l << KisBaseNode::Property(KoID("generator", i18n("Generator")),
0203                                KisGeneratorRegistry::instance()->value(filterConfig->name())->name());
0204 
0205     return l;
0206 }
0207 
0208 void KisGeneratorLayer::setX(qint32 x)
0209 {
0210     KisSelectionBasedLayer::setX(x);
0211     {
0212         QMutexLocker locker(&m_d->mutex);
0213         m_d->preparedRect = QRect();
0214     }
0215     m_d->updateSignalCompressor.start();
0216 }
0217 
0218 void KisGeneratorLayer::setY(qint32 y)
0219 {
0220     KisSelectionBasedLayer::setY(y);
0221     {
0222         QMutexLocker locker(&m_d->mutex);
0223         m_d->preparedRect = QRect();
0224     }
0225     m_d->updateSignalCompressor.start();
0226 }
0227 
0228 void KisGeneratorLayer::resetCache()
0229 {
0230     resetCacheWithoutUpdate();
0231     m_d->updateSignalCompressor.start();
0232 }
0233 
0234 void KisGeneratorLayer::forceUpdateTimedNode()
0235 {
0236     if (hasPendingTimedUpdates()) {
0237         m_d->updateSignalCompressor.stop();
0238         m_d->updateCookie.clear();
0239 
0240         slotDelayedStaticUpdate();
0241     }
0242 }
0243 
0244 bool KisGeneratorLayer::hasPendingTimedUpdates() const
0245 {
0246     return m_d->updateSignalCompressor.isActive();
0247 }
0248 
0249 void KisGeneratorLayer::resetCacheWithoutUpdate()
0250 {
0251     KisSelectionBasedLayer::resetCache();
0252     {
0253         QMutexLocker locker(&m_d->mutex);
0254         m_d->preparedRect = QRect();
0255     }
0256 }
0257 
0258 void KisGeneratorLayer::setDirty(const QVector<QRect> &rects)
0259 {
0260     setDirtyWithoutUpdate(rects);
0261     m_d->updateSignalCompressor.start();
0262 }
0263 
0264 void KisGeneratorLayer::setDirtyWithoutUpdate(const QVector<QRect> &rects)
0265 {
0266     KisSelectionBasedLayer::setDirty(rects);
0267 }