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 }