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 }