File indexing completed on 2024-05-12 15:58:37
0001 /* 0002 * SPDX-FileCopyrightText: 2011 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_processing_applicator.h" 0008 0009 #include "kis_image.h" 0010 #include "kis_paint_layer.h" 0011 #include "kis_node.h" 0012 #include "kis_clone_layer.h" 0013 #include "kis_processing_visitor.h" 0014 #include "commands_new/kis_processing_command.h" 0015 #include "kis_stroke_strategy_undo_command_based.h" 0016 #include "kis_layer_utils.h" 0017 #include "kis_command_utils.h" 0018 #include "kis_time_span.h" 0019 #include "kis_node.h" 0020 #include "kis_image_signal_router.h" 0021 #include "KisAsynchronouslyMergeableCommandInterface.h" 0022 #include "kis_command_ids.h" 0023 0024 class DisableUIUpdatesCommand : public KisCommandUtils::FlipFlopCommand, public KisAsynchronouslyMergeableCommandInterface 0025 { 0026 public: 0027 DisableUIUpdatesCommand(KisImageWSP image, 0028 bool finalUpdate) 0029 : FlipFlopCommand(finalUpdate), 0030 m_image(image) 0031 { 0032 } 0033 0034 void partA() override { 0035 m_image->disableUIUpdates(); 0036 } 0037 0038 void partB() override { 0039 m_image->enableUIUpdates(); 0040 } 0041 0042 int id() const override { 0043 return KisCommandUtils::DisableUIUpdatesCommandId; 0044 } 0045 0046 bool mergeWith(const KUndo2Command *command) override { 0047 return canMergeWith(command); 0048 } 0049 0050 bool canMergeWith(const KUndo2Command *command) const override { 0051 const DisableUIUpdatesCommand *other = 0052 dynamic_cast<const DisableUIUpdatesCommand*>(command); 0053 0054 return other && other->m_image == m_image; 0055 } 0056 0057 private: 0058 KisImageWSP m_image; 0059 }; 0060 0061 0062 class UpdateCommand : public KisCommandUtils::FlipFlopCommand, public KisAsynchronouslyMergeableCommandInterface 0063 { 0064 public: 0065 UpdateCommand(KisImageWSP image, KisNodeSP node, 0066 KisProcessingApplicator::ProcessingFlags flags, 0067 State initialState, QSharedPointer<bool> sharedAllFramesToken) 0068 : FlipFlopCommand(initialState), 0069 m_image(image), 0070 m_node(node), 0071 m_flags(flags), 0072 m_sharedAllFramesToken(sharedAllFramesToken) 0073 { 0074 } 0075 0076 private: 0077 void partA() override { 0078 /** 0079 * We disable all non-centralized updates here. Everything 0080 * should be done by this command's explicit updates. 0081 * 0082 * If you still need third-party updates work, please add a 0083 * flag to the applicator. 0084 */ 0085 m_image->disableDirtyRequests(); 0086 } 0087 0088 void partB() override { 0089 m_image->enableDirtyRequests(); 0090 0091 if (*m_sharedAllFramesToken) { 0092 KisLayerUtils::recursiveApplyNodes(m_image->root(), [](KisNodeSP node){ 0093 KisPaintLayer* paintLayer = qobject_cast<KisPaintLayer*>(node.data()); 0094 if (paintLayer && paintLayer->onionSkinEnabled()) { 0095 paintLayer->flushOnionSkinCache(); 0096 } 0097 }); 0098 } 0099 0100 m_image->root()->graphListener()->invalidateFrames(KisTimeSpan::infinite(0), m_node->exactBounds()); 0101 0102 if (!m_flags.testFlag(KisProcessingApplicator::NO_IMAGE_UPDATES)) { 0103 if(m_flags.testFlag(KisProcessingApplicator::RECURSIVE)) { 0104 m_image->refreshGraphAsync(m_node); 0105 } 0106 0107 m_node->setDirty(m_image->bounds()); 0108 0109 updateClones(m_node); 0110 } 0111 } 0112 0113 void updateClones(KisNodeSP node) { 0114 // simple tail-recursive iteration 0115 0116 KisNodeSP prevNode = node->lastChild(); 0117 while(prevNode) { 0118 updateClones(prevNode); 0119 prevNode = prevNode->prevSibling(); 0120 } 0121 0122 KisLayer *layer = qobject_cast<KisLayer*>(m_node.data()); 0123 if(layer && layer->hasClones()) { 0124 Q_FOREACH (KisCloneLayerSP clone, layer->registeredClones()) { 0125 if(!clone) continue; 0126 0127 QPoint offset(clone->x(), clone->y()); 0128 QRegion dirtyRegion(m_image->bounds()); 0129 dirtyRegion -= m_image->bounds().translated(offset); 0130 0131 clone->setDirty(KisRegion::fromQRegion(dirtyRegion)); 0132 } 0133 } 0134 } 0135 0136 int id() const override { 0137 return KisCommandUtils::UpdateCommandId; 0138 } 0139 0140 bool mergeWith(const KUndo2Command *command) override { 0141 return canMergeWith(command); 0142 } 0143 0144 bool canMergeWith(const KUndo2Command *command) const override { 0145 const UpdateCommand *other = 0146 dynamic_cast<const UpdateCommand*>(command); 0147 0148 return other && 0149 other->m_image == m_image && 0150 other->m_node == m_node && 0151 other->m_flags == m_flags && 0152 bool(other->m_sharedAllFramesToken) == bool(m_sharedAllFramesToken) && 0153 (!m_sharedAllFramesToken || *m_sharedAllFramesToken == *other->m_sharedAllFramesToken); 0154 } 0155 0156 private: 0157 KisImageWSP m_image; 0158 KisNodeSP m_node; 0159 KisProcessingApplicator::ProcessingFlags m_flags; 0160 QSharedPointer<bool> m_sharedAllFramesToken; 0161 }; 0162 0163 class EmitImageSignalsCommand : public KisCommandUtils::FlipFlopCommand, public KisAsynchronouslyMergeableCommandInterface 0164 { 0165 public: 0166 EmitImageSignalsCommand(KisImageWSP image, 0167 KisImageSignalVector emitSignals, 0168 bool finalUpdate) 0169 : FlipFlopCommand(finalUpdate), 0170 m_image(image), 0171 m_emitSignals(emitSignals) 0172 { 0173 } 0174 0175 void partB() override { 0176 if (getState() == State::FINALIZING) { 0177 doUpdate(m_emitSignals); 0178 } else { 0179 KisImageSignalVector reverseSignals; 0180 0181 KisImageSignalVector::iterator i = m_emitSignals.end(); 0182 while (i != m_emitSignals.begin()) { 0183 --i; 0184 reverseSignals.append(i->inverted()); 0185 } 0186 0187 doUpdate(reverseSignals); 0188 } 0189 } 0190 0191 int id() const override { 0192 return KisCommandUtils::EmitImageSignalsCommandId; 0193 } 0194 0195 bool mergeWith(const KUndo2Command *command) override { 0196 return canMergeWith(command); 0197 } 0198 0199 bool canMergeWith(const KUndo2Command *command) const override { 0200 const EmitImageSignalsCommand *other = 0201 dynamic_cast<const EmitImageSignalsCommand*>(command); 0202 0203 return other && 0204 other->m_image == m_image; 0205 0206 // TODO: implement proper comparison for emitted signals 0207 // other->m_emitSignals == m_emitSignals; 0208 } 0209 0210 private: 0211 void doUpdate(KisImageSignalVector emitSignals) { 0212 Q_FOREACH (KisImageSignalType type, emitSignals) { 0213 m_image->signalRouter()->emitNotification(type); 0214 } 0215 } 0216 0217 private: 0218 KisImageWSP m_image; 0219 KisImageSignalVector m_emitSignals; 0220 }; 0221 0222 0223 KisProcessingApplicator::KisProcessingApplicator(KisImageWSP image, 0224 KisNodeSP node, 0225 ProcessingFlags flags, 0226 KisImageSignalVector emitSignals, 0227 const KUndo2MagicString &name, 0228 KUndo2CommandExtraData *extraData, 0229 int macroId) 0230 : m_image(image), 0231 m_node(node), 0232 m_flags(flags), 0233 m_emitSignals(emitSignals), 0234 m_finalSignalsEmitted(false), 0235 m_sharedAllFramesToken(new bool(false)) 0236 { 0237 KisStrokeStrategyUndoCommandBased *strategy = 0238 new KisStrokeStrategyUndoCommandBased(name, false, m_image.data()); 0239 0240 if (m_flags.testFlag(SUPPORTS_WRAPAROUND_MODE)) { 0241 strategy->setSupportsWrapAroundMode(true); 0242 } 0243 0244 if (extraData) { 0245 strategy->setCommandExtraData(extraData); 0246 } 0247 0248 strategy->setMacroId(macroId); 0249 0250 m_strokeId = m_image->startStroke(strategy); 0251 if(!m_emitSignals.isEmpty()) { 0252 applyCommand(new EmitImageSignalsCommand(m_image, m_emitSignals, false), KisStrokeJobData::BARRIER); 0253 } 0254 0255 if(m_flags.testFlag(NO_UI_UPDATES)) { 0256 applyCommand(new DisableUIUpdatesCommand(m_image, false), KisStrokeJobData::BARRIER); 0257 } 0258 0259 if (m_node) { 0260 applyCommand(new UpdateCommand(m_image, m_node, m_flags, 0261 UpdateCommand::INITIALIZING, 0262 m_sharedAllFramesToken)); 0263 } 0264 } 0265 0266 KisProcessingApplicator::~KisProcessingApplicator() 0267 { 0268 } 0269 0270 const KisStrokeId KisProcessingApplicator::getStroke() const 0271 { 0272 return m_strokeId; 0273 } 0274 0275 void KisProcessingApplicator::applyVisitor(KisProcessingVisitorSP visitor, 0276 KisStrokeJobData::Sequentiality sequentiality, 0277 KisStrokeJobData::Exclusivity exclusivity) 0278 { 0279 KUndo2Command *initCommand = visitor->createInitCommand(); 0280 if (initCommand) { 0281 applyCommand(initCommand, 0282 KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); 0283 } 0284 0285 if(!m_flags.testFlag(RECURSIVE)) { 0286 applyCommand(new KisProcessingCommand(visitor, m_node), 0287 sequentiality, exclusivity); 0288 } 0289 else { 0290 visitRecursively(m_node, visitor, sequentiality, exclusivity); 0291 } 0292 } 0293 0294 void KisProcessingApplicator::applyVisitorAllFrames(KisProcessingVisitorSP visitor, 0295 KisStrokeJobData::Sequentiality sequentiality, 0296 KisStrokeJobData::Exclusivity exclusivity) 0297 { 0298 *m_sharedAllFramesToken = true; 0299 0300 KUndo2Command *initCommand = visitor->createInitCommand(); 0301 if (initCommand) { 0302 applyCommand(initCommand, 0303 KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); 0304 } 0305 0306 KisLayerUtils::FrameJobs jobs; 0307 0308 // TODO: implement a nonrecursive case when !m_flags.testFlag(RECURSIVE) 0309 // (such case is not yet used anywhere) 0310 KIS_SAFE_ASSERT_RECOVER_NOOP(m_flags.testFlag(RECURSIVE)); 0311 0312 KisLayerUtils::updateFrameJobsRecursive(&jobs, m_node); 0313 0314 if (jobs.isEmpty()) { 0315 applyVisitor(visitor, sequentiality, exclusivity); 0316 return; 0317 } 0318 0319 KisLayerUtils::FrameJobs::const_iterator it = jobs.constBegin(); 0320 KisLayerUtils::FrameJobs::const_iterator end = jobs.constEnd(); 0321 0322 KisLayerUtils::SwitchFrameCommand::SharedStorageSP switchFrameStorage( 0323 new KisLayerUtils::SwitchFrameCommand::SharedStorage()); 0324 0325 for (; it != end; ++it) { 0326 const int frame = it.key(); 0327 const QSet<KisNodeSP> &nodes = it.value(); 0328 0329 applyCommand(new KisLayerUtils::SwitchFrameCommand(m_image, frame, false, switchFrameStorage), KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE); 0330 0331 foreach (KisNodeSP node, nodes) { 0332 applyCommand(new KisProcessingCommand(visitor, node), 0333 sequentiality, exclusivity); 0334 } 0335 0336 applyCommand(new KisLayerUtils::SwitchFrameCommand(m_image, frame, true, switchFrameStorage), KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE); 0337 } 0338 } 0339 0340 void KisProcessingApplicator::visitRecursively(KisNodeSP node, 0341 KisProcessingVisitorSP visitor, 0342 KisStrokeJobData::Sequentiality sequentiality, 0343 KisStrokeJobData::Exclusivity exclusivity) 0344 { 0345 // simple tail-recursive iteration 0346 0347 KisNodeSP prevNode = node->lastChild(); 0348 while(prevNode) { 0349 visitRecursively(prevNode, visitor, sequentiality, exclusivity); 0350 prevNode = prevNode->prevSibling(); 0351 } 0352 0353 applyCommand(new KisProcessingCommand(visitor, node), 0354 sequentiality, exclusivity); 0355 } 0356 0357 void KisProcessingApplicator::applyCommand(KUndo2Command *command, 0358 KisStrokeJobData::Sequentiality sequentiality, 0359 KisStrokeJobData::Exclusivity exclusivity) 0360 { 0361 /* 0362 * One should not add commands after the final signals have been 0363 * emitted, only end or cancel the stroke 0364 */ 0365 KIS_ASSERT_RECOVER_RETURN(!m_finalSignalsEmitted); 0366 0367 m_image->addJob(m_strokeId, 0368 new KisStrokeStrategyUndoCommandBased::Data(KUndo2CommandSP(command), 0369 false, 0370 sequentiality, 0371 exclusivity)); 0372 } 0373 0374 void KisProcessingApplicator::explicitlyEmitFinalSignals() 0375 { 0376 KIS_ASSERT_RECOVER_RETURN(!m_finalSignalsEmitted); 0377 0378 if (m_node) { 0379 applyCommand(new UpdateCommand(m_image, m_node, m_flags, 0380 UpdateCommand::FINALIZING, 0381 m_sharedAllFramesToken)); 0382 } 0383 0384 if(m_flags.testFlag(NO_UI_UPDATES)) { 0385 applyCommand(new DisableUIUpdatesCommand(m_image, true), KisStrokeJobData::BARRIER); 0386 } 0387 0388 if(!m_emitSignals.isEmpty()) { 0389 applyCommand(new EmitImageSignalsCommand(m_image, m_emitSignals, true), KisStrokeJobData::BARRIER); 0390 } 0391 0392 // simple consistency check 0393 m_finalSignalsEmitted = true; 0394 } 0395 0396 void KisProcessingApplicator::end() 0397 { 0398 if (!m_finalSignalsEmitted) { 0399 explicitlyEmitFinalSignals(); 0400 } 0401 0402 m_image->endStroke(m_strokeId); 0403 } 0404 0405 void KisProcessingApplicator::cancel() 0406 { 0407 m_image->cancelStroke(m_strokeId); 0408 } 0409 0410 void KisProcessingApplicator::runSingleCommandStroke(KisImageSP image, KUndo2Command *cmd, KisStrokeJobData::Sequentiality sequentiality, KisStrokeJobData::Exclusivity exclusivity) 0411 { 0412 KisProcessingApplicator applicator(image, 0, 0413 KisProcessingApplicator::NONE, 0414 KisImageSignalVector(), 0415 cmd->text()); 0416 applicator.applyCommand(cmd, sequentiality, exclusivity); 0417 applicator.end(); 0418 }