Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_processing_applicator.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2011 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include "kis_image.h"
10#include "kis_paint_layer.h"
11#include "kis_node.h"
12#include "kis_clone_layer.h"
16#include "kis_layer_utils.h"
17#include "kis_command_utils.h"
18#include "kis_time_span.h"
19#include "kis_node.h"
22#include "kis_command_ids.h"
24
26{
27public:
29 bool finalUpdate)
30 : FlipFlopCommand(finalUpdate),
31 m_image(image)
32 {
33 }
34
35 void partA() override {
37 }
38
39 void partB() override {
41 }
42
43 int id() const override {
45 }
46
47 bool mergeWith(const KUndo2Command *command) override {
48 return canMergeWith(command);
49 }
50
51 bool canMergeWith(const KUndo2Command *command) const override {
52 const DisableUIUpdatesCommand *other =
53 dynamic_cast<const DisableUIUpdatesCommand*>(command);
54
55 return other && other->m_image == m_image;
56 }
57
58private:
60};
61
62
64{
65public:
67 KisProcessingApplicator::ProcessingFlags flags,
68 State initialState, QSharedPointer<bool> sharedAllFramesToken)
69 : FlipFlopCommand(initialState),
70 m_image(image),
71 m_nodes(nodes),
72 m_flags(flags),
73 m_sharedAllFramesToken(sharedAllFramesToken)
74 {
75 }
76
77private:
78 void partA() override {
87 }
88
89 void partB() override {
91
94 KisPaintLayer* paintLayer = qobject_cast<KisPaintLayer*>(node.data());
95 if (paintLayer && paintLayer->onionSkinEnabled()) {
96 paintLayer->flushOnionSkinCache();
97 }
98 });
99 }
100
101 Q_FOREACH(KisNodeSP node, m_nodes) {
103
107 }
108
109 node->setDirty(m_image->bounds());
110
111 updateClones(node);
112 }
113 }
114 }
115
128 if (qEnvironmentVariableIsSet("KRITA_ENABLE_CLONE_UPDATES_IN_APPLICATOR")) {
129 // simple tail-recursive iteration
130 KisNodeSP prevNode = node->lastChild();
131 while(prevNode) {
132 updateClones(prevNode);
133 prevNode = prevNode->prevSibling();
134 }
135
136 Q_FOREACH(KisNodeSP node, m_nodes) {
137 KisLayer *layer = qobject_cast<KisLayer*>(node.data());
138 if(layer && layer->hasClones()) {
139 Q_FOREACH (KisCloneLayerSP clone, layer->registeredClones()) {
140 if(!clone) continue;
141
142 QPoint offset(clone->x(), clone->y());
143 QRegion dirtyRegion(m_image->bounds());
144 dirtyRegion -= m_image->bounds().translated(offset);
145
146 clone->setDirty(KisRegion::fromQRegion(dirtyRegion));
147 }
148 }
149 }
150 }
151 }
152
153 int id() const override {
155 }
156
157 bool mergeWith(const KUndo2Command *command) override {
158 return canMergeWith(command);
159 }
160
161 bool canMergeWith(const KUndo2Command *command) const override {
162 const UpdateCommand *other =
163 dynamic_cast<const UpdateCommand*>(command);
164
165 return other &&
166 other->m_image == m_image &&
167 other->m_nodes == m_nodes &&
168 other->m_flags == m_flags &&
169 bool(other->m_sharedAllFramesToken) == bool(m_sharedAllFramesToken) &&
170 (!m_sharedAllFramesToken || *m_sharedAllFramesToken == *other->m_sharedAllFramesToken);
171 }
172
173private:
176 KisProcessingApplicator::ProcessingFlags m_flags;
178};
179
181{
182public:
184 KisImageSignalVector emitSignals,
185 bool finalUpdate)
186 : FlipFlopCommand(finalUpdate),
187 m_image(image),
188 m_emitSignals(emitSignals)
189 {
190 }
191
192 void partB() override {
193 if (getState() == State::FINALIZING) {
195 } else {
196 KisImageSignalVector reverseSignals;
197
198 KisImageSignalVector::iterator i = m_emitSignals.end();
199 while (i != m_emitSignals.begin()) {
200 --i;
201 reverseSignals.append(i->inverted());
202 }
203
204 doUpdate(reverseSignals);
205 }
206 }
207
208 int id() const override {
210 }
211
212 bool mergeWith(const KUndo2Command *command) override {
213 return canMergeWith(command);
214 }
215
216 bool canMergeWith(const KUndo2Command *command) const override {
217 const EmitImageSignalsCommand *other =
218 dynamic_cast<const EmitImageSignalsCommand*>(command);
219
220 return other &&
221 other->m_image == m_image;
222
223 // TODO: implement proper comparison for emitted signals
224 // other->m_emitSignals == m_emitSignals;
225 }
226
227private:
228 void doUpdate(KisImageSignalVector emitSignals) {
229 Q_FOREACH (KisImageSignalType type, emitSignals) {
231 }
232 }
233
234private:
237};
238
261
263 KisNodeSP node,
264 ProcessingFlags flags,
265 KisImageSignalVector emitSignals,
266 const KUndo2MagicString &name,
267 KUndo2CommandExtraData *extraData,
268 int macroId)
269 : KisProcessingApplicator(image, node ? KisNodeList { node } : KisNodeList(), flags, emitSignals, name, extraData, macroId )
270{
271
272}
274 KisNodeList nodes,
275 ProcessingFlags flags,
276 KisImageSignalVector emitSignals,
277 const KUndo2MagicString &name,
278 KUndo2CommandExtraData *extraData,
279 int macroId)
280 : m_image(image),
281 m_nodes(nodes),
282 m_flags(flags),
283 m_emitSignals(emitSignals),
284 m_finalSignalsEmitted(false),
285 m_sharedAllFramesToken(new bool(false))
286{
287 StrategyWithStatusPromise *strategy =
289
291
292 if (m_flags.testFlag(SUPPORTS_WRAPAROUND_MODE)) {
293 strategy->setSupportsWrapAroundMode(true);
294 }
295
296 if (extraData) {
297 strategy->setCommandExtraData(extraData);
298 }
299
300 strategy->setMacroId(macroId);
301
302 m_strokeId = m_image->startStroke(strategy);
303 if(!m_emitSignals.isEmpty()) {
305 }
306
307 if(m_flags.testFlag(NO_UI_UPDATES)) {
309 }
310
311 if (!m_nodes.isEmpty()) {
315 }
316}
317
321
326
332
336{
337 KUndo2Command *initCommand = visitor->createInitCommand();
338 if (initCommand) {
339 applyCommand(initCommand,
341 }
342
343 if (!m_nodes.isEmpty()) {
344 Q_FOREACH(KisNodeSP node, m_nodes) {
345 if(!m_flags.testFlag(RECURSIVE)) {
346 applyCommand(new KisProcessingCommand(visitor, node),
347 sequentiality, exclusivity);
348 }
349 else {
350 visitRecursively(node, visitor, sequentiality, exclusivity);
351 }
352 }
353 }
354}
355
359{
361
362 KUndo2Command *initCommand = visitor->createInitCommand();
363 if (initCommand) {
364 applyCommand(initCommand,
366 }
367
369
370 // TODO: implement a nonrecursive case when !m_flags.testFlag(RECURSIVE)
371 // (such case is not yet used anywhere)
373
374 if (!m_nodes.isEmpty()) {
375 Q_FOREACH(KisNodeSP node, m_nodes) {
377 }
378 }
379
380 if (jobs.isEmpty()) {
381 applyVisitor(visitor, sequentiality, exclusivity);
382 return;
383 }
384
385 KisLayerUtils::FrameJobs::const_iterator it = jobs.constBegin();
386 KisLayerUtils::FrameJobs::const_iterator end = jobs.constEnd();
387
390
391 for (; it != end; ++it) {
392 const int frame = it.key();
393 const QSet<KisNodeSP> &nodes = it.value();
394
396
397 foreach (KisNodeSP node, nodes) {
398 applyCommand(new KisProcessingCommand(visitor, node),
399 sequentiality, exclusivity);
400 }
401
403 }
404}
405
410{
411 // simple tail-recursive iteration
412
413 KisNodeSP prevNode = node->lastChild();
414 while(prevNode) {
415 visitRecursively(prevNode, visitor, sequentiality, exclusivity);
416 prevNode = prevNode->prevSibling();
417 }
418
419 applyCommand(new KisProcessingCommand(visitor, node),
420 sequentiality, exclusivity);
421}
422
426{
427 /*
428 * One should not add commands after the final signals have been
429 * emitted, only end or cancel the stroke
430 */
432
435 false,
436 sequentiality,
437 exclusivity));
438}
439
461
470
475
477{
478 KisProcessingApplicator applicator(image, 0,
481 cmd->text());
482 applicator.applyCommand(cmd, sequentiality, exclusivity);
483 applicator.end();
484}
QVector< KisImageSignalType > KisImageSignalVector
bool mergeWith(const KUndo2Command *command) override
bool canMergeWith(const KUndo2Command *command) const override
DisableUIUpdatesCommand(KisImageWSP image, bool finalUpdate)
bool mergeWith(const KUndo2Command *command) override
void doUpdate(KisImageSignalVector emitSignals)
EmitImageSignalsCommand(KisImageWSP image, KisImageSignalVector emitSignals, bool finalUpdate)
bool canMergeWith(const KUndo2Command *command) const override
KUndo2MagicString text() const
void emitNotification(KisImageSignalType type)
void disableUIUpdates() override
void refreshGraphAsync(KisNodeSP root, const QVector< QRect > &rects, const QRect &cropRect, KisProjectionUpdateFlags flags=KisProjectionUpdateFlag::None) override
bool cancelStroke(KisStrokeId id) override
void disableDirtyRequests() override
void enableDirtyRequests() override
void addJob(KisStrokeId id, KisStrokeJobData *data) override
QVector< QRect > enableUIUpdates() override
KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy) override
KisImageSignalRouter * signalRouter()
QRect bounds() const override
void endStroke(KisStrokeId id) override
void applyVisitor(KisProcessingVisitorSP visitor, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
QSharedPointer< bool > m_sharedAllFramesToken
void visitRecursively(KisNodeSP node, KisProcessingVisitorSP visitor, KisStrokeJobData::Sequentiality sequentiality, KisStrokeJobData::Exclusivity exclusivity)
static void runSingleCommandStroke(KisImageSP image, KUndo2Command *cmd, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
runSingleCommandStroke creates a stroke and runs cmd in it. The text() field of cmd is used as a titl...
const KisStrokeId getStroke() const
void applyCommand(KUndo2Command *command, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
void applyVisitorAllFrames(KisProcessingVisitorSP visitor, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
std::future< bool > && successfullyCompletedFuture()
std::future< bool > m_successfullyCompletedFuture
KisProcessingApplicator(KisImageWSP image, KisNodeSP node, ProcessingFlags flags=NONE, KisImageSignalVector emitSignals=KisImageSignalVector(), const KUndo2MagicString &name=KUndo2MagicString(), KUndo2CommandExtraData *extraData=0, int macroId=-1)
virtual KUndo2Command * createInitCommand()
static KisRegion fromQRegion(const QRegion &region)
void cancelStrokeCallbackImpl(QVector< KisStrokeJobData * > &mutatedJobs)
void addMutatedJobs(const QVector< KisStrokeJobData * > list)
KUndo2MagicString name() const
static KisTimeSpan infinite(int start)
void updateClones(KisNodeSP node)
QSharedPointer< bool > m_sharedAllFramesToken
bool canMergeWith(const KUndo2Command *command) const override
int id() const override
UpdateCommand(KisImageWSP image, KisNodeList nodes, KisProcessingApplicator::ProcessingFlags flags, State initialState, QSharedPointer< bool > sharedAllFramesToken)
bool mergeWith(const KUndo2Command *command) override
KisProcessingApplicator::ProcessingFlags m_flags
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
QSharedPointer< KUndo2Command > KUndo2CommandSP
Definition kis_types.h:262
void updateFrameJobsRecursive(FrameJobs *jobs, KisNodeSP rootNode)
QMap< int, QSet< KisNodeSP > > FrameJobs
void recursiveApplyNodes(NodePointer node, Functor func)
void addJobBarrier(QVector< Job * > &jobs, Func func)
virtual QRect exactBounds() const
FlipFlopCommand(State initialState, KUndo2Command *parent=0)
The SwitchFrameCommand struct Switches to frame with undo/redo support.
const QList< KisCloneLayerWSP > registeredClones() const
Definition kis_layer.cc:478
bool hasClones() const
Definition kis_layer.cc:483
virtual void invalidateFrames(const KisTimeSpan &range, const QRect &rect)
KisNodeSP prevSibling() const
Definition kis_node.cpp:402
KisNodeSP lastChild() const
Definition kis_node.cpp:367
KisNodeGraphListener * graphListener
Definition kis_node.cpp:87
virtual void setDirty()
Definition kis_node.cpp:577
StrategyWithStatusPromise(const KUndo2MagicString &name, KisStrokeUndoFacade *facade)