1 /*
2  *  Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "kis_processing_applicator.h"
20 
21 #include "kis_image.h"
22 #include "kis_paint_layer.h"
23 #include "kis_node.h"
24 #include "kis_clone_layer.h"
25 #include "kis_processing_visitor.h"
26 #include "commands_new/kis_processing_command.h"
27 #include "kis_stroke_strategy_undo_command_based.h"
28 #include "kis_layer_utils.h"
29 #include "kis_command_utils.h"
30 #include "kis_time_range.h"
31 #include "kis_node.h"
32 #include "kis_image_signal_router.h"
33 #include "KisAsynchronouslyMergeableCommandInterface.h"
34 #include "kis_command_ids.h"
35 
36 class DisableUIUpdatesCommand : public KisCommandUtils::FlipFlopCommand, public KisAsynchronouslyMergeableCommandInterface
37 {
38 public:
DisableUIUpdatesCommand(KisImageWSP image,bool finalUpdate)39     DisableUIUpdatesCommand(KisImageWSP image,
40                             bool finalUpdate)
41         : FlipFlopCommand(finalUpdate),
42           m_image(image)
43     {
44     }
45 
partA()46     void partA() override {
47         m_image->disableUIUpdates();
48     }
49 
partB()50     void partB() override {
51         m_image->enableUIUpdates();
52     }
53 
id() const54     int id() const override {
55         return KisCommandUtils::DisableUIUpdatesCommandId;
56     }
57 
mergeWith(const KUndo2Command * command)58     bool mergeWith(const KUndo2Command *command) override {
59         return canMergeWith(command);
60     }
61 
canMergeWith(const KUndo2Command * command) const62     bool canMergeWith(const KUndo2Command *command) const override {
63         const DisableUIUpdatesCommand *other =
64             dynamic_cast<const DisableUIUpdatesCommand*>(command);
65 
66         return other && other->m_image == m_image;
67     }
68 
69 private:
70     KisImageWSP m_image;
71 };
72 
73 
74 class UpdateCommand : public KisCommandUtils::FlipFlopCommand, public KisAsynchronouslyMergeableCommandInterface
75 {
76 public:
UpdateCommand(KisImageWSP image,KisNodeSP node,KisProcessingApplicator::ProcessingFlags flags,State initialState,QSharedPointer<bool> sharedAllFramesToken)77     UpdateCommand(KisImageWSP image, KisNodeSP node,
78                   KisProcessingApplicator::ProcessingFlags flags,
79                   State initialState, QSharedPointer<bool> sharedAllFramesToken)
80         : FlipFlopCommand(initialState),
81           m_image(image),
82           m_node(node),
83           m_flags(flags),
84           m_sharedAllFramesToken(sharedAllFramesToken)
85     {
86     }
87 
88 private:
partA()89     void partA() override {
90         /**
91          * We disable all non-centralized updates here. Everything
92          * should be done by this command's explicit updates.
93          *
94          * If you still need third-party updates work, please add a
95          * flag to the applicator.
96          */
97         m_image->disableDirtyRequests();
98     }
99 
partB()100     void partB() override {
101         m_image->enableDirtyRequests();
102 
103         if (*m_sharedAllFramesToken) {
104             KisLayerUtils::recursiveApplyNodes(m_image->root(), [](KisNodeSP node){
105                 KisPaintLayer* paintLayer = qobject_cast<KisPaintLayer*>(node.data());
106                 if (paintLayer && paintLayer->onionSkinEnabled()) {
107                     paintLayer->flushOnionSkinCache();
108                 }
109             });
110         }
111 
112         m_image->root()->graphListener()->invalidateFrames(KisTimeRange::infinite(0), m_node->exactBounds());
113 
114         if (!m_flags.testFlag(KisProcessingApplicator::NO_IMAGE_UPDATES)) {
115             if(m_flags.testFlag(KisProcessingApplicator::RECURSIVE)) {
116                 m_image->refreshGraphAsync(m_node);
117             }
118 
119             m_node->setDirty(m_image->bounds());
120 
121             updateClones(m_node);
122         }
123     }
124 
updateClones(KisNodeSP node)125     void updateClones(KisNodeSP node) {
126         // simple tail-recursive iteration
127 
128         KisNodeSP prevNode = node->lastChild();
129         while(prevNode) {
130             updateClones(prevNode);
131             prevNode = prevNode->prevSibling();
132         }
133 
134         KisLayer *layer = qobject_cast<KisLayer*>(m_node.data());
135         if(layer && layer->hasClones()) {
136             Q_FOREACH (KisCloneLayerSP clone, layer->registeredClones()) {
137                 if(!clone) continue;
138 
139                 QPoint offset(clone->x(), clone->y());
140                 QRegion dirtyRegion(m_image->bounds());
141                 dirtyRegion -= m_image->bounds().translated(offset);
142 
143                 clone->setDirty(KisRegion::fromQRegion(dirtyRegion));
144             }
145         }
146     }
147 
id() const148     int id() const override {
149         return KisCommandUtils::UpdateCommandId;
150     }
151 
mergeWith(const KUndo2Command * command)152     bool mergeWith(const KUndo2Command *command) override {
153         return canMergeWith(command);
154     }
155 
canMergeWith(const KUndo2Command * command) const156     bool canMergeWith(const KUndo2Command *command) const override {
157         const UpdateCommand *other =
158             dynamic_cast<const UpdateCommand*>(command);
159 
160         return other &&
161             other->m_image == m_image &&
162             other->m_node == m_node &&
163             other->m_flags == m_flags &&
164             bool(other->m_sharedAllFramesToken) == bool(m_sharedAllFramesToken) &&
165             (!m_sharedAllFramesToken || *m_sharedAllFramesToken == *other->m_sharedAllFramesToken);
166     }
167 
168 private:
169     KisImageWSP m_image;
170     KisNodeSP m_node;
171     KisProcessingApplicator::ProcessingFlags m_flags;
172     QSharedPointer<bool> m_sharedAllFramesToken;
173 };
174 
175 class EmitImageSignalsCommand : public KisCommandUtils::FlipFlopCommand, public KisAsynchronouslyMergeableCommandInterface
176 {
177 public:
EmitImageSignalsCommand(KisImageWSP image,KisImageSignalVector emitSignals,bool finalUpdate)178     EmitImageSignalsCommand(KisImageWSP image,
179                             KisImageSignalVector emitSignals,
180                             bool finalUpdate)
181         : FlipFlopCommand(finalUpdate),
182           m_image(image),
183           m_emitSignals(emitSignals)
184     {
185     }
186 
partB()187     void partB() override {
188         if (getState() == State::FINALIZING) {
189             doUpdate(m_emitSignals);
190         } else {
191             KisImageSignalVector reverseSignals;
192 
193             KisImageSignalVector::iterator i = m_emitSignals.end();
194             while (i != m_emitSignals.begin()) {
195                 --i;
196                 reverseSignals.append(i->inverted());
197             }
198 
199             doUpdate(reverseSignals);
200         }
201     }
202 
id() const203     int id() const override {
204         return KisCommandUtils::EmitImageSignalsCommandId;
205     }
206 
mergeWith(const KUndo2Command * command)207     bool mergeWith(const KUndo2Command *command) override {
208         return canMergeWith(command);
209     }
210 
canMergeWith(const KUndo2Command * command) const211     bool canMergeWith(const KUndo2Command *command) const override {
212         const EmitImageSignalsCommand *other =
213             dynamic_cast<const EmitImageSignalsCommand*>(command);
214 
215         return other &&
216             other->m_image == m_image;
217 
218             // TODO: implement proper comparison for emitted signals
219             // other->m_emitSignals == m_emitSignals;
220     }
221 
222 private:
doUpdate(KisImageSignalVector emitSignals)223     void doUpdate(KisImageSignalVector emitSignals) {
224         Q_FOREACH (KisImageSignalType type, emitSignals) {
225             m_image->signalRouter()->emitNotification(type);
226         }
227     }
228 
229 private:
230     KisImageWSP m_image;
231     KisImageSignalVector m_emitSignals;
232 };
233 
234 
KisProcessingApplicator(KisImageWSP image,KisNodeSP node,ProcessingFlags flags,KisImageSignalVector emitSignals,const KUndo2MagicString & name,KUndo2CommandExtraData * extraData,int macroId)235 KisProcessingApplicator::KisProcessingApplicator(KisImageWSP image,
236                                                  KisNodeSP node,
237                                                  ProcessingFlags flags,
238                                                  KisImageSignalVector emitSignals,
239                                                  const KUndo2MagicString &name,
240                                                  KUndo2CommandExtraData *extraData,
241                                                  int macroId)
242     : m_image(image),
243       m_node(node),
244       m_flags(flags),
245       m_emitSignals(emitSignals),
246       m_finalSignalsEmitted(false),
247       m_sharedAllFramesToken(new bool(false))
248 {
249     KisStrokeStrategyUndoCommandBased *strategy =
250             new KisStrokeStrategyUndoCommandBased(name, false, m_image.data());
251 
252     if (m_flags.testFlag(SUPPORTS_WRAPAROUND_MODE)) {
253         strategy->setSupportsWrapAroundMode(true);
254     }
255 
256     if (extraData) {
257         strategy->setCommandExtraData(extraData);
258     }
259 
260     strategy->setMacroId(macroId);
261 
262     m_strokeId = m_image->startStroke(strategy);
263     if(!m_emitSignals.isEmpty()) {
264         applyCommand(new EmitImageSignalsCommand(m_image, m_emitSignals, false), KisStrokeJobData::BARRIER);
265     }
266 
267     if(m_flags.testFlag(NO_UI_UPDATES)) {
268         applyCommand(new DisableUIUpdatesCommand(m_image, false), KisStrokeJobData::BARRIER);
269     }
270 
271     if (m_node) {
272         applyCommand(new UpdateCommand(m_image, m_node, m_flags,
273                                        UpdateCommand::INITIALIZING,
274                                        m_sharedAllFramesToken));
275     }
276 }
277 
~KisProcessingApplicator()278 KisProcessingApplicator::~KisProcessingApplicator()
279 {
280 }
281 
getStroke() const282 const KisStrokeId KisProcessingApplicator::getStroke() const
283 {
284     return m_strokeId;
285 }
286 
applyVisitor(KisProcessingVisitorSP visitor,KisStrokeJobData::Sequentiality sequentiality,KisStrokeJobData::Exclusivity exclusivity)287 void KisProcessingApplicator::applyVisitor(KisProcessingVisitorSP visitor,
288                                            KisStrokeJobData::Sequentiality sequentiality,
289                                            KisStrokeJobData::Exclusivity exclusivity)
290 {
291     KUndo2Command *initCommand = visitor->createInitCommand();
292     if (initCommand) {
293         applyCommand(initCommand,
294                      KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL);
295     }
296 
297     if(!m_flags.testFlag(RECURSIVE)) {
298         applyCommand(new KisProcessingCommand(visitor, m_node),
299                      sequentiality, exclusivity);
300     }
301     else {
302         visitRecursively(m_node, visitor, sequentiality, exclusivity);
303     }
304 }
305 
applyVisitorAllFrames(KisProcessingVisitorSP visitor,KisStrokeJobData::Sequentiality sequentiality,KisStrokeJobData::Exclusivity exclusivity)306 void KisProcessingApplicator::applyVisitorAllFrames(KisProcessingVisitorSP visitor,
307                                                     KisStrokeJobData::Sequentiality sequentiality,
308                                                     KisStrokeJobData::Exclusivity exclusivity)
309 {
310     *m_sharedAllFramesToken = true;
311 
312     KUndo2Command *initCommand = visitor->createInitCommand();
313     if (initCommand) {
314         applyCommand(initCommand,
315                      KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL);
316     }
317 
318     KisLayerUtils::FrameJobs jobs;
319 
320     // TODO: implement a nonrecursive case when !m_flags.testFlag(RECURSIVE)
321     //       (such case is not yet used anywhere)
322     KIS_SAFE_ASSERT_RECOVER_NOOP(m_flags.testFlag(RECURSIVE));
323 
324     KisLayerUtils::updateFrameJobsRecursive(&jobs, m_node);
325 
326     if (jobs.isEmpty()) {
327         applyVisitor(visitor, sequentiality, exclusivity);
328         return;
329     }
330 
331     KisLayerUtils::FrameJobs::const_iterator it = jobs.constBegin();
332     KisLayerUtils::FrameJobs::const_iterator end = jobs.constEnd();
333 
334     KisLayerUtils::SwitchFrameCommand::SharedStorageSP switchFrameStorage(
335         new KisLayerUtils::SwitchFrameCommand::SharedStorage());
336 
337     for (; it != end; ++it) {
338         const int frame = it.key();
339         const QSet<KisNodeSP> &nodes = it.value();
340 
341         applyCommand(new KisLayerUtils::SwitchFrameCommand(m_image, frame, false, switchFrameStorage), KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
342 
343         foreach (KisNodeSP node, nodes) {
344             applyCommand(new KisProcessingCommand(visitor, node),
345                          sequentiality, exclusivity);
346         }
347 
348         applyCommand(new KisLayerUtils::SwitchFrameCommand(m_image, frame, true, switchFrameStorage), KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
349     }
350 }
351 
visitRecursively(KisNodeSP node,KisProcessingVisitorSP visitor,KisStrokeJobData::Sequentiality sequentiality,KisStrokeJobData::Exclusivity exclusivity)352 void KisProcessingApplicator::visitRecursively(KisNodeSP node,
353                                                KisProcessingVisitorSP visitor,
354                                                KisStrokeJobData::Sequentiality sequentiality,
355                                                KisStrokeJobData::Exclusivity exclusivity)
356 {
357     // simple tail-recursive iteration
358 
359     KisNodeSP prevNode = node->lastChild();
360     while(prevNode) {
361         visitRecursively(prevNode, visitor, sequentiality, exclusivity);
362         prevNode = prevNode->prevSibling();
363     }
364 
365     applyCommand(new KisProcessingCommand(visitor, node),
366                  sequentiality, exclusivity);
367 }
368 
applyCommand(KUndo2Command * command,KisStrokeJobData::Sequentiality sequentiality,KisStrokeJobData::Exclusivity exclusivity)369 void KisProcessingApplicator::applyCommand(KUndo2Command *command,
370                                            KisStrokeJobData::Sequentiality sequentiality,
371                                            KisStrokeJobData::Exclusivity exclusivity)
372 {
373     /*
374      * One should not add commands after the final signals have been
375      * emitted, only end or cancel the stroke
376      */
377     KIS_ASSERT_RECOVER_RETURN(!m_finalSignalsEmitted);
378 
379     m_image->addJob(m_strokeId,
380                     new KisStrokeStrategyUndoCommandBased::Data(KUndo2CommandSP(command),
381                                                                 false,
382                                                                 sequentiality,
383                                                                 exclusivity));
384 }
385 
explicitlyEmitFinalSignals()386 void KisProcessingApplicator::explicitlyEmitFinalSignals()
387 {
388     KIS_ASSERT_RECOVER_RETURN(!m_finalSignalsEmitted);
389 
390     if (m_node) {
391         applyCommand(new UpdateCommand(m_image, m_node, m_flags,
392                                        UpdateCommand::FINALIZING,
393                                        m_sharedAllFramesToken));
394     }
395 
396     if(m_flags.testFlag(NO_UI_UPDATES)) {
397         applyCommand(new DisableUIUpdatesCommand(m_image, true), KisStrokeJobData::BARRIER);
398     }
399 
400     if(!m_emitSignals.isEmpty()) {
401         applyCommand(new EmitImageSignalsCommand(m_image, m_emitSignals, true), KisStrokeJobData::BARRIER);
402     }
403 
404     // simple consistency check
405     m_finalSignalsEmitted = true;
406 }
407 
end()408 void KisProcessingApplicator::end()
409 {
410     if (!m_finalSignalsEmitted) {
411         explicitlyEmitFinalSignals();
412     }
413 
414     m_image->endStroke(m_strokeId);
415 }
416 
cancel()417 void KisProcessingApplicator::cancel()
418 {
419     m_image->cancelStroke(m_strokeId);
420 }
421 
runSingleCommandStroke(KisImageSP image,KUndo2Command * cmd,KisStrokeJobData::Sequentiality sequentiality,KisStrokeJobData::Exclusivity exclusivity)422 void KisProcessingApplicator::runSingleCommandStroke(KisImageSP image, KUndo2Command *cmd, KisStrokeJobData::Sequentiality sequentiality, KisStrokeJobData::Exclusivity exclusivity)
423 {
424     KisProcessingApplicator applicator(image, 0,
425                                        KisProcessingApplicator::NONE,
426                                        KisImageSignalVector() << ModifiedSignal,
427                                        cmd->text());
428     applicator.applyCommand(cmd, sequentiality, exclusivity);
429     applicator.end();
430 }
431