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