1 /*
2  *  Copyright (c) 2015 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_layer_utils.h"
20 
21 #include <algorithm>
22 
23 #include <QUuid>
24 #include <KoColorSpaceConstants.h>
25 #include <KoProperties.h>
26 
27 #include "kis_painter.h"
28 #include "kis_image.h"
29 #include "kis_node.h"
30 #include "kis_layer.h"
31 #include "kis_paint_layer.h"
32 #include "kis_clone_layer.h"
33 #include "kis_group_layer.h"
34 #include "kis_selection.h"
35 #include "kis_selection_mask.h"
36 #include "kis_meta_data_merge_strategy.h"
37 #include <kundo2command.h>
38 #include "commands/kis_image_layer_add_command.h"
39 #include "commands/kis_image_layer_remove_command.h"
40 #include "commands/kis_image_layer_move_command.h"
41 #include "commands/kis_image_change_layers_command.h"
42 #include "commands_new/kis_activate_selection_mask_command.h"
43 #include "commands/kis_image_change_visibility_command.h"
44 #include "kis_abstract_projection_plane.h"
45 #include "kis_processing_applicator.h"
46 #include "kis_image_animation_interface.h"
47 #include "kis_keyframe_channel.h"
48 #include "kis_command_utils.h"
49 #include "commands_new/kis_change_projection_color_command.h"
50 #include "kis_layer_properties_icons.h"
51 #include "lazybrush/kis_colorize_mask.h"
52 #include "commands/kis_node_property_list_command.h"
53 #include "commands/kis_node_compositeop_command.h"
54 #include <KisDelayedUpdateNodeInterface.h>
55 #include <KisCroppedOriginalLayerInterface.h>
56 #include "krita_utils.h"
57 #include "kis_image_signal_router.h"
58 
59 
60 namespace KisLayerUtils {
61 
fetchSelectionMasks(KisNodeList mergedNodes,QVector<KisSelectionMaskSP> & selectionMasks)62     void fetchSelectionMasks(KisNodeList mergedNodes, QVector<KisSelectionMaskSP> &selectionMasks)
63     {
64         foreach (KisNodeSP node, mergedNodes) {
65 
66             Q_FOREACH(KisNodeSP child, node->childNodes(QStringList("KisSelectionMask"), KoProperties())) {
67 
68                 KisSelectionMaskSP mask = qobject_cast<KisSelectionMask*>(child.data());
69                 if (mask) {
70                     selectionMasks.append(mask);
71                 }
72             }
73         }
74     }
75 
76     struct MergeDownInfoBase {
MergeDownInfoBaseKisLayerUtils::MergeDownInfoBase77         MergeDownInfoBase(KisImageSP _image)
78             : image(_image),
79               storage(new SwitchFrameCommand::SharedStorage())
80         {
81         }
~MergeDownInfoBaseKisLayerUtils::MergeDownInfoBase82         virtual ~MergeDownInfoBase() {}
83 
84         KisImageWSP image;
85 
86         QVector<KisSelectionMaskSP> selectionMasks;
87 
88         KisNodeSP dstNode;
89 
90         SwitchFrameCommand::SharedStorageSP storage;
91         QSet<int> frames;
92         bool useInTimeline = false;
93         bool enableOnionSkins = false;
94 
95         virtual KisNodeList allSrcNodes() = 0;
96 
dstLayerKisLayerUtils::MergeDownInfoBase97         KisLayerSP dstLayer() {
98             return qobject_cast<KisLayer*>(dstNode.data());
99         }
100     };
101 
102     struct MergeDownInfo : public MergeDownInfoBase {
MergeDownInfoKisLayerUtils::MergeDownInfo103         MergeDownInfo(KisImageSP _image,
104                       KisLayerSP _prevLayer,
105                       KisLayerSP _currLayer)
106             : MergeDownInfoBase(_image),
107               prevLayer(_prevLayer),
108               currLayer(_currLayer)
109         {
110             frames =
111                 fetchLayerFramesRecursive(prevLayer) |
112                 fetchLayerFramesRecursive(currLayer);
113 
114             useInTimeline = prevLayer->useInTimeline() || currLayer->useInTimeline();
115 
116             const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer*>(currLayer.data());
117             if (paintLayer) enableOnionSkins |= paintLayer->onionSkinEnabled();
118 
119             paintLayer = qobject_cast<KisPaintLayer*>(prevLayer.data());
120             if (paintLayer) enableOnionSkins |= paintLayer->onionSkinEnabled();
121         }
122 
123         KisLayerSP prevLayer;
124         KisLayerSP currLayer;
125 
allSrcNodesKisLayerUtils::MergeDownInfo126         KisNodeList allSrcNodes() override {
127             KisNodeList mergedNodes;
128             mergedNodes << prevLayer;
129             mergedNodes << currLayer;
130             return mergedNodes;
131         }
132     };
133 
134     struct MergeMultipleInfo : public MergeDownInfoBase {
MergeMultipleInfoKisLayerUtils::MergeMultipleInfo135         MergeMultipleInfo(KisImageSP _image,
136                           KisNodeList _mergedNodes)
137             : MergeDownInfoBase(_image),
138               mergedNodes(_mergedNodes)
139         {
140             foreach (KisNodeSP node, mergedNodes) {
141                 frames |= fetchLayerFramesRecursive(node);
142                 useInTimeline |= node->useInTimeline();
143 
144                 const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer*>(node.data());
145                 if (paintLayer) {
146                     enableOnionSkins |= paintLayer->onionSkinEnabled();
147                 }
148             }
149         }
150 
151         KisNodeList mergedNodes;
152         bool nodesCompositingVaries = false;
153 
allSrcNodesKisLayerUtils::MergeMultipleInfo154         KisNodeList allSrcNodes() override {
155             return mergedNodes;
156         }
157     };
158 
159     typedef QSharedPointer<MergeDownInfoBase> MergeDownInfoBaseSP;
160     typedef QSharedPointer<MergeDownInfo> MergeDownInfoSP;
161     typedef QSharedPointer<MergeMultipleInfo> MergeMultipleInfoSP;
162 
163     struct FillSelectionMasks : public KUndo2Command {
FillSelectionMasksKisLayerUtils::FillSelectionMasks164         FillSelectionMasks(MergeDownInfoBaseSP info) : m_info(info) {}
165 
redoKisLayerUtils::FillSelectionMasks166         void redo() override {
167             fetchSelectionMasks(m_info->allSrcNodes(), m_info->selectionMasks);
168         }
169 
170     private:
171         MergeDownInfoBaseSP m_info;
172     };
173 
174     struct DisableColorizeKeyStrokes : public KisCommandUtils::AggregateCommand {
DisableColorizeKeyStrokesKisLayerUtils::DisableColorizeKeyStrokes175         DisableColorizeKeyStrokes(MergeDownInfoBaseSP info) : m_info(info) {}
176 
populateChildCommandsKisLayerUtils::DisableColorizeKeyStrokes177         void populateChildCommands() override {
178             Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
179                 recursiveApplyNodes(node,
180                                     [this] (KisNodeSP node) {
181                                         if (dynamic_cast<KisColorizeMask*>(node.data()) &&
182                                             KisLayerPropertiesIcons::nodeProperty(node, KisLayerPropertiesIcons::colorizeEditKeyStrokes, true).toBool()) {
183 
184                                             KisBaseNode::PropertyList props = node->sectionModelProperties();
185                                             KisLayerPropertiesIcons::setNodeProperty(&props,
186                                                                                      KisLayerPropertiesIcons::colorizeEditKeyStrokes,
187                                                                                      false);
188 
189                                             addCommand(new KisNodePropertyListCommand(node, props));
190                                         }
191                                     });
192             }
193         }
194 
195     private:
196         MergeDownInfoBaseSP m_info;
197     };
198 
199     struct DisableOnionSkins : public KisCommandUtils::AggregateCommand {
DisableOnionSkinsKisLayerUtils::DisableOnionSkins200         DisableOnionSkins(MergeDownInfoBaseSP info) : m_info(info) {}
201 
populateChildCommandsKisLayerUtils::DisableOnionSkins202         void populateChildCommands() override {
203             Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
204                 recursiveApplyNodes(node,
205                                     [this] (KisNodeSP node) {
206                                         if (KisLayerPropertiesIcons::nodeProperty(node, KisLayerPropertiesIcons::onionSkins, false).toBool()) {
207 
208                                             KisBaseNode::PropertyList props = node->sectionModelProperties();
209                                             KisLayerPropertiesIcons::setNodeProperty(&props,
210                                                                                      KisLayerPropertiesIcons::onionSkins,
211                                                                                      false);
212 
213                                             addCommand(new KisNodePropertyListCommand(node, props));
214                                         }
215                                     });
216             }
217         }
218 
219     private:
220         MergeDownInfoBaseSP m_info;
221     };
222 
223     struct DisableExtraCompositing : public KisCommandUtils::AggregateCommand {
DisableExtraCompositingKisLayerUtils::DisableExtraCompositing224         DisableExtraCompositing(MergeMultipleInfoSP info) : m_info(info) {}
225 
populateChildCommandsKisLayerUtils::DisableExtraCompositing226         void populateChildCommands() override {
227             /**
228              * We disable extra compositing only in case all the layers have
229              * the same compositing properties, therefore, we can just sum them using
230              * Normal blend mode
231              */
232             if (m_info->nodesCompositingVaries) return;
233 
234             // we should disable dirty requests on **redo only**, otherwise
235             // the state of the layers will not be recovered on undo
236             m_info->image->disableDirtyRequests();
237 
238             Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
239                 if (node->compositeOpId() != COMPOSITE_OVER) {
240                     addCommand(new KisNodeCompositeOpCommand(node, node->compositeOpId(), COMPOSITE_OVER));
241                 }
242 
243                 if (KisLayerPropertiesIcons::nodeProperty(node, KisLayerPropertiesIcons::inheritAlpha, false).toBool()) {
244 
245                     KisBaseNode::PropertyList props = node->sectionModelProperties();
246                     KisLayerPropertiesIcons::setNodeProperty(&props,
247                                                              KisLayerPropertiesIcons::inheritAlpha,
248                                                              false);
249 
250                     addCommand(new KisNodePropertyListCommand(node, props));
251                 }
252             }
253 
254             m_info->image->enableDirtyRequests();
255         }
256 
257     private:
258         MergeMultipleInfoSP m_info;
259     };
260 
261     struct DisablePassThroughForHeadsOnly : public KisCommandUtils::AggregateCommand {
DisablePassThroughForHeadsOnlyKisLayerUtils::DisablePassThroughForHeadsOnly262         DisablePassThroughForHeadsOnly(MergeDownInfoBaseSP info, bool skipIfDstIsGroup = false)
263             : m_info(info),
264               m_skipIfDstIsGroup(skipIfDstIsGroup)
265         {
266         }
267 
populateChildCommandsKisLayerUtils::DisablePassThroughForHeadsOnly268         void populateChildCommands() override {
269             if (m_skipIfDstIsGroup &&
270                 m_info->dstLayer() &&
271                 m_info->dstLayer()->inherits("KisGroupLayer")) {
272 
273                 return;
274             }
275 
276 
277             Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
278                 if (KisLayerPropertiesIcons::nodeProperty(node, KisLayerPropertiesIcons::passThrough, false).toBool()) {
279 
280                     KisBaseNode::PropertyList props = node->sectionModelProperties();
281                     KisLayerPropertiesIcons::setNodeProperty(&props,
282                                                              KisLayerPropertiesIcons::passThrough,
283                                                              false);
284 
285                     addCommand(new KisNodePropertyListCommand(node, props));
286                 }
287             }
288         }
289 
290     private:
291         MergeDownInfoBaseSP m_info;
292         bool m_skipIfDstIsGroup;
293     };
294 
295     struct RefreshHiddenAreas : public KUndo2Command {
RefreshHiddenAreasKisLayerUtils::RefreshHiddenAreas296         RefreshHiddenAreas(MergeDownInfoBaseSP info) : m_info(info) {}
297 
redoKisLayerUtils::RefreshHiddenAreas298         void redo() override {
299             KisImageAnimationInterface *interface = m_info->image->animationInterface();
300             const QRect preparedRect = !interface->externalFrameActive() ?
301                 m_info->image->bounds() : QRect();
302 
303             foreach (KisNodeSP node, m_info->allSrcNodes()) {
304                 refreshHiddenAreaAsync(m_info->image, node, preparedRect);
305             }
306         }
307 
308     private:
309         MergeDownInfoBaseSP m_info;
310     };
311 
312     struct RefreshDelayedUpdateLayers : public KUndo2Command {
RefreshDelayedUpdateLayersKisLayerUtils::RefreshDelayedUpdateLayers313         RefreshDelayedUpdateLayers(MergeDownInfoBaseSP info) : m_info(info) {}
314 
redoKisLayerUtils::RefreshDelayedUpdateLayers315         void redo() override {
316             foreach (KisNodeSP node, m_info->allSrcNodes()) {
317                 forceAllDelayedNodesUpdate(node);
318             }
319         }
320 
321     private:
322         MergeDownInfoBaseSP m_info;
323     };
324 
325     struct KeepMergedNodesSelected : public KisCommandUtils::AggregateCommand {
KeepMergedNodesSelectedKisLayerUtils::KeepMergedNodesSelected326         KeepMergedNodesSelected(MergeDownInfoSP info, bool finalizing)
327             : m_singleInfo(info),
328               m_finalizing(finalizing) {}
329 
KeepMergedNodesSelectedKisLayerUtils::KeepMergedNodesSelected330         KeepMergedNodesSelected(MergeMultipleInfoSP info, KisNodeSP putAfter, bool finalizing)
331             : m_multipleInfo(info),
332               m_finalizing(finalizing),
333               m_putAfter(putAfter) {}
334 
populateChildCommandsKisLayerUtils::KeepMergedNodesSelected335         void populateChildCommands() override {
336             KisNodeSP prevNode;
337             KisNodeSP nextNode;
338             KisNodeList prevSelection;
339             KisNodeList nextSelection;
340             KisImageSP image;
341 
342             if (m_singleInfo) {
343                 prevNode = m_singleInfo->currLayer;
344                 nextNode = m_singleInfo->dstNode;
345                 image = m_singleInfo->image;
346             } else if (m_multipleInfo) {
347                 prevNode = m_putAfter;
348                 nextNode = m_multipleInfo->dstNode;
349                 prevSelection = m_multipleInfo->allSrcNodes();
350                 image = m_multipleInfo->image;
351             }
352 
353             if (!m_finalizing) {
354                 addCommand(new KeepNodesSelectedCommand(prevSelection, KisNodeList(),
355                                                         prevNode, KisNodeSP(),
356                                                         image, false));
357             } else {
358                 addCommand(new KeepNodesSelectedCommand(KisNodeList(), nextSelection,
359                                                         KisNodeSP(), nextNode,
360                                                         image, true));
361             }
362         }
363 
364     private:
365         MergeDownInfoSP m_singleInfo;
366         MergeMultipleInfoSP m_multipleInfo;
367         bool m_finalizing;
368         KisNodeSP m_putAfter;
369     };
370 
371     struct CreateMergedLayer : public KisCommandUtils::AggregateCommand {
CreateMergedLayerKisLayerUtils::CreateMergedLayer372         CreateMergedLayer(MergeDownInfoSP info) : m_info(info) {}
373 
populateChildCommandsKisLayerUtils::CreateMergedLayer374         void populateChildCommands() override {
375             // actual merging done by KisLayer::createMergedLayer (or specialized descendant)
376             m_info->dstNode = m_info->currLayer->createMergedLayerTemplate(m_info->prevLayer);
377 
378             if (m_info->frames.size() > 0) {
379                 m_info->dstNode->enableAnimation();
380                 m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id(), true);
381             }
382 
383             m_info->dstNode->setUseInTimeline(m_info->useInTimeline);
384             m_info->dstNode->setColorLabelIndex(m_info->allSrcNodes().first()->colorLabelIndex());
385 
386             KisPaintLayer *dstPaintLayer = qobject_cast<KisPaintLayer*>(m_info->dstNode.data());
387             if (dstPaintLayer) {
388                 dstPaintLayer->setOnionSkinEnabled(m_info->enableOnionSkins);
389             }
390         }
391 
392     private:
393         MergeDownInfoSP m_info;
394     };
395 
396     struct CreateMergedLayerMultiple : public KisCommandUtils::AggregateCommand {
CreateMergedLayerMultipleKisLayerUtils::CreateMergedLayerMultiple397         CreateMergedLayerMultiple(MergeMultipleInfoSP info, const QString name = QString() )
398             : m_info(info),
399               m_name(name) {}
400 
populateChildCommandsKisLayerUtils::CreateMergedLayerMultiple401         void populateChildCommands() override {
402             QString mergedLayerName;
403             if (m_name.isEmpty()){
404                 const QString mergedLayerSuffix = i18n("Merged");
405                 mergedLayerName = m_info->mergedNodes.first()->name();
406 
407                 if (!mergedLayerName.endsWith(mergedLayerSuffix)) {
408                     mergedLayerName = QString("%1 %2")
409                         .arg(mergedLayerName).arg(mergedLayerSuffix);
410                 }
411             } else {
412                 mergedLayerName = m_name;
413             }
414 
415             KisPaintLayer *dstPaintLayer = new KisPaintLayer(m_info->image, mergedLayerName, OPACITY_OPAQUE_U8);
416             m_info->dstNode = dstPaintLayer;
417 
418             if (m_info->frames.size() > 0) {
419                 m_info->dstNode->enableAnimation();
420                 m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id(), true);
421             }
422 
423 
424             auto channelFlagsLazy = [](KisNodeSP node) {
425                 KisLayer *layer = dynamic_cast<KisLayer*>(node.data());
426                 return layer ? layer->channelFlags() : QBitArray();
427             };
428 
429             QString compositeOpId;
430             QBitArray channelFlags;
431             bool compositionVaries = false;
432             bool isFirstCycle = true;
433 
434             foreach (KisNodeSP node, m_info->allSrcNodes()) {
435                 if (isFirstCycle) {
436                     compositeOpId = node->compositeOpId();
437                     channelFlags = channelFlagsLazy(node);
438                     isFirstCycle = false;
439                 } else if (compositeOpId != node->compositeOpId() ||
440                            channelFlags != channelFlagsLazy(node)) {
441                     compositionVaries = true;
442                     break;
443                 }
444 
445                 KisLayerSP layer = qobject_cast<KisLayer*>(node.data());
446                 if (layer && layer->layerStyle()) {
447                     compositionVaries = true;
448                     break;
449                 }
450             }
451 
452             if (!compositionVaries) {
453                 if (!compositeOpId.isEmpty()) {
454                     m_info->dstNode->setCompositeOpId(compositeOpId);
455                 }
456                 if (m_info->dstLayer() && !channelFlags.isEmpty()) {
457                     m_info->dstLayer()->setChannelFlags(channelFlags);
458                 }
459             }
460 
461             m_info->nodesCompositingVaries = compositionVaries;
462 
463             m_info->dstNode->setUseInTimeline(m_info->useInTimeline);
464             m_info->dstNode->setColorLabelIndex(m_info->allSrcNodes().first()->colorLabelIndex());
465 
466             dstPaintLayer->setOnionSkinEnabled(m_info->enableOnionSkins);
467         }
468 
469     private:
470         MergeMultipleInfoSP m_info;
471         QString m_name;
472     };
473 
474     struct MergeLayers : public KisCommandUtils::AggregateCommand {
MergeLayersKisLayerUtils::MergeLayers475         MergeLayers(MergeDownInfoSP info) : m_info(info) {}
476 
populateChildCommandsKisLayerUtils::MergeLayers477         void populateChildCommands() override {
478             // actual merging done by KisLayer::createMergedLayer (or specialized descendant)
479             m_info->currLayer->fillMergedLayerTemplate(m_info->dstLayer(), m_info->prevLayer);
480         }
481 
482     private:
483         MergeDownInfoSP m_info;
484     };
485 
486     struct MergeLayersMultiple : public KisCommandUtils::AggregateCommand {
MergeLayersMultipleKisLayerUtils::MergeLayersMultiple487         MergeLayersMultiple(MergeMultipleInfoSP info) : m_info(info) {}
488 
populateChildCommandsKisLayerUtils::MergeLayersMultiple489         void populateChildCommands() override {
490             KisPainter gc(m_info->dstNode->paintDevice());
491 
492             foreach (KisNodeSP node, m_info->allSrcNodes()) {
493                 QRect rc = node->exactBounds() | m_info->image->bounds();
494                 node->projectionPlane()->apply(&gc, rc);
495             }
496         }
497 
498     private:
499         MergeMultipleInfoSP m_info;
500     };
501 
502     struct MergeMetaData : public KUndo2Command {
MergeMetaDataKisLayerUtils::MergeMetaData503         MergeMetaData(MergeDownInfoSP info, const KisMetaData::MergeStrategy* strategy)
504             : m_info(info),
505               m_strategy(strategy) {}
506 
redoKisLayerUtils::MergeMetaData507         void redo() override {
508             QRect layerProjectionExtent = m_info->currLayer->projection()->extent();
509             QRect prevLayerProjectionExtent = m_info->prevLayer->projection()->extent();
510             int prevLayerArea = prevLayerProjectionExtent.width() * prevLayerProjectionExtent.height();
511             int layerArea = layerProjectionExtent.width() * layerProjectionExtent.height();
512 
513             QList<double> scores;
514             double norm = qMax(prevLayerArea, layerArea);
515             scores.append(prevLayerArea / norm);
516             scores.append(layerArea / norm);
517 
518             QList<const KisMetaData::Store*> srcs;
519             srcs.append(m_info->prevLayer->metaData());
520             srcs.append(m_info->currLayer->metaData());
521             m_strategy->merge(m_info->dstLayer()->metaData(), srcs, scores);
522         }
523 
524     private:
525         MergeDownInfoSP m_info;
526         const KisMetaData::MergeStrategy *m_strategy;
527     };
528 
KeepNodesSelectedCommand(const KisNodeList & selectedBefore,const KisNodeList & selectedAfter,KisNodeSP activeBefore,KisNodeSP activeAfter,KisImageSP image,bool finalize,KUndo2Command * parent)529     KeepNodesSelectedCommand::KeepNodesSelectedCommand(const KisNodeList &selectedBefore,
530                                                        const KisNodeList &selectedAfter,
531                                                        KisNodeSP activeBefore,
532                                                        KisNodeSP activeAfter,
533                                                        KisImageSP image,
534                                                        bool finalize, KUndo2Command *parent)
535         : FlipFlopCommand(finalize, parent),
536           m_selectedBefore(selectedBefore),
537           m_selectedAfter(selectedAfter),
538           m_activeBefore(activeBefore),
539           m_activeAfter(activeAfter),
540           m_image(image)
541     {
542     }
543 
partB()544     void KeepNodesSelectedCommand::partB() {
545         KisImageSignalType type;
546         if (getState() == State::FINALIZING) {
547             type = ComplexNodeReselectionSignal(m_activeAfter, m_selectedAfter);
548         } else {
549             type = ComplexNodeReselectionSignal(m_activeBefore, m_selectedBefore);
550         }
551         m_image->signalRouter()->emitNotification(type);
552     }
553 
SelectGlobalSelectionMask(KisImageSP image)554     SelectGlobalSelectionMask::SelectGlobalSelectionMask(KisImageSP image)
555         : m_image(image)
556     {
557     }
558 
redo()559     void SelectGlobalSelectionMask::redo() {
560 
561         KisImageSignalType type =
562                 ComplexNodeReselectionSignal(m_image->rootLayer()->selectionMask(), KisNodeList());
563         m_image->signalRouter()->emitNotification(type);
564 
565     }
566 
~RemoveNodeHelper()567     RemoveNodeHelper::~RemoveNodeHelper()
568     {
569     }
570 
571     /**
572      * The removal of two nodes in one go may be a bit tricky, because one
573      * of them may be the clone of another. If we remove the source of a
574      * clone layer, it will reincarnate into a paint layer. In this case
575      * the pointer to the second layer will be lost.
576      *
577      * That's why we need to care about the order of the nodes removal:
578      * the clone --- first, the source --- last.
579      */
safeRemoveMultipleNodes(KisNodeList nodes,KisImageSP image)580     void RemoveNodeHelper::safeRemoveMultipleNodes(KisNodeList nodes, KisImageSP image) {
581         const bool lastLayer = scanForLastLayer(image, nodes);
582 
583         auto isNodeWeird = [] (KisNodeSP node) {
584             const bool normalCompositeMode = node->compositeOpId() == COMPOSITE_OVER;
585 
586             KisLayer *layer = dynamic_cast<KisLayer*>(node.data());
587             const bool hasInheritAlpha = layer && layer->alphaChannelDisabled();
588             return !normalCompositeMode && !hasInheritAlpha;
589         };
590 
591         while (!nodes.isEmpty()) {
592             KisNodeList::iterator it = nodes.begin();
593 
594             while (it != nodes.end()) {
595                 if (!checkIsSourceForClone(*it, nodes)) {
596                     KisNodeSP node = *it;
597 
598                     addCommandImpl(new KisImageLayerRemoveCommand(image, node, !isNodeWeird(node), true));
599                     it = nodes.erase(it);
600                 } else {
601                     ++it;
602                 }
603             }
604         }
605 
606         if (lastLayer) {
607             KisLayerSP newLayer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace());
608             addCommandImpl(new KisImageLayerAddCommand(image, newLayer,
609                                                        image->root(),
610                                                        KisNodeSP(),
611                                                        false, false));
612         }
613     }
614 
checkIsSourceForClone(KisNodeSP src,const KisNodeList & nodes)615     bool RemoveNodeHelper::checkIsSourceForClone(KisNodeSP src, const KisNodeList &nodes) {
616         foreach (KisNodeSP node, nodes) {
617             if (node == src) continue;
618 
619             KisCloneLayer *clone = dynamic_cast<KisCloneLayer*>(node.data());
620 
621             if (clone && KisNodeSP(clone->copyFrom()) == src) {
622                 return true;
623             }
624         }
625 
626         return false;
627     }
628 
scanForLastLayer(KisImageWSP image,KisNodeList nodesToRemove)629     bool RemoveNodeHelper::scanForLastLayer(KisImageWSP image, KisNodeList nodesToRemove) {
630         bool removeLayers = false;
631         Q_FOREACH(KisNodeSP nodeToRemove, nodesToRemove) {
632             if (qobject_cast<KisLayer*>(nodeToRemove.data())) {
633                 removeLayers = true;
634                 break;
635             }
636         }
637         if (!removeLayers) return false;
638 
639         bool lastLayer = true;
640         KisNodeSP node = image->root()->firstChild();
641         while (node) {
642             if (!nodesToRemove.contains(node) &&
643                 qobject_cast<KisLayer*>(node.data()) &&
644                 !node->isFakeNode()) {
645 
646                 lastLayer = false;
647                 break;
648             }
649             node = node->nextSibling();
650         }
651 
652         return lastLayer;
653     }
654 
SimpleRemoveLayers(const KisNodeList & nodes,KisImageSP image)655     SimpleRemoveLayers::SimpleRemoveLayers(const KisNodeList &nodes,
656                                            KisImageSP image)
657         : m_nodes(nodes),
658           m_image(image)
659     {
660     }
661 
populateChildCommands()662     void SimpleRemoveLayers::populateChildCommands() {
663         if (m_nodes.isEmpty()) return;
664         safeRemoveMultipleNodes(m_nodes, m_image);
665     }
666 
addCommandImpl(KUndo2Command * cmd)667     void SimpleRemoveLayers::addCommandImpl(KUndo2Command *cmd) {
668         addCommand(cmd);
669     }
670 
671     struct InsertNode : public KisCommandUtils::AggregateCommand {
InsertNodeKisLayerUtils::InsertNode672         InsertNode(MergeDownInfoBaseSP info, KisNodeSP putAfter)
673             : m_info(info), m_putAfter(putAfter) {}
674 
populateChildCommandsKisLayerUtils::InsertNode675         void populateChildCommands() override {
676             addCommand(new KisImageLayerAddCommand(m_info->image,
677                                                            m_info->dstNode,
678                                                            m_putAfter->parent(),
679                                                            m_putAfter,
680                                                            true, false));
681 
682         }
683 
684     private:
addCommandImplKisLayerUtils::InsertNode685         virtual void addCommandImpl(KUndo2Command *cmd) {
686             addCommand(cmd);
687         }
688 
689     private:
690         MergeDownInfoBaseSP m_info;
691         KisNodeSP m_putAfter;
692     };
693 
694 
splitNonRemovableNodes(KisNodeList & nodesToRemove,KisNodeList & _nodesToHide)695     void splitNonRemovableNodes(KisNodeList &nodesToRemove, KisNodeList &_nodesToHide)
696     {
697         QSet<KisNodeSP> nodesToHide;
698         QSet<KisNodeSP> extraNodesToRemove;
699 
700         for (auto it = nodesToRemove.begin(); it != nodesToRemove.end(); ++it) {
701             KisNodeSP root = *it;
702             KIS_SAFE_ASSERT_RECOVER_NOOP(root->visible());
703 
704             if (!root->isEditable(false)) {
705                 nodesToHide.insert(root);
706             } else {
707                 bool rootNeedsCarefulRemoval = false;
708 
709                 recursiveApplyNodes(root,
710                                     [root, &nodesToHide, &rootNeedsCarefulRemoval] (KisNodeSP node) {
711                                         if (!node->isEditable(false)) {
712                                             while (node != root) {
713                                                 nodesToHide.insert(node);
714                                                 node = node->parent();
715                                                 KIS_SAFE_ASSERT_RECOVER_BREAK(node);
716                                             }
717                                             nodesToHide.insert(root);
718                                             rootNeedsCarefulRemoval = true;
719                                         }
720                                     });
721 
722                 if (rootNeedsCarefulRemoval) {
723                     recursiveApplyNodes(root,
724                                         [&extraNodesToRemove] (KisNodeSP node) {
725                                             extraNodesToRemove.insert(node);
726                                         });
727                 }
728             }
729         }
730 
731         nodesToRemove += extraNodesToRemove.toList();
732 
733         KritaUtils::filterContainer<KisNodeList>(nodesToRemove,
734                                                  [nodesToHide](KisNodeSP node) {
735                                                      return !nodesToHide.contains(node);
736                                                  });
737 
738         _nodesToHide = nodesToHide.toList();
739     }
740 
741     struct CleanUpNodes : private RemoveNodeHelper, public KisCommandUtils::AggregateCommand {
CleanUpNodesKisLayerUtils::CleanUpNodes742         CleanUpNodes(MergeDownInfoBaseSP info, KisNodeSP putAfter)
743             : m_info(info), m_putAfter(putAfter) {}
744 
findPerfectParentKisLayerUtils::CleanUpNodes745         static void findPerfectParent(KisNodeList nodesToDelete, KisNodeSP &putAfter, KisNodeSP &parent) {
746             if (!putAfter) {
747                 putAfter = nodesToDelete.last();
748             }
749 
750             // Add the new merged node on top of the active node
751             // -- checking all parents if they are included in nodesToDelete
752             // Not every descendant is included in nodesToDelete even if in fact
753             //   they are going to be deleted, so we need to check it.
754             // If we consider the path from root to the putAfter node,
755             //    if there are any nodes marked for deletion, any node afterwards
756             //    is going to be deleted, too.
757             //   example:      root . . . . . ! ! . . ! ! ! ! . . . . putAfter
758             //   it should be: root . . . . . ! ! ! ! ! ! ! ! ! ! ! ! !putAfter
759             //   and here:     root . . . . X ! ! . . ! ! ! ! . . . . putAfter
760             //   you can see which node is "the perfect ancestor"
761             //   (marked X; called "parent" in the function arguments).
762             //   and here:     root . . . . . O ! . . ! ! ! ! . . . . putAfter
763             //   you can see which node is "the topmost deleted ancestor" (marked 'O')
764 
765             KisNodeSP node = putAfter->parent();
766             bool foundDeletedAncestor = false;
767             KisNodeSP topmostAncestorToDelete = nullptr;
768 
769             while (node) {
770 
771                 if (nodesToDelete.contains(node)
772                         && !nodesToDelete.contains(node->parent())) {
773                     foundDeletedAncestor = true;
774                     topmostAncestorToDelete = node;
775                     // Here node is to be deleted and its parent is not,
776                     // so its parent is the one of the first not deleted (="perfect") ancestors.
777                     // We need the one that is closest to the top (root)
778                 }
779 
780                 node = node->parent();
781             }
782 
783             if (foundDeletedAncestor) {
784                 parent = topmostAncestorToDelete->parent();
785                 putAfter = topmostAncestorToDelete;
786             }
787             else {
788                 parent = putAfter->parent(); // putAfter (and none of its ancestors) is to be deleted, so its parent is the first not deleted ancestor
789             }
790 
791         }
792 
populateChildCommandsKisLayerUtils::CleanUpNodes793         void populateChildCommands() override {
794             KisNodeList nodesToDelete = m_info->allSrcNodes();
795 
796             KisNodeSP parent;
797             findPerfectParent(nodesToDelete, m_putAfter, parent);
798 
799             if (!parent) {
800                 KisNodeSP oldRoot = m_info->image->root();
801                 KisNodeSP newRoot(new KisGroupLayer(m_info->image, "root", OPACITY_OPAQUE_U8));
802 
803                 // copy all fake nodes into the new image
804                 KisLayerUtils::recursiveApplyNodes(oldRoot, [this, oldRoot, newRoot] (KisNodeSP node) {
805                     if (node->isFakeNode() && node->parent() == oldRoot) {
806                         addCommand(new KisImageLayerAddCommand(m_info->image,
807                                                                node->clone(),
808                                                                newRoot,
809                                                                KisNodeSP(),
810                                                                false, false));
811 
812                     }
813                 });
814 
815                 addCommand(new KisImageLayerAddCommand(m_info->image,
816                                                        m_info->dstNode,
817                                                        newRoot,
818                                                        KisNodeSP(),
819                                                        true, false));
820                 addCommand(new KisImageChangeLayersCommand(m_info->image, oldRoot, newRoot));
821 
822             }
823             else {
824                 addCommand(new KisImageLayerAddCommand(m_info->image,
825                                                        m_info->dstNode,
826                                                        parent,
827                                                        m_putAfter,
828                                                        true, false));
829 
830 
831                 /**
832                  * We can merge selection masks, in this case dstLayer is not defined!
833                  */
834                 if (m_info->dstLayer()) {
835                     reparentSelectionMasks(m_info->image,
836                                            m_info->dstLayer(),
837                                            m_info->selectionMasks);
838                 }
839 
840                 KisNodeList safeNodesToDelete = m_info->allSrcNodes();
841                 KisNodeList safeNodesToHide;
842 
843                 splitNonRemovableNodes(safeNodesToDelete, safeNodesToHide);
844 
845                 Q_FOREACH(KisNodeSP node, safeNodesToHide) {
846                     addCommand(new KisImageChangeVisibilityCommand(false, node));
847                 }
848 
849                 safeRemoveMultipleNodes(safeNodesToDelete, m_info->image);
850             }
851 
852 
853         }
854 
855     private:
addCommandImplKisLayerUtils::CleanUpNodes856         void addCommandImpl(KUndo2Command *cmd) override {
857             addCommand(cmd);
858         }
859 
reparentSelectionMasksKisLayerUtils::CleanUpNodes860         void reparentSelectionMasks(KisImageSP image,
861                                     KisLayerSP newLayer,
862                                     const QVector<KisSelectionMaskSP> &selectionMasks) {
863 
864             KIS_SAFE_ASSERT_RECOVER_RETURN(newLayer);
865 
866             foreach (KisSelectionMaskSP mask, selectionMasks) {
867                 addCommand(new KisImageLayerMoveCommand(image, mask, newLayer, newLayer->lastChild()));
868                 addCommand(new KisActivateSelectionMaskCommand(mask, false));
869             }
870         }
871     private:
872         MergeDownInfoBaseSP m_info;
873         KisNodeSP m_putAfter;
874     };
875 
~SharedStorage()876     SwitchFrameCommand::SharedStorage::~SharedStorage() {
877     }
878 
SwitchFrameCommand(KisImageSP image,int time,bool finalize,SharedStorageSP storage)879     SwitchFrameCommand::SwitchFrameCommand(KisImageSP image, int time, bool finalize, SharedStorageSP storage)
880         : FlipFlopCommand(finalize),
881           m_image(image),
882           m_newTime(time),
883           m_storage(storage) {}
884 
~SwitchFrameCommand()885     SwitchFrameCommand::~SwitchFrameCommand() {}
886 
partA()887     void SwitchFrameCommand::partA() {
888         KisImageAnimationInterface *interface = m_image->animationInterface();
889         const int currentTime = interface->currentTime();
890         if (currentTime == m_newTime) {
891             m_storage->value = m_newTime;
892             return;
893         }
894 
895         interface->image()->disableUIUpdates();
896         interface->saveAndResetCurrentTime(m_newTime, &m_storage->value);
897     }
898 
partB()899     void SwitchFrameCommand::partB() {
900         KisImageAnimationInterface *interface = m_image->animationInterface();
901         const int currentTime = interface->currentTime();
902         if (currentTime == m_storage->value) {
903             return;
904         }
905 
906         interface->restoreCurrentTime(&m_storage->value);
907         interface->image()->enableUIUpdates();
908     }
909 
910     struct AddNewFrame : public KisCommandUtils::AggregateCommand {
AddNewFrameKisLayerUtils::AddNewFrame911         AddNewFrame(MergeDownInfoBaseSP info, int frame) : m_info(info), m_frame(frame) {}
912 
populateChildCommandsKisLayerUtils::AddNewFrame913         void populateChildCommands() override {
914             KUndo2Command *cmd = new KisCommandUtils::SkipFirstRedoWrapper();
915             KisKeyframeChannel *channel = m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id());
916             KisKeyframeSP keyframe = channel->addKeyframe(m_frame, cmd);
917 
918             applyKeyframeColorLabel(keyframe);
919 
920             addCommand(cmd);
921         }
922 
applyKeyframeColorLabelKisLayerUtils::AddNewFrame923         void applyKeyframeColorLabel(KisKeyframeSP dstKeyframe) {
924             Q_FOREACH(KisNodeSP srcNode, m_info->allSrcNodes()) {
925                 Q_FOREACH(KisKeyframeChannel *channel, srcNode->keyframeChannels().values()) {
926                     KisKeyframeSP keyframe = channel->keyframeAt(m_frame);
927                     if (!keyframe.isNull() && keyframe->colorLabel() != 0) {
928                         dstKeyframe->setColorLabel(keyframe->colorLabel());
929                         return;
930                     }
931                 }
932             }
933 
934             dstKeyframe->setColorLabel(0);
935         }
936 
937     private:
938         MergeDownInfoBaseSP m_info;
939         int m_frame;
940     };
941 
fetchLayerFrames(KisNodeSP node)942     QSet<int> fetchLayerFrames(KisNodeSP node) {
943         KisKeyframeChannel *channel = node->getKeyframeChannel(KisKeyframeChannel::Content.id());
944         if (!channel) return QSet<int>();
945 
946         return channel->allKeyframeIds();
947     }
948 
fetchLayerFramesRecursive(KisNodeSP rootNode)949     QSet<int> fetchLayerFramesRecursive(KisNodeSP rootNode) {
950         if (!rootNode->visible()) return QSet<int>();
951 
952         QSet<int> frames = fetchLayerFrames(rootNode);
953 
954         KisNodeSP node = rootNode->firstChild();
955         while(node) {
956             frames |= fetchLayerFramesRecursive(node);
957             node = node->nextSibling();
958         }
959 
960         return frames;
961     }
962 
updateFrameJobs(FrameJobs * jobs,KisNodeSP node)963     void updateFrameJobs(FrameJobs *jobs, KisNodeSP node) {
964         QSet<int> frames = fetchLayerFrames(node);
965 
966         if (frames.isEmpty()) {
967             (*jobs)[0].insert(node);
968         } else {
969             foreach (int frame, frames) {
970                 (*jobs)[frame].insert(node);
971             }
972         }
973     }
974 
updateFrameJobsRecursive(FrameJobs * jobs,KisNodeSP rootNode)975     void updateFrameJobsRecursive(FrameJobs *jobs, KisNodeSP rootNode) {
976         updateFrameJobs(jobs, rootNode);
977 
978         KisNodeSP node = rootNode->firstChild();
979         while(node) {
980             updateFrameJobsRecursive(jobs, node);
981             node = node->nextSibling();
982         }
983     }
984 
985     /**
986      * \see a comment in mergeMultipleLayersImpl()
987      */
mergeDown(KisImageSP image,KisLayerSP layer,const KisMetaData::MergeStrategy * strategy)988     void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy* strategy)
989     {
990         if (!layer->prevSibling()) return;
991 
992         // XXX: this breaks if we allow free mixing of masks and layers
993         KisLayerSP prevLayer = qobject_cast<KisLayer*>(layer->prevSibling().data());
994         if (!prevLayer) return;
995 
996         if (!layer->visible() && !prevLayer->visible()) {
997             return;
998         }
999 
1000         KisImageSignalVector emitSignals;
1001         emitSignals << ModifiedSignal;
1002 
1003         KisProcessingApplicator applicator(image, 0,
1004                                            KisProcessingApplicator::NONE,
1005                                            emitSignals,
1006                                            kundo2_i18n("Merge Down"));
1007 
1008         if (layer->visible() && prevLayer->visible()) {
1009             MergeDownInfoSP info(new MergeDownInfo(image, prevLayer, layer));
1010 
1011             // disable key strokes on all colorize masks, all onion skins on
1012             // paint layers and wait until update is finished with a barrier
1013             applicator.applyCommand(new DisableColorizeKeyStrokes(info));
1014             applicator.applyCommand(new DisableOnionSkins(info));
1015             applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER);
1016 
1017             applicator.applyCommand(new KeepMergedNodesSelected(info, false));
1018             applicator.applyCommand(new FillSelectionMasks(info));
1019             applicator.applyCommand(new CreateMergedLayer(info), KisStrokeJobData::BARRIER);
1020 
1021             // NOTE: shape layer may have emitted spontaneous jobs during layer creation,
1022             //       wait for them to complete!
1023             applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER);
1024             applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER);
1025 
1026             // in two-layer mode we disable pass through only when the destination layer
1027             // is not a group layer
1028             applicator.applyCommand(new DisablePassThroughForHeadsOnly(info, true));
1029             applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER);
1030 
1031             if (info->frames.size() > 0) {
1032                 foreach (int frame, info->frames) {
1033                     applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
1034 
1035                     applicator.applyCommand(new AddNewFrame(info, frame));
1036                     applicator.applyCommand(new RefreshHiddenAreas(info));
1037                     applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER);
1038                     applicator.applyCommand(new MergeLayers(info), KisStrokeJobData::BARRIER);
1039 
1040                     applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
1041                 }
1042             } else {
1043                 applicator.applyCommand(new RefreshHiddenAreas(info));
1044                 applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER);
1045                 applicator.applyCommand(new MergeLayers(info), KisStrokeJobData::BARRIER);
1046             }
1047 
1048             applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
1049             applicator.applyCommand(new CleanUpNodes(info, layer),
1050                                     KisStrokeJobData::SEQUENTIAL,
1051                                     KisStrokeJobData::EXCLUSIVE);
1052             applicator.applyCommand(new KeepMergedNodesSelected(info, true));
1053         } else if (layer->visible()) {
1054             applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(),
1055                                                                  layer, KisNodeSP(),
1056                                                                  image, false));
1057 
1058             applicator.applyCommand(
1059                 new SimpleRemoveLayers(KisNodeList() << prevLayer,
1060                                        image),
1061                 KisStrokeJobData::SEQUENTIAL,
1062                 KisStrokeJobData::EXCLUSIVE);
1063 
1064             applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(),
1065                                                                  KisNodeSP(), layer,
1066                                                                  image, true));
1067         } else if (prevLayer->visible()) {
1068             applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(),
1069                                                                  layer, KisNodeSP(),
1070                                                                  image, false));
1071 
1072             applicator.applyCommand(
1073                 new SimpleRemoveLayers(KisNodeList() << layer,
1074                                        image),
1075                 KisStrokeJobData::SEQUENTIAL,
1076                 KisStrokeJobData::EXCLUSIVE);
1077 
1078             applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(),
1079                                                                  KisNodeSP(), prevLayer,
1080                                                                  image, true));
1081         }
1082 
1083         applicator.end();
1084     }
1085 
checkIsChildOf(KisNodeSP node,const KisNodeList & parents)1086     bool checkIsChildOf(KisNodeSP node, const KisNodeList &parents)
1087     {
1088         KisNodeList nodeParents;
1089 
1090         KisNodeSP parent = node->parent();
1091         while (parent) {
1092             nodeParents << parent;
1093             parent = parent->parent();
1094         }
1095 
1096         foreach(KisNodeSP perspectiveParent, parents) {
1097             if (nodeParents.contains(perspectiveParent)) {
1098                 return true;
1099             }
1100         }
1101 
1102         return false;
1103     }
1104 
checkIsCloneOf(KisNodeSP node,const KisNodeList & nodes)1105     bool checkIsCloneOf(KisNodeSP node, const KisNodeList &nodes)
1106     {
1107         bool result = false;
1108 
1109         KisCloneLayer *clone = dynamic_cast<KisCloneLayer*>(node.data());
1110         if (clone) {
1111             KisNodeSP cloneSource = KisNodeSP(clone->copyFrom());
1112 
1113             Q_FOREACH(KisNodeSP subtree, nodes) {
1114                 result =
1115                     recursiveFindNode(subtree,
1116                                       [cloneSource](KisNodeSP node) -> bool
1117                                       {
1118                                           return node == cloneSource;
1119                                       });
1120 
1121                 if (!result) {
1122                     result = checkIsCloneOf(cloneSource, nodes);
1123                 }
1124 
1125                 if (result) {
1126                     break;
1127                 }
1128             }
1129         }
1130 
1131         return result;
1132     }
1133 
filterMergableNodes(KisNodeList & nodes,bool allowMasks)1134     void filterMergableNodes(KisNodeList &nodes, bool allowMasks)
1135     {
1136         KisNodeList::iterator it = nodes.begin();
1137 
1138         while (it != nodes.end()) {
1139             if ((!allowMasks && !qobject_cast<KisLayer*>(it->data())) ||
1140                 checkIsChildOf(*it, nodes)) {
1141                 //qDebug() << "Skipping node" << ppVar((*it)->name());
1142                 it = nodes.erase(it);
1143             } else {
1144                 ++it;
1145             }
1146         }
1147     }
1148 
sortMergableNodes(KisNodeSP root,KisNodeList & inputNodes,KisNodeList & outputNodes)1149     void sortMergableNodes(KisNodeSP root, KisNodeList &inputNodes, KisNodeList &outputNodes)
1150     {
1151         KisNodeList::iterator it = std::find(inputNodes.begin(), inputNodes.end(), root);
1152 
1153         if (it != inputNodes.end()) {
1154             outputNodes << *it;
1155             inputNodes.erase(it);
1156         }
1157 
1158         if (inputNodes.isEmpty()) {
1159             return;
1160         }
1161 
1162         KisNodeSP child = root->firstChild();
1163         while (child) {
1164             sortMergableNodes(child, inputNodes, outputNodes);
1165             child = child->nextSibling();
1166         }
1167 
1168         /**
1169          * By the end of recursion \p inputNodes must be empty
1170          */
1171         KIS_ASSERT_RECOVER_NOOP(root->parent() || inputNodes.isEmpty());
1172     }
1173 
sortMergableNodes(KisNodeSP root,KisNodeList nodes)1174     KisNodeList sortMergableNodes(KisNodeSP root, KisNodeList nodes)
1175     {
1176         KisNodeList result;
1177         sortMergableNodes(root, nodes, result);
1178         return result;
1179     }
1180 
sortAndFilterMergableInternalNodes(KisNodeList nodes,bool allowMasks)1181     KisNodeList sortAndFilterMergableInternalNodes(KisNodeList nodes, bool allowMasks)
1182     {
1183         KIS_ASSERT_RECOVER(!nodes.isEmpty()) { return nodes; }
1184 
1185         KisNodeSP root;
1186         Q_FOREACH(KisNodeSP node, nodes) {
1187             KisNodeSP localRoot = node;
1188             while (localRoot->parent()) {
1189                 localRoot = localRoot->parent();
1190             }
1191 
1192             if (!root) {
1193                 root = localRoot;
1194             }
1195             KIS_ASSERT_RECOVER(root == localRoot) { return nodes; }
1196         }
1197 
1198         KisNodeList result;
1199         sortMergableNodes(root, nodes, result);
1200         filterMergableNodes(result, allowMasks);
1201         return result;
1202     }
1203 
sortAndFilterAnyMergableNodesSafe(const KisNodeList & nodes,KisImageSP image)1204     KisNodeList sortAndFilterAnyMergableNodesSafe(const KisNodeList &nodes, KisImageSP image) {
1205         KisNodeList filteredNodes = nodes;
1206         KisNodeList sortedNodes;
1207 
1208         KisLayerUtils::filterMergableNodes(filteredNodes, true);
1209 
1210         bool haveExternalNodes = false;
1211         Q_FOREACH (KisNodeSP node, nodes) {
1212             if (node->graphListener() != image->root()->graphListener()) {
1213                 haveExternalNodes = true;
1214                 break;
1215             }
1216         }
1217 
1218         if (!haveExternalNodes) {
1219             KisLayerUtils::sortMergableNodes(image->root(), filteredNodes, sortedNodes);
1220         } else {
1221             sortedNodes = filteredNodes;
1222         }
1223 
1224         return sortedNodes;
1225     }
1226 
1227 
addCopyOfNameTag(KisNodeSP node)1228     void addCopyOfNameTag(KisNodeSP node)
1229     {
1230         const QString prefix = i18n("Copy of");
1231         QString newName = node->name();
1232         if (!newName.startsWith(prefix)) {
1233             newName = QString("%1 %2").arg(prefix).arg(newName);
1234             node->setName(newName);
1235         }
1236     }
1237 
findNodesWithProps(KisNodeSP root,const KoProperties & props,bool excludeRoot)1238     KisNodeList findNodesWithProps(KisNodeSP root, const KoProperties &props, bool excludeRoot)
1239     {
1240         KisNodeList nodes;
1241 
1242         if ((!excludeRoot || root->parent()) && root->check(props)) {
1243             nodes << root;
1244         }
1245 
1246         KisNodeSP node = root->firstChild();
1247         while (node) {
1248             nodes += findNodesWithProps(node, props, excludeRoot);
1249             node = node->nextSibling();
1250         }
1251 
1252         return nodes;
1253     }
1254 
filterInvisibleNodes(const KisNodeList & nodes,KisNodeList * invisibleNodes,KisNodeSP * putAfter)1255     KisNodeList filterInvisibleNodes(const KisNodeList &nodes, KisNodeList *invisibleNodes, KisNodeSP *putAfter)
1256     {
1257         KIS_ASSERT_RECOVER(invisibleNodes) { return nodes; }
1258         KIS_ASSERT_RECOVER(putAfter) { return nodes; }
1259 
1260         KisNodeList visibleNodes;
1261         int putAfterIndex = -1;
1262 
1263         Q_FOREACH(KisNodeSP node, nodes) {
1264             if (node->visible() || node->userLocked()) {
1265                 visibleNodes << node;
1266             } else {
1267                 *invisibleNodes << node;
1268 
1269                 if (node == *putAfter) {
1270                     putAfterIndex = visibleNodes.size() - 1;
1271                 }
1272             }
1273         }
1274 
1275         if (!visibleNodes.isEmpty() && putAfterIndex >= 0) {
1276             putAfterIndex = qBound(0, putAfterIndex, visibleNodes.size() - 1);
1277             *putAfter = visibleNodes[putAfterIndex];
1278         }
1279 
1280         return visibleNodes;
1281     }
1282 
filterUnlockedNodes(KisNodeList & nodes)1283     void filterUnlockedNodes(KisNodeList &nodes)
1284     {
1285         KisNodeList::iterator it = nodes.begin();
1286 
1287         while (it != nodes.end()) {
1288             if ((*it)->userLocked()) {
1289                 it = nodes.erase(it);
1290             } else {
1291                 ++it;
1292             }
1293         }
1294     }
1295 
changeImageDefaultProjectionColor(KisImageSP image,const KoColor & color)1296     void changeImageDefaultProjectionColor(KisImageSP image, const KoColor &color)
1297     {
1298         KisImageSignalVector emitSignals;
1299         emitSignals << ModifiedSignal;
1300 
1301         KisProcessingApplicator applicator(image,
1302                                            image->root(),
1303                                            KisProcessingApplicator::RECURSIVE,
1304                                            emitSignals,
1305                                            kundo2_i18n("Change projection color"),
1306                                            0,
1307                                            142857 + 1);
1308         applicator.applyCommand(new KisChangeProjectionColorCommand(image, color), KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE);
1309         applicator.end();
1310     }
1311 
1312     /**
1313      * There might be two approaches for merging multiple layers:
1314      *
1315      * 1) Consider the selected nodes as a distinct "group" and merge them
1316      *    as if they were isolated from the rest of the image. The key point
1317      *    of this approach is that the look of the image will change, when
1318      *    merging "weird" layers, like adjustment layers or layers with
1319      *    non-normal blending mode.
1320      *
1321      * 2) Merge layers in a way to keep the look of the image as unchanged as
1322      *    possible. With this approach one uses a few heuristics:
1323      *
1324      *       * when merging multiple layers with non-normal (but equal) blending
1325      *         mode, first merge these layers together using Normal blending mode,
1326      *         then set blending mode of the result to the original blending mode
1327      *
1328      *       * when merging multiple layers with different blending modes or
1329      *         layer styles, they are first rasterized, and then laid over each
1330      *         other with their own composite op. The blending mode of the final
1331      *         layer is set to Normal, so the user could clearly see that he should
1332      *         choose the correct blending mode.
1333      *
1334      * Krita uses the second approach: after merge operation, the image should look
1335      * as if nothing has happened (if it is technically possible).
1336      */
mergeMultipleLayersImpl(KisImageSP image,KisNodeList mergedNodes,KisNodeSP putAfter,bool flattenSingleLayer,const KUndo2MagicString & actionName,bool cleanupNodes=true,const QString layerName=QString ())1337     void mergeMultipleLayersImpl(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter,
1338                                            bool flattenSingleLayer, const KUndo2MagicString &actionName,
1339                                            bool cleanupNodes = true, const QString layerName = QString())
1340     {
1341         if (!putAfter) {
1342             putAfter = mergedNodes.first();
1343         }
1344 
1345         filterMergableNodes(mergedNodes);
1346         {
1347             KisNodeList tempNodes;
1348             std::swap(mergedNodes, tempNodes);
1349             sortMergableNodes(image->root(), tempNodes, mergedNodes);
1350         }
1351 
1352         if (mergedNodes.size() <= 1 &&
1353             (!flattenSingleLayer && mergedNodes.size() == 1)) return;
1354 
1355         KisImageSignalVector emitSignals;
1356         emitSignals << ModifiedSignal;
1357         emitSignals << ComplexNodeReselectionSignal(KisNodeSP(), KisNodeList(), KisNodeSP(), mergedNodes);
1358 
1359 
1360 
1361         KisNodeList originalNodes = mergedNodes;
1362         KisNodeList invisibleNodes;
1363         mergedNodes = filterInvisibleNodes(originalNodes, &invisibleNodes, &putAfter);
1364 
1365         if (mergedNodes.isEmpty()) return;
1366 
1367 
1368         // make sure we don't add the new layer into a locked group
1369         KIS_SAFE_ASSERT_RECOVER_RETURN(putAfter->parent());
1370         while (putAfter->parent() && !putAfter->parent()->isEditable()) {
1371             putAfter = putAfter->parent();
1372         }
1373 
1374         /**
1375          * We have reached the root of the layer hierarchy and didn't manage
1376          * to find a node that was editable enough for putting our merged
1377          * result into it. That whouldn't happen in normal circumstances,
1378          * unless the user chose to make the root layer visible and lock
1379          * it manually.
1380          */
1381         if (!putAfter->parent()) {
1382             return;
1383         }
1384 
1385         KisProcessingApplicator applicator(image, 0,
1386                                            KisProcessingApplicator::NONE,
1387                                            emitSignals,
1388                                            actionName);
1389 
1390 
1391         if (!invisibleNodes.isEmpty() && cleanupNodes) {
1392 
1393             /* If the putAfter node is invisible,
1394              * we should instead pick one of the nodes
1395              * to be merged to avoid a null putAfter
1396              * after we remove all invisible layers from
1397              * the image.
1398              * (The assumption is that putAfter is among
1399              * the layers to merge, so if it's invisible,
1400              * it's going to be removed)
1401              */
1402             if (!putAfter->visible()){
1403                 putAfter = mergedNodes.first();
1404             }
1405 
1406             applicator.applyCommand(
1407                 new SimpleRemoveLayers(invisibleNodes,
1408                                        image),
1409                 KisStrokeJobData::SEQUENTIAL,
1410                 KisStrokeJobData::EXCLUSIVE);
1411         }
1412 
1413         if (mergedNodes.size() > 1 || invisibleNodes.isEmpty()) {
1414             MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes));
1415 
1416             // disable key strokes on all colorize masks, all onion skins on
1417             // paint layers and wait until update is finished with a barrier
1418             applicator.applyCommand(new DisableColorizeKeyStrokes(info));
1419             applicator.applyCommand(new DisableOnionSkins(info));
1420             applicator.applyCommand(new DisablePassThroughForHeadsOnly(info));
1421             applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER);
1422 
1423             applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, false));
1424             applicator.applyCommand(new FillSelectionMasks(info));
1425             applicator.applyCommand(new CreateMergedLayerMultiple(info, layerName), KisStrokeJobData::BARRIER);
1426             applicator.applyCommand(new DisableExtraCompositing(info));
1427             applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER);
1428 
1429             if (!info->frames.isEmpty()) {
1430                 foreach (int frame, info->frames) {
1431                     applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
1432 
1433                     applicator.applyCommand(new AddNewFrame(info, frame));
1434                     applicator.applyCommand(new RefreshHiddenAreas(info));
1435                     applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER);
1436                     applicator.applyCommand(new MergeLayersMultiple(info), KisStrokeJobData::BARRIER);
1437 
1438                     applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
1439                 }
1440             } else {
1441                 applicator.applyCommand(new RefreshHiddenAreas(info));
1442                 applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER);
1443                 applicator.applyCommand(new MergeLayersMultiple(info), KisStrokeJobData::BARRIER);
1444             }
1445 
1446             //applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
1447             if (cleanupNodes){
1448                 applicator.applyCommand(new CleanUpNodes(info, putAfter),
1449                                             KisStrokeJobData::SEQUENTIAL,
1450                                         KisStrokeJobData::EXCLUSIVE);
1451             } else {
1452                 applicator.applyCommand(new InsertNode(info, putAfter),
1453                                             KisStrokeJobData::SEQUENTIAL,
1454                                         KisStrokeJobData::EXCLUSIVE);
1455             }
1456 
1457             applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, true));
1458         }
1459 
1460         applicator.end();
1461 
1462     }
1463 
mergeMultipleLayers(KisImageSP image,KisNodeList mergedNodes,KisNodeSP putAfter)1464     void mergeMultipleLayers(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter)
1465     {
1466         mergeMultipleLayersImpl(image, mergedNodes, putAfter, false, kundo2_i18n("Merge Selected Nodes"));
1467     }
1468 
newLayerFromVisible(KisImageSP image,KisNodeSP putAfter)1469     void newLayerFromVisible(KisImageSP image, KisNodeSP putAfter)
1470     {
1471         KisNodeList mergedNodes;
1472         mergedNodes << image->root();
1473 
1474         mergeMultipleLayersImpl(image, mergedNodes, putAfter, true, kundo2_i18n("New From Visible"), false, i18nc("New layer created from all the visible layers", "Visible"));
1475     }
1476 
1477     struct MergeSelectionMasks : public KisCommandUtils::AggregateCommand {
MergeSelectionMasksKisLayerUtils::MergeSelectionMasks1478         MergeSelectionMasks(MergeDownInfoBaseSP info, KisNodeSP putAfter)
1479             : m_info(info),
1480               m_putAfter(putAfter){}
1481 
populateChildCommandsKisLayerUtils::MergeSelectionMasks1482         void populateChildCommands() override {
1483             KisNodeSP parent;
1484             CleanUpNodes::findPerfectParent(m_info->allSrcNodes(), m_putAfter, parent);
1485 
1486             KisLayerSP parentLayer;
1487             do {
1488                 parentLayer = qobject_cast<KisLayer*>(parent.data());
1489 
1490                 parent = parent->parent();
1491             } while(!parentLayer && parent);
1492 
1493             KisSelectionSP selection = new KisSelection();
1494 
1495             foreach (KisNodeSP node, m_info->allSrcNodes()) {
1496                 KisMaskSP mask = dynamic_cast<KisMask*>(node.data());
1497                 if (!mask) continue;
1498 
1499                 selection->pixelSelection()->applySelection(
1500                     mask->selection()->pixelSelection(), SELECTION_ADD);
1501             }
1502 
1503             KisSelectionMaskSP mergedMask = new KisSelectionMask(m_info->image, i18n("Selection Mask"));
1504             mergedMask->initSelection(parentLayer);
1505             mergedMask->setSelection(selection);
1506 
1507             m_info->dstNode = mergedMask;
1508         }
1509 
1510     private:
1511         MergeDownInfoBaseSP m_info;
1512         KisNodeSP m_putAfter;
1513     };
1514 
1515     struct ActivateSelectionMask : public KisCommandUtils::AggregateCommand {
ActivateSelectionMaskKisLayerUtils::ActivateSelectionMask1516         ActivateSelectionMask(MergeDownInfoBaseSP info)
1517             : m_info(info) {}
1518 
populateChildCommandsKisLayerUtils::ActivateSelectionMask1519         void populateChildCommands() override {
1520             KisSelectionMaskSP mergedMask = dynamic_cast<KisSelectionMask*>(m_info->dstNode.data());
1521             addCommand(new KisActivateSelectionMaskCommand(mergedMask, true));
1522         }
1523 
1524     private:
1525         MergeDownInfoBaseSP m_info;
1526     };
1527 
tryMergeSelectionMasks(KisImageSP image,KisNodeList mergedNodes,KisNodeSP putAfter)1528     bool tryMergeSelectionMasks(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter)
1529     {
1530         QList<KisSelectionMaskSP> selectionMasks;
1531 
1532         for (auto it = mergedNodes.begin(); it != mergedNodes.end(); /*noop*/) {
1533             KisSelectionMaskSP mask = dynamic_cast<KisSelectionMask*>(it->data());
1534             if (!mask) {
1535                 it = mergedNodes.erase(it);
1536             } else {
1537                 selectionMasks.append(mask);
1538                 ++it;
1539             }
1540         }
1541 
1542         if (mergedNodes.isEmpty()) return false;
1543 
1544         KisLayerSP parentLayer = qobject_cast<KisLayer*>(selectionMasks.first()->parent().data());
1545         KIS_ASSERT_RECOVER(parentLayer) { return 0; }
1546 
1547         KisImageSignalVector emitSignals;
1548         emitSignals << ModifiedSignal;
1549 
1550         KisProcessingApplicator applicator(image, 0,
1551                                            KisProcessingApplicator::NONE,
1552                                            emitSignals,
1553                                            kundo2_i18n("Merge Selection Masks"));
1554 
1555         MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes));
1556 
1557 
1558         applicator.applyCommand(new MergeSelectionMasks(info, putAfter));
1559         applicator.applyCommand(new CleanUpNodes(info, putAfter),
1560                                 KisStrokeJobData::SEQUENTIAL,
1561                                 KisStrokeJobData::EXCLUSIVE);
1562         applicator.applyCommand(new ActivateSelectionMask(info));
1563         applicator.end();
1564 
1565         return true;
1566     }
1567 
flattenLayer(KisImageSP image,KisLayerSP layer)1568     void flattenLayer(KisImageSP image, KisLayerSP layer)
1569     {
1570         if (!layer->childCount() && !layer->layerStyle())
1571             return;
1572 
1573         KisNodeList mergedNodes;
1574         mergedNodes << layer;
1575 
1576         mergeMultipleLayersImpl(image, mergedNodes, layer, true, kundo2_i18n("Flatten Layer"));
1577     }
1578 
flattenImage(KisImageSP image,KisNodeSP activeNode)1579     void flattenImage(KisImageSP image, KisNodeSP activeNode)
1580     {
1581         if (!activeNode) {
1582             activeNode = image->root()->lastChild();
1583         }
1584 
1585 
1586         KisNodeList mergedNodes;
1587         mergedNodes << image->root();
1588 
1589         mergeMultipleLayersImpl(image, mergedNodes, activeNode, true, kundo2_i18n("Flatten Image"));
1590     }
1591 
KisSimpleUpdateCommand(KisNodeList nodes,bool finalize,KUndo2Command * parent)1592     KisSimpleUpdateCommand::KisSimpleUpdateCommand(KisNodeList nodes, bool finalize, KUndo2Command *parent)
1593         : FlipFlopCommand(finalize, parent),
1594           m_nodes(nodes)
1595     {
1596     }
partB()1597     void KisSimpleUpdateCommand::partB()
1598     {
1599         updateNodes(m_nodes);
1600     }
updateNodes(const KisNodeList & nodes)1601     void KisSimpleUpdateCommand::updateNodes(const KisNodeList &nodes)
1602     {
1603         Q_FOREACH(KisNodeSP node, nodes) {
1604             node->setDirty(node->extent());
1605         }
1606     }
1607 
recursiveFindNode(KisNodeSP node,std::function<bool (KisNodeSP)> func)1608     KisNodeSP recursiveFindNode(KisNodeSP node, std::function<bool(KisNodeSP)> func)
1609     {
1610         if (func(node)) {
1611             return node;
1612         }
1613 
1614         node = node->firstChild();
1615         while (node) {
1616             KisNodeSP resultNode = recursiveFindNode(node, func);
1617             if (resultNode) {
1618                 return resultNode;
1619             }
1620             node = node->nextSibling();
1621         }
1622 
1623         return 0;
1624     }
1625 
findNodeByUuid(KisNodeSP root,const QUuid & uuid)1626     KisNodeSP findNodeByUuid(KisNodeSP root, const QUuid &uuid)
1627     {
1628         return recursiveFindNode(root,
1629             [uuid] (KisNodeSP node) {
1630                 return node->uuid() == uuid;
1631         });
1632     }
1633 
forceAllDelayedNodesUpdate(KisNodeSP root)1634     void forceAllDelayedNodesUpdate(KisNodeSP root)
1635     {
1636         KisLayerUtils::recursiveApplyNodes(root,
1637         [] (KisNodeSP node) {
1638             KisDelayedUpdateNodeInterface *delayedUpdate =
1639                     dynamic_cast<KisDelayedUpdateNodeInterface*>(node.data());
1640             if (delayedUpdate) {
1641                 delayedUpdate->forceUpdateTimedNode();
1642             }
1643         });
1644     }
1645 
hasDelayedNodeWithUpdates(KisNodeSP root)1646     bool hasDelayedNodeWithUpdates(KisNodeSP root)
1647     {
1648         return recursiveFindNode(root,
1649             [] (KisNodeSP node) {
1650                 KisDelayedUpdateNodeInterface *delayedUpdate =
1651                     dynamic_cast<KisDelayedUpdateNodeInterface*>(node.data());
1652 
1653                 return delayedUpdate ? delayedUpdate->hasPendingTimedUpdates() : false;
1654             });
1655     }
1656 
forceAllHiddenOriginalsUpdate(KisNodeSP root)1657     void forceAllHiddenOriginalsUpdate(KisNodeSP root)
1658     {
1659         KisLayerUtils::recursiveApplyNodes(root,
1660         [] (KisNodeSP node) {
1661             KisCroppedOriginalLayerInterface *croppedUpdate =
1662                     dynamic_cast<KisCroppedOriginalLayerInterface*>(node.data());
1663             if (croppedUpdate) {
1664                 croppedUpdate->forceUpdateHiddenAreaOnOriginal();
1665             }
1666         });
1667     }
1668 
findImageByHierarchy(KisNodeSP node)1669     KisImageSP findImageByHierarchy(KisNodeSP node)
1670     {
1671         while (node) {
1672             const KisLayer *layer = dynamic_cast<const KisLayer*>(node.data());
1673             if (layer) {
1674                 return layer->image();
1675             }
1676 
1677             node = node->parent();
1678         }
1679 
1680         return 0;
1681     }
1682 
1683     namespace Private {
realNodeChangeRect(KisNodeSP rootNode,QRect currentRect=QRect ())1684     QRect realNodeChangeRect(KisNodeSP rootNode, QRect currentRect = QRect()) {
1685         KisNodeSP node = rootNode->firstChild();
1686 
1687         while(node) {
1688             currentRect |= realNodeChangeRect(node, currentRect);
1689             node = node->nextSibling();
1690         }
1691 
1692         if (!rootNode->isFakeNode()) {
1693             // TODO: it would be better to count up changeRect inside
1694             // node's extent() method
1695             currentRect |= rootNode->projectionPlane()->changeRect(rootNode->exactBounds());
1696         }
1697 
1698         return currentRect;
1699     }
1700     }
1701 
refreshHiddenAreaAsync(KisImageSP image,KisNodeSP rootNode,const QRect & preparedArea)1702     void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea) {
1703         QRect realNodeRect = Private::realNodeChangeRect(rootNode);
1704         if (!preparedArea.contains(realNodeRect)) {
1705 
1706             QRegion dirtyRegion = realNodeRect;
1707             dirtyRegion -= preparedArea;
1708 
1709             auto rc = dirtyRegion.begin();
1710             while (rc != dirtyRegion.end()) {
1711                 image->refreshGraphAsync(rootNode, *rc, realNodeRect);
1712                 rc++;
1713             }
1714         }
1715     }
1716 
recursiveTightNodeVisibleBounds(KisNodeSP rootNode)1717     QRect recursiveTightNodeVisibleBounds(KisNodeSP rootNode)
1718     {
1719         QRect exactBounds;
1720         recursiveApplyNodes(rootNode, [&exactBounds] (KisNodeSP node) {
1721             exactBounds |= node->projectionPlane()->tightUserVisibleBounds();
1722         });
1723         return exactBounds;
1724     }
1725 
findRoot(KisNodeSP node)1726     KisNodeSP findRoot(KisNodeSP node)
1727     {
1728         if (!node) return node;
1729 
1730         while (node->parent()) {
1731             node = node->parent();
1732         }
1733         return node;
1734     }
1735 
canChangeImageProfileInvisibly(KisImageSP image)1736     bool canChangeImageProfileInvisibly(KisImageSP image)
1737     {
1738         int numLayers = 0;
1739         bool hasNonNormalLayers = false;
1740         bool hasTransparentLayer = false;
1741 
1742 
1743         recursiveApplyNodes(image->root(),
1744             [&numLayers, &hasNonNormalLayers, &hasTransparentLayer, image] (KisNodeSP node) {
1745                 if (!node->inherits("KisLayer")) return;
1746 
1747                 numLayers++;
1748 
1749                 if (node->exactBounds().isEmpty()) return;
1750 
1751                 // this is only an approximation! it is not exact!
1752                 if (!hasTransparentLayer &&
1753                     node->exactBounds() != image->bounds()) {
1754 
1755                     hasTransparentLayer = true;
1756                 }
1757 
1758                 if (!hasNonNormalLayers &&
1759                     node->compositeOpId() != COMPOSITE_OVER) {
1760 
1761                     hasNonNormalLayers = true;
1762                 }
1763             });
1764 
1765         return numLayers == 1 || (!hasNonNormalLayers && !hasTransparentLayer);
1766     }
1767 }
1768