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 }