Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_layer_utils.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include "kis_layer_utils.h"
8
9#include <algorithm>
10
11#include <QUuid>
13#include <KoProperties.h>
14
15#include "kis_painter.h"
16#include "kis_image.h"
17#include "kis_node.h"
18#include "kis_layer.h"
19#include "kis_paint_layer.h"
20#include "kis_clone_layer.h"
21#include "kis_group_layer.h"
22#include "kis_selection.h"
23#include "kis_selection_mask.h"
25#include <kundo2command.h>
37#include "kis_projection_leaf.h"
39#include "kis_time_span.h"
40#include "kis_command_utils.h"
48#include "krita_utils.h"
53#include "kis_command_ids.h"
54#include "kis_image_config.h"
55#include "KisFutureUtils.h"
58
59
60namespace KisLayerUtils {
61
62namespace Private {
63 void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea, const QRect &extraUpdateRect);
64}
65
67 {
68 foreach (KisNodeSP node, mergedNodes) {
69
70 Q_FOREACH(KisNodeSP child, node->childNodes(QStringList("KisSelectionMask"), KoProperties())) {
71
72 KisSelectionMaskSP mask = qobject_cast<KisSelectionMask*>(child.data());
73 if (mask) {
74 selectionMasks.append(mask);
75 }
76 }
77 }
78 }
79
82 : image(_image),
83 storage(new SwitchFrameCommand::SharedStorage())
84 {
85 }
86
87 virtual ~MergeDownInfoBase() {}
88
90
92
94
96 QSet<int> frames;
97 bool pinnedToTimeline = false;
98 bool enableOnionSkins = false;
99
101
103 return qobject_cast<KisLayer*>(dstNode.data());
104 }
105 };
106
108 SplitAlphaToMaskInfo(KisImageSP _image, KisNodeSP _node, const QString& maskName)
109 : image(_image)
110 , node(_node)
111 , storage(new SwitchFrameCommand::SharedStorage())
112 {
114 mask = new KisTransparencyMask(image, maskName);
115 }
116
120 QSet<int> frames;
121
125
127 return mask;
128 }
129
131 return qobject_cast<KisLayer*>(node.data());
132 }
133
134 private:
136
137 };
138
141 KisLayerSP _prevLayer,
142 KisLayerSP _currLayer,
143 MergeFlags flags)
144 : MergeDownInfoBase(_image),
145 prevLayer(_prevLayer),
146 currLayer(_currLayer)
147 {
148 if (!flags.testFlag(SkipMergingFrames)) {
151 }
152
159 if (!frames.isEmpty() && !currLayer->isAnimated()) {
161 }
162
164
165 const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer*>(currLayer.data());
166 if (paintLayer) enableOnionSkins |= paintLayer->onionSkinEnabled();
167
168 paintLayer = qobject_cast<KisPaintLayer*>(prevLayer.data());
169 if (paintLayer) enableOnionSkins |= paintLayer->onionSkinEnabled();
170 }
171
174
176 KisNodeList mergedNodes;
177 mergedNodes << prevLayer;
178 mergedNodes << currLayer;
179 return mergedNodes;
180 }
181 };
182
185 : storage(new SwitchFrameCommand::SharedStorage())
186 , m_sourceNode(node)
187 , m_image(image)
188 , m_pinnedToTimeline(false)
189 {
191
193
196
198
199 KisColorizeMask *colorizeMask = dynamic_cast<KisColorizeMask*>(m_sourceNode.data());
200 if (colorizeMask) {
201 m_sourcePaintDevice = colorizeMask->coloringProjection();
202 const bool putBehind = colorizeMask->compositeOpId() == COMPOSITE_BEHIND;
203 if (putBehind) {
205 }
206
209
210 } else if (dynamic_cast<KisMask*>(m_sourceNode.data())) {
213 } else {
216 }
217
218 KisCloneLayer *cloneLayer = dynamic_cast<KisCloneLayer*>(m_sourceNode.data());
219 if (cloneLayer) {
221 } else if (m_sourcePaintDevice) {
222 KisPaintDeviceSP clone;
223
226
228 clone->setDefaultPixel(
231
232 QRect rc(m_sourcePaintDevice->extent());
233 KisPainter::copyAreaOptimized(rc.topLeft(), m_sourcePaintDevice, clone, rc);
234 } else {
236 }
237
241 clone);
242
244
245 if (sourceLayer() && targetLayer()) {
246 targetLayer()->disableAlphaChannel(sourceLayer()->alphaChannelDisabled());
247 }
248
251 }
252 }
253 }
254
255 QSet<int> frames() {
256 return m_frames;
257 }
258
260 return m_sourceNode;
261 }
262
264 return qobject_cast<KisLayer*>(m_sourceNode.data());
265 }
266
268 KisNodeList list;
269 list << m_sourceNode;
270 return list;
271 }
272
274 return qobject_cast<KisPaintLayer*>(m_sourceNode.data());
275 }
276
278 return m_targetNode != nullptr;
279 }
280
282 return m_targetNode;
283 }
284
286 return qobject_cast<KisLayer*>(m_targetNode.data());
287 }
288
290 return qobject_cast<KisPaintLayer*>(m_targetNode.data());
291 }
292
293
295 return m_image;
296 }
297
301
303 KisNodeList lst;
304 lst << m_sourceNode;
305 return lst;
306 }
307
311
313 return m_insertionParent;
314 }
315
317
318 private:
323 QSet<int> m_frames;
326
329 };
330
333 KisNodeList _mergedNodes,
334 MergeFlags flags)
335 : MergeDownInfoBase(_image),
336 mergedNodes(_mergedNodes)
337 {
338 foreach (KisNodeSP node, mergedNodes) {
339 if (!flags.testFlag(SkipMergingFrames)) {
341 }
343
344 const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer*>(node.data());
345 if (paintLayer) {
346 enableOnionSkins |= paintLayer->onionSkinEnabled();
347 }
348 }
349 }
350
351 QScopedPointer<KisSurrogateUndoStore> ephemeralCommandsStore;
354
356 return mergedNodes;
357 }
358 };
359
365
368
369 void redo() override {
370 fetchSelectionMasks(m_info->allSrcNodes(), m_info->selectionMasks);
371 }
372
373 private:
375 };
376
379
380 void populateChildCommands() override {
381 Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
383 [this] (KisNodeSP node) {
384 if (dynamic_cast<KisColorizeMask*>(node.data()) &&
386
387 KisBaseNode::PropertyList props = node->sectionModelProperties();
388 KisLayerPropertiesIcons::setNodeProperty(&props,
389 KisLayerPropertiesIcons::colorizeEditKeyStrokes,
390 false);
391
392 addCommand(new KisNodePropertyListCommand(node, props));
393 }
394 });
395 }
396 }
397
398 private:
400 };
401
404
405 void populateChildCommands() override {
406 Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
408 [this] (KisNodeSP node) {
410
414 false);
415
416 addCommand(new KisNodePropertyListCommand(node, props));
417 }
418 });
419 }
420 }
421
422 private:
424 };
425
428
429 void populateChildCommands() override {
435 if (m_info->nodesCompositingVaries) return;
436
437 // we should disable dirty requests on **redo only**, otherwise
438 // the state of the layers will not be recovered on undo
439 m_info->image->disableDirtyRequests();
440
441 Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
442 if (node->compositeOpId() != COMPOSITE_OVER) {
444 }
445
447
451 false);
452
453 addCommand(new KisNodePropertyListCommand(node, props));
454 }
455 }
456
457 m_info->image->enableDirtyRequests();
458 }
459
460 private:
462 };
463
465 DisablePassThroughForHeadsOnly(MergeDownInfoBaseSP info, bool skipIfDstIsGroup = false)
466 : m_info(info),
467 m_skipIfDstIsGroup(skipIfDstIsGroup)
468 {
469 }
470
471 void populateChildCommands() override {
472 if (m_skipIfDstIsGroup &&
473 m_info->dstLayer() &&
474 m_info->dstLayer()->inherits("KisGroupLayer")) {
475
476 return;
477 }
478
479
480 Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
482
486 false);
487
488 addCommand(new KisNodePropertyListCommand(node, props));
489 }
490 }
491 }
492
493 private:
496 };
497
501
502 RefreshHiddenAreas(MergeDownInfoBaseSP info) : m_image(info->image), m_nodes(info->allSrcNodes()) {}
503 RefreshHiddenAreas(MergeDownInfoBaseSP info, refresh_entire_image_t) : m_image(info->image), m_nodes(info->allSrcNodes()), m_extraUpdateRect(info->image->bounds()) {}
505 m_nodes << node;
506 }
507
508 void populateChildCommands() override {
509 KisImageAnimationInterface *interface = m_image->animationInterface();
510 const QRect preparedRect = !interface->externalFrameActive() ?
511 m_image->bounds() : QRect();
512
513 foreach (KisNodeSP node, m_nodes) {
515 }
516 }
517
518 private:
522 };
523
526 : m_nodes(info->allSrcNodes()) {}
527
529 m_nodes << nodes;
530 }
531
532 void redo() override {
533 if (m_info) {
534 m_nodes << m_info->allSrcNodes();
535 }
536
537 foreach (KisNodeSP node, m_nodes) {
539 }
540 }
541
542 private:
545 };
546
549 : m_singleInfo(info),
550 m_finalizing(finalizing) {}
551
553 : m_multipleInfo(info),
554 m_finalizing(finalizing),
555 m_putAfter(putAfter) {}
556
557 void populateChildCommands() override {
558 KisNodeSP prevNode;
559 KisNodeSP nextNode;
560 KisNodeList prevSelection;
561 KisNodeList nextSelection;
562 KisImageSP image;
563
564 if (m_singleInfo) {
565 prevNode = m_singleInfo->currLayer;
566 nextNode = m_singleInfo->dstNode;
567 image = m_singleInfo->image;
568 } else if (m_multipleInfo) {
569 prevNode = m_putAfter;
570 nextNode = m_multipleInfo->dstNode;
571 prevSelection = m_multipleInfo->allSrcNodes();
572 image = m_multipleInfo->image;
573 }
574
575 if (!m_finalizing) {
577 prevNode, KisNodeSP(),
578 image, false));
579 } else {
581 KisNodeSP(), nextNode,
582 image, true));
583 }
584 }
585
586 private:
591 };
592
595
596 void populateChildCommands() override {
597 // actual merging done by KisLayer::createMergedLayer (or specialized descendant)
598 m_info->dstNode = m_info->currLayer->createMergedLayerTemplate(m_info->prevLayer);
599
600 if (m_info->frames.size() > 0) {
601 m_info->dstNode->enableAnimation();
602 m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Raster.id(), true);
603 }
604
605 m_info->dstNode->setPinnedToTimeline(m_info->pinnedToTimeline);
606 m_info->dstNode->setColorLabelIndex(m_info->allSrcNodes().first()->colorLabelIndex());
607
608 KisPaintLayer *dstPaintLayer = qobject_cast<KisPaintLayer*>(m_info->dstNode.data());
609 if (dstPaintLayer) {
610 dstPaintLayer->setOnionSkinEnabled(m_info->enableOnionSkins);
611 }
612 }
613
614 private:
616 };
617
619 CreateMergedLayerMultiple(MergeMultipleInfoSP info, const QString name = QString() )
620 : m_info(info),
621 m_name(name) {}
622
623 void populateChildCommands() override {
624 QString mergedLayerName;
625 if (m_name.isEmpty()){
626 const QString mergedLayerSuffix = i18n("Merged");
627 mergedLayerName = m_info->mergedNodes.first()->name();
628
629 if (KisImageConfig(true).renameMergedLayers() && !mergedLayerName.endsWith(mergedLayerSuffix)) {
630 mergedLayerName = QString("%1 %2")
631 .arg(mergedLayerName).arg(mergedLayerSuffix);
632 }
633 } else {
634 mergedLayerName = m_name;
635 }
636
637 KisPaintLayer *dstPaintLayer = new KisPaintLayer(m_info->image, mergedLayerName, OPACITY_OPAQUE_U8);
638 m_info->dstNode = dstPaintLayer;
639
640 if (m_info->frames.size() > 0) {
641 m_info->dstNode->enableAnimation();
642 m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Raster.id(), true);
643 }
644
645
646 auto channelFlagsLazy = [](KisNodeSP node) {
647 KisLayer *layer = dynamic_cast<KisLayer*>(node.data());
648 return layer ? layer->channelFlags() : QBitArray();
649 };
650
651 QString compositeOpId;
652 QBitArray channelFlags;
653 bool compositionVaries = false;
654 bool isFirstCycle = true;
655
656 foreach (KisNodeSP node, m_info->allSrcNodes()) {
657 if (isFirstCycle) {
658 compositeOpId = node->compositeOpId();
659 channelFlags = channelFlagsLazy(node);
660 isFirstCycle = false;
661 } else if (compositeOpId != node->compositeOpId() ||
662 channelFlags != channelFlagsLazy(node)) {
663 compositionVaries = true;
664 break;
665 }
666
667 KisLayerSP layer = qobject_cast<KisLayer*>(node.data());
668 if (layer && layer->layerStyle()) {
669 compositionVaries = true;
670 break;
671 }
672 }
673
674 if (!compositionVaries) {
675 if (!compositeOpId.isEmpty()) {
676 m_info->dstNode->setCompositeOpId(compositeOpId);
677 }
678 if (m_info->dstLayer() && !channelFlags.isEmpty()) {
679 m_info->dstLayer()->setChannelFlags(channelFlags);
680 }
681 }
682
683 m_info->nodesCompositingVaries = compositionVaries;
684
685 m_info->dstNode->setPinnedToTimeline(m_info->pinnedToTimeline);
686 m_info->dstNode->setColorLabelIndex(m_info->allSrcNodes().first()->colorLabelIndex());
687
688 dstPaintLayer->setOnionSkinEnabled(m_info->enableOnionSkins);
689 }
690
691 private:
693 QString m_name;
694 };
695
697 MergeLayers(MergeDownInfoSP info, bool skipMergingSourceLayer)
698 : m_info(info), m_skipMergingSourceLayer(skipMergingSourceLayer) {}
699
700 void populateChildCommands() override {
701 // actual merging done by KisLayer::createMergedLayer (or specialized descendant)
702 m_info->currLayer->fillMergedLayerTemplate(m_info->dstLayer(), m_info->prevLayer, m_skipMergingSourceLayer);
703 }
704
705 private:
708 };
709
712
713 void populateChildCommands() override {
714 KisPainter gc(m_info->dstNode->paintDevice());
715
716 foreach (KisNodeSP node, m_info->allSrcNodes()) {
717 QRect rc = node->exactBounds() | m_info->image->bounds();
718 node->projectionPlane()->apply(&gc, rc);
719 }
720 }
721
722 private:
724 };
725
726 struct MergeMetaData : public KUndo2Command {
728 : m_info(info),
729 m_strategy(strategy) {}
730
731 void redo() override {
732 QRect layerProjectionExtent = m_info->currLayer->projection()->extent();
733 QRect prevLayerProjectionExtent = m_info->prevLayer->projection()->extent();
734 int prevLayerArea = prevLayerProjectionExtent.width() * prevLayerProjectionExtent.height();
735 int layerArea = layerProjectionExtent.width() * layerProjectionExtent.height();
736
737 QList<double> scores;
738 double norm = qMax(prevLayerArea, layerArea);
739 scores.append(prevLayerArea / norm);
740 scores.append(layerArea / norm);
741
743 srcs.append(m_info->prevLayer->metaData());
744 srcs.append(m_info->currLayer->metaData());
745 m_strategy->merge(m_info->dstLayer()->metaData(), srcs, scores);
746 }
747
748 private:
751 };
752
756
757 void populateChildCommands() override {
758 m_info->getMask()->initSelection(m_info->getLayer());
759 }
760
761 private:
763 };
764
767 : m_info(info) {
768 m_cached = new KisPaintDevice(*m_info->node->paintDevice(), KritaUtils::CopyAllFrames);
769 }
770
771 void redo() override {
772 KisPaintDeviceSP srcDevice = m_info->node->paintDevice();
773 const KoColorSpace *srcCS = srcDevice->colorSpace();
774 const QRect processRect =
775 srcDevice->exactBounds() |
776 srcDevice->defaultBounds()->bounds();
777
778 KisSequentialIterator srcIt(srcDevice, processRect);
779 KisSequentialIterator dstIt(m_info->getMaskDevice(), processRect);
780
781 while (srcIt.nextPixel() && dstIt.nextPixel()) {
782 quint8 *srcPtr = srcIt.rawData();
783 quint8 *alpha8Ptr = dstIt.rawData();
784
785 *alpha8Ptr = srcCS->opacityU8(srcPtr);
786 srcCS->setOpacity(srcPtr, OPACITY_OPAQUE_U8, 1);
787 }
788 }
789
790 void undo() override {
791 KisPaintDeviceSP srcDevice = m_info->node->paintDevice();
792
793 if (srcDevice->framesInterface()) { //Swap contents of all frames to reflect the pre-operation state.
795 Q_FOREACH(const int& frame, srcDevice->framesInterface()->frames() ) {
796 if (m_cached->framesInterface()->frames().contains(frame)) {
798 srcDevice->framesInterface()->uploadFrame(frame, tempPD);
799 }
800 }
801 } else {
802 const QRect processRect =
803 srcDevice->exactBounds() |
804 srcDevice->defaultBounds()->bounds();
805
806 const KoColorSpace *srcCS = srcDevice->colorSpace();
807 KisSequentialIterator srcIt(m_cached, processRect);
808 KisSequentialIterator dstIt(srcDevice, processRect);
809
810 while (srcIt.nextPixel() && dstIt.nextPixel()) {
811 quint8 *srcPtr = srcIt.rawData();
812 quint8 *dstPtr = dstIt.rawData();
813 srcCS->setOpacity(dstPtr, srcCS->opacityU8(srcPtr), 1);
814 }
815 }
816 }
817
818 private:
821 };
822
825 : m_source(src)
827 , m_frame(frame)
828 {}
829
830 void populateChildCommands() override {
832 if (!channel)
833 return;
834
835
838 m_target->paintDevice()->framesInterface()->uploadFrame(key->frameID(), clone);
839 }
840
841 private:
845 };
846
848 const KisNodeList &selectedAfter,
849 KisNodeSP activeBefore,
850 KisNodeSP activeAfter,
851 KisImageSP image,
852 bool finalize, KUndo2Command *parent)
853 : FlipFlopCommand(finalize, parent),
854 m_selectedBefore(selectedBefore),
855 m_selectedAfter(selectedAfter),
856 m_activeBefore(activeBefore),
857 m_activeAfter(activeAfter),
858 m_image(image)
859 {
860 }
861
871
876
880
888
892
903 safeReplaceMultipleNodes(nodes, image, std::nullopt);
904 }
905
907 std::optional<ReplacementNode> replacementNode) {
908
909 const bool lastLayer = !replacementNode && scanForLastLayer(image, removedNodes);
910
911 auto isNodeWeird = [] (KisNodeSP node) {
912 const bool normalCompositeMode = node->compositeOpId() == COMPOSITE_OVER;
913
914 KisLayer *layer = dynamic_cast<KisLayer*>(node.data());
915 const bool hasInheritAlpha = layer && layer->alphaChannelDisabled();
916 return !normalCompositeMode && !hasInheritAlpha;
917 };
918
919
922 RecipeSP updateRecipe(new Recipe());
923
924 if (replacementNode) {
925 updateRecipe->nodesToAdd.push_back({replacementNode->node,
926 replacementNode->doRedoUpdates,
927 replacementNode->doUndoUpdates});
928 }
929
930 Q_FOREACH (KisNodeSP node, removedNodes) {
931 updateRecipe->nodesToRemove.push_back({node, !isNodeWeird(node), true});
932 }
933
935
936 if (replacementNode) {
938 replacementNode->node,
939 replacementNode->parent,
940 replacementNode->putAfter,
941 false, false));
942
943 Q_FOREACH (KisSelectionMaskSP mask, replacementNode->selectionMasks) {
944 addCommandImpl(new KisImageLayerMoveCommand(image, mask, replacementNode->node, replacementNode->node->lastChild(), false));
946 }
947
948 // relink all the clone layers onto the new replacement node
949 if (replacementNode->relinkClones) {
951
952 Q_FOREACH (KisNodeSP node, removedNodes) {
953 KisLayerSP originalSource = dynamic_cast<KisLayer*>(node.data());
954 if (originalSource) {
955 clones.append(originalSource->registeredClones());
956 }
957 }
958
959 KisLayerSP finalSource = dynamic_cast<KisLayer*>(replacementNode->node.data());
960
961 if (finalSource && !clones.isEmpty()) {
962 addCommandImpl(new KisChangeCloneLayersCommand(implicitCastList<KisCloneLayerSP>(clones), finalSource));
963 }
964 }
965 }
966
967 while (!removedNodes.isEmpty()) {
968 KisNodeList::iterator it = removedNodes.begin();
969
970 while (it != removedNodes.end()) {
971 if (!checkIsSourceForClone(*it, removedNodes)) {
972 KisNodeSP node = *it;
973
974 addCommandImpl(new KisImageLayerRemoveCommand(image, node, false, false));
975 it = removedNodes.erase(it);
976 } else {
977 ++it;
978 }
979 }
980 }
981
983
984 // Hint: we shouldn't include that into the batch update since this layer doesn't trigger any updates
985 if (lastLayer) {
986 KisLayerSP newLayer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace());
987 addCommandImpl(new KisImageLayerAddCommand(image, newLayer,
988 image->root(),
989 KisNodeSP(),
990 false, false));
991 }
992 }
993
995 foreach (KisNodeSP node, nodes) {
996 if (node == src) continue;
997
998 KisCloneLayer *clone = dynamic_cast<KisCloneLayer*>(node.data());
999
1000 if (clone && KisNodeSP(clone->copyFrom()) == src) {
1001 return true;
1002 }
1003 }
1004
1005 return false;
1006 }
1007
1009 bool removeLayers = false;
1010 Q_FOREACH(KisNodeSP nodeToRemove, nodesToRemove) {
1011 if (qobject_cast<KisLayer*>(nodeToRemove.data())) {
1012 removeLayers = true;
1013 break;
1014 }
1015 }
1016 if (!removeLayers) return false;
1017
1018 bool lastLayer = true;
1019 KisNodeSP node = image->root()->firstChild();
1020 while (node) {
1021 if (!nodesToRemove.contains(node) &&
1022 qobject_cast<KisLayer*>(node.data()) &&
1023 !node->isFakeNode()) {
1024
1025 lastLayer = false;
1026 break;
1027 }
1028 node = node->nextSibling();
1029 }
1030
1031 return lastLayer;
1032 }
1033
1035 KisImageSP image)
1036 : m_nodes(nodes),
1037 m_image(image)
1038 {
1039 }
1040
1045
1049
1052 : m_info(info), m_putAfter(putAfter) {}
1053
1054 void populateChildCommands() override {
1056 m_info->dstNode,
1057 m_putAfter->parent(),
1058 m_putAfter,
1059 true, false));
1060
1061 }
1062
1063 private:
1064 virtual void addCommandImpl(KUndo2Command *cmd) {
1065 addCommand(cmd);
1066 }
1067
1070 };
1071
1073 SimpleAddNode(KisImageSP image, KisNodeSP toAdd, KisNodeSP parent = 0, KisNodeSP putAfter = 0)
1074 : m_image(image)
1075 , m_toAdd(toAdd)
1076 , m_parent(parent)
1077 , m_putAfter(putAfter)
1078 {
1079 while (m_parent && !m_parent->allowAsChild(m_toAdd)) {
1082 }
1083
1084 if (!m_parent) {
1085 m_parent = m_image->root();
1086 }
1087 }
1088
1089
1090 void populateChildCommands() override {
1092 m_toAdd,
1093 m_parent,
1094 m_putAfter,
1095 true, false));
1096 }
1097
1098 private:
1099 virtual void addCommandImpl(KUndo2Command *cmd) {
1100 addCommand(cmd);
1101 }
1102
1107
1108 };
1109
1110
1111 void splitNonRemovableNodes(KisNodeList &nodesToRemove, KisNodeList &_nodesToHide)
1112 {
1113 QSet<KisNodeSP> nodesToHide;
1114 QSet<KisNodeSP> extraNodesToRemove;
1115
1116 for (auto it = nodesToRemove.begin(); it != nodesToRemove.end(); ++it) {
1117 KisNodeSP root = *it;
1119
1120 if (!root->isEditable(false)) {
1121 nodesToHide.insert(root);
1122 } else {
1123 bool rootNeedsCarefulRemoval = false;
1124
1126 [root, &nodesToHide, &rootNeedsCarefulRemoval] (KisNodeSP node) {
1127 if (!node->isEditable(false)) {
1128 while (node != root) {
1129 nodesToHide.insert(node);
1130 node = node->parent();
1131 KIS_SAFE_ASSERT_RECOVER_BREAK(node);
1132 }
1133 nodesToHide.insert(root);
1134 rootNeedsCarefulRemoval = true;
1135 }
1136 });
1137
1138 if (rootNeedsCarefulRemoval) {
1140 [&extraNodesToRemove] (KisNodeSP node) {
1141 extraNodesToRemove.insert(node);
1142 });
1143 }
1144 }
1145 }
1146 nodesToRemove += KisNodeList(extraNodesToRemove.begin(), extraNodesToRemove.end());
1147 KritaUtils::filterContainer<KisNodeList>(nodesToRemove,
1148 [nodesToHide](KisNodeSP node) {
1149 return !nodesToHide.contains(node);
1150 });
1151 _nodesToHide = KisNodeList(nodesToHide.begin(), nodesToHide.end());
1152 }
1153
1155 CleanUpNodes(MergeDownInfoBaseSP info, KisNodeSP putAfter, bool relinkClones = false)
1156 : m_info(info), m_putAfter(putAfter), m_relinkClones(relinkClones) {}
1157
1158 static void findPerfectParent(KisNodeList nodesToDelete, KisNodeSP &putAfter, KisNodeSP &parent) {
1159 if (!putAfter) {
1160 putAfter = nodesToDelete.last();
1161 }
1162
1163 // Add the new merged node on top of the active node
1164 // -- checking all parents if they are included in nodesToDelete
1165 // Not every descendant is included in nodesToDelete even if in fact
1166 // they are going to be deleted, so we need to check it.
1167 // If we consider the path from root to the putAfter node,
1168 // if there are any nodes marked for deletion, any node afterwards
1169 // is going to be deleted, too.
1170 // example: root . . . . . ! ! . . ! ! ! ! . . . . putAfter
1171 // it should be: root . . . . . ! ! ! ! ! ! ! ! ! ! ! ! !putAfter
1172 // and here: root . . . . X ! ! . . ! ! ! ! . . . . putAfter
1173 // you can see which node is "the perfect ancestor"
1174 // (marked X; called "parent" in the function arguments).
1175 // and here: root . . . . . O ! . . ! ! ! ! . . . . putAfter
1176 // you can see which node is "the topmost deleted ancestor" (marked 'O')
1177
1178 KisNodeSP node = putAfter->parent();
1179 bool foundDeletedAncestor = false;
1180 KisNodeSP topmostAncestorToDelete = nullptr;
1181
1182 while (node) {
1183
1184 if (nodesToDelete.contains(node)
1185 && !nodesToDelete.contains(node->parent())) {
1186 foundDeletedAncestor = true;
1187 topmostAncestorToDelete = node;
1188 // Here node is to be deleted and its parent is not,
1189 // so its parent is the one of the first not deleted (="perfect") ancestors.
1190 // We need the one that is closest to the top (root)
1191 }
1192
1193 node = node->parent();
1194 }
1195
1196 if (foundDeletedAncestor) {
1197 parent = topmostAncestorToDelete->parent();
1198 putAfter = topmostAncestorToDelete;
1199 }
1200 else {
1201 parent = putAfter->parent(); // putAfter (and none of its ancestors) is to be deleted, so its parent is the first not deleted ancestor
1202 }
1203
1204 }
1205
1206 void populateChildCommands() override {
1207 KisNodeList nodesToDelete = m_info->allSrcNodes();
1208
1209 KisNodeSP parent;
1210 findPerfectParent(nodesToDelete, m_putAfter, parent);
1211
1212 if (!parent) {
1213 KisNodeSP oldRoot = m_info->image->root();
1214 KisNodeSP newRoot(new KisGroupLayer(m_info->image, "root", OPACITY_OPAQUE_U8));
1215
1216 // copy all fake nodes into the new image
1217 KisLayerUtils::recursiveApplyNodes(oldRoot, [this, oldRoot, newRoot] (KisNodeSP node) {
1218 if (node->isFakeNode() && node->parent() == oldRoot) {
1219 addCommand(new KisImageLayerAddCommand(m_info->image,
1220 node->clone(),
1221 newRoot,
1222 KisNodeSP(),
1223 false, false));
1224
1225 }
1226 });
1227
1228 addCommand(new KisImageLayerAddCommand(m_info->image,
1229 m_info->dstNode,
1230 newRoot,
1231 KisNodeSP(),
1232 true, false));
1233 addCommand(new KisImageChangeLayersCommand(m_info->image, oldRoot, newRoot));
1234
1235 }
1236 else {
1237 KisNodeList safeNodesToDelete = m_info->allSrcNodes();
1238 KisNodeList safeNodesToHide;
1239
1240 splitNonRemovableNodes(safeNodesToDelete, safeNodesToHide);
1241
1242 Q_FOREACH(KisNodeSP node, safeNodesToHide) {
1243 addCommand(new KisImageChangeVisibilityCommand(false, node));
1244 }
1245
1246 safeReplaceMultipleNodes(safeNodesToDelete, m_info->image,
1247 std::make_optional<ReplacementNode>(
1248 {m_info->dstNode,
1249 parent,
1250 m_putAfter,
1251 true, false,
1252 m_info->selectionMasks,
1253 m_relinkClones}));
1254 }
1255
1256
1257 }
1258
1259 private:
1260 void addCommandImpl(KUndo2Command *cmd) override {
1261 addCommand(cmd);
1262 }
1263
1264 private:
1267 bool m_relinkClones {false};
1268 };
1269
1270 SwitchFrameCommand::SharedStorage::~SharedStorage() {
1271 }
1272
1273 SwitchFrameCommand::SwitchFrameCommand(KisImageSP image, int time, bool finalize, SharedStorageSP storage)
1274 : FlipFlopCommand(finalize),
1275 m_image(image),
1276 m_newTime(time),
1277 m_storage(storage) {}
1278
1280
1282 KisImageAnimationInterface *interface = m_image->animationInterface();
1283 const int currentTime = interface->currentTime();
1284 if (currentTime == m_newTime) {
1285 m_storage->value = m_newTime;
1286 return;
1287 }
1288
1289 interface->image()->disableUIUpdates();
1290 interface->saveAndResetCurrentTime(m_newTime, &m_storage->value);
1291 }
1292
1294 KisImageAnimationInterface *interface = m_image->animationInterface();
1295 const int currentTime = interface->currentTime();
1296 if (currentTime == m_storage->value) {
1297 return;
1298 }
1299
1300 interface->restoreCurrentTime(&m_storage->value);
1301 interface->image()->enableUIUpdates();
1302 }
1303
1305 AddNewFrame(KisNodeSP node, int frame) : m_node(node), m_frame(frame) {}
1306 AddNewFrame(KisNodeSP node, int frame, KisNodeList sampleNodes) : m_node(node), m_frame(frame), m_sampledNodes(sampleNodes) {}
1307 AddNewFrame(KisNodeSP node, int frame, KisNodeSP source) : m_node(node), m_frame(frame) { m_sampledNodes << source; }
1308 AddNewFrame(MergeDownInfoBaseSP info, int frame) : m_frame(frame), m_sampledNodes(info->allSrcNodes()), m_mergeInfo(info) {}
1309
1310 void populateChildCommands() override {
1311 KUndo2Command *cmd = new KUndo2Command;
1312 KisNodeSP node = m_node ? m_node : m_mergeInfo->dstNode;
1314 channel->addKeyframe(m_frame, cmd);
1315
1316 if (m_sampledNodes.count() > 0) {
1318 }
1319
1320 addCommand(cmd);
1321 }
1322
1324 Q_FOREACH(KisNodeSP srcNode, srcNodes) {
1325 Q_FOREACH(KisKeyframeChannel *channel, srcNode->keyframeChannels().values()) {
1326 KisKeyframeSP keyframe = channel->keyframeAt(m_frame);
1327 if (!keyframe.isNull() && keyframe->colorLabel() != 0) {
1328 dstKeyframe->setColorLabel(keyframe->colorLabel());
1329 return;
1330 }
1331 }
1332 }
1333
1334 dstKeyframe->setColorLabel(0);
1335 }
1336
1337 private:
1342 };
1343
1344 QSet<int> fetchLayerFrames(KisNodeSP node) {
1345 QSet<int> frames;
1346 Q_FOREACH(KisKeyframeChannel *channel, node->keyframeChannels()) {
1347 if (!channel) {
1348 continue;
1349 }
1350
1351 KisRasterKeyframeChannel *rasterChan = dynamic_cast<KisRasterKeyframeChannel*>(channel);
1352 if (rasterChan) {
1353 frames.unite(rasterChan->allKeyframeTimes());
1354 continue;
1355 }
1356
1357 KisScalarKeyframeChannel *scalarChan = dynamic_cast<KisScalarKeyframeChannel*>(channel);
1358 if (scalarChan) {
1359 const int initialKeyframe = scalarChan->firstKeyframeTime();
1360
1361 if (initialKeyframe == -1) {
1362 continue;
1363 }
1364
1365 const int lastKeyframe = scalarChan->lastKeyframeTime();
1366 KisTimeSpan currentSpan = scalarChan->identicalFrames(initialKeyframe);
1367 while (!currentSpan.isInfinite() && currentSpan.isValid() && currentSpan.start() < lastKeyframe) {
1368 frames.insert(currentSpan.start());
1369 currentSpan = scalarChan->identicalFrames(currentSpan.end() + 1);
1370 }
1371
1372 frames.insert(lastKeyframe);
1373 }
1374
1375 }
1376
1377 return frames;
1378 }
1379
1381 if (!rootNode->visible()) return QSet<int>();
1382
1383 QSet<int> frames = fetchLayerFrames(rootNode);
1384
1385 KisNodeSP node = rootNode->firstChild();
1386 while(node) {
1387 frames |= fetchLayerFramesRecursive(node);
1388 node = node->nextSibling();
1389 }
1390
1391 return frames;
1392 }
1393
1395 QSet<int> frames = fetchLayerFrames(node);
1396 frames = fetchUniqueFrameTimes(node, frames, false);
1397
1398 if (frames.isEmpty()) {
1399 (*jobs)[0].insert(node);
1400 } else {
1401 foreach (int frame, frames) {
1402 (*jobs)[frame].insert(node);
1403 }
1404 }
1405 }
1406
1408 updateFrameJobs(jobs, rootNode);
1409
1410 KisNodeSP node = rootNode->firstChild();
1411 while(node) {
1412 updateFrameJobsRecursive(jobs, node);
1413 node = node->nextSibling();
1414 }
1415 }
1416
1420 void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy* strategy, MergeFlags flags)
1421 {
1422 if (!layer->prevSibling()) return;
1423
1424 // XXX: this breaks if we allow free mixing of masks and layers
1425 KisLayerSP prevLayer = qobject_cast<KisLayer*>(layer->prevSibling().data());
1426
1427 while (prevLayer && prevLayer->isFakeNode()) {
1428 prevLayer = qobject_cast<KisLayer*>(prevLayer->prevSibling().data());
1429 }
1430
1431 if (!prevLayer) return;
1432
1433 if (!layer->visible() && !prevLayer->visible()) {
1434 return;
1435 }
1436
1437 KisImageSignalVector emitSignals;
1438 KisProcessingApplicator applicator(image, 0,
1440 emitSignals,
1441 kundo2_i18n("Merge Down"));
1442
1443 if (layer->visible() && prevLayer->visible()) {
1444 MergeDownInfoSP info(new MergeDownInfo(image, prevLayer, layer, flags));
1445
1446 // disable key strokes on all colorize masks, all onion skins on
1447 // paint layers and wait until update is finished with a barrier
1448 applicator.applyCommand(new DisableColorizeKeyStrokes(info));
1449 applicator.applyCommand(new DisableOnionSkins(info));
1451
1452 applicator.applyCommand(new KeepMergedNodesSelected(info, false));
1453 applicator.applyCommand(new FillSelectionMasks(info));
1455
1456 // NOTE: shape layer may have emitted spontaneous jobs during layer creation,
1457 // wait for them to complete!
1460
1461 // in two-layer mode we disable pass through only when the destination layer
1462 // is not a group layer
1463 applicator.applyCommand(new DisablePassThroughForHeadsOnly(info, true));
1465
1466 if (info->frames.size() > 0) {
1471 const int currentTimeOnStart = info->image->animationInterface()->currentTime();
1472
1473 foreach (int frame, info->frames) {
1474 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
1475
1476 applicator.applyCommand(new AddNewFrame(info, frame));
1485
1494 const bool skipMergingSourceLayer = !layer->isAnimated() &&
1495 frame != currentTimeOnStart;
1496
1497 applicator.applyCommand(new MergeLayers(info, skipMergingSourceLayer), KisStrokeJobData::BARRIER);
1498
1499 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage), KisStrokeJobData::BARRIER);
1500 }
1501 } else {
1502 applicator.applyCommand(new RefreshHiddenAreas(info));
1504 applicator.applyCommand(new MergeLayers(info, false), KisStrokeJobData::BARRIER);
1505 }
1506
1507 applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
1508 applicator.applyCommand(new CleanUpNodes(info, layer),
1511 applicator.applyCommand(new KeepMergedNodesSelected(info, true));
1512 } else if (layer->visible()) {
1514 layer, KisNodeSP(),
1515 image, false));
1516
1517 applicator.applyCommand(
1518 new SimpleRemoveLayers(KisNodeList() << prevLayer,
1519 image),
1522
1524 KisNodeSP(), layer,
1525 image, true));
1526 } else if (prevLayer->visible()) {
1528 layer, KisNodeSP(),
1529 image, false));
1530
1531 applicator.applyCommand(
1532 new SimpleRemoveLayers(KisNodeList() << layer,
1533 image),
1536
1538 KisNodeSP(), prevLayer,
1539 image, true));
1540 }
1541
1542 applicator.end();
1543 }
1544
1545 bool checkIsChildOf(KisNodeSP node, const KisNodeList &parents)
1546 {
1547 KisNodeList nodeParents;
1548
1549 KisNodeSP parent = node->parent();
1550 while (parent) {
1551 nodeParents << parent;
1552 parent = parent->parent();
1553 }
1554
1555 foreach(KisNodeSP perspectiveParent, parents) {
1556 if (nodeParents.contains(perspectiveParent)) {
1557 return true;
1558 }
1559 }
1560
1561 return false;
1562 }
1563
1564 bool checkIsCloneOf(KisNodeSP node, const KisNodeList &nodes)
1565 {
1566 bool result = false;
1567
1568 KisCloneLayer *clone = dynamic_cast<KisCloneLayer*>(node.data());
1569 if (clone) {
1570 KisNodeSP cloneSource = KisNodeSP(clone->copyFrom());
1571
1572 Q_FOREACH(KisNodeSP subtree, nodes) {
1573 result =
1574 recursiveFindNode(subtree,
1575 [cloneSource](KisNodeSP node) -> bool
1576 {
1577 return node == cloneSource;
1578 });
1579
1580 if (!result) {
1581 result = checkIsCloneOf(cloneSource, nodes);
1582 }
1583
1584 if (result) {
1585 break;
1586 }
1587 }
1588 }
1589
1590 return result;
1591 }
1592
1593 void filterMergeableNodes(KisNodeList &nodes, bool allowMasks)
1594 {
1595 KisNodeList::iterator it = nodes.begin();
1596
1597 while (it != nodes.end()) {
1598 if ((!allowMasks && !qobject_cast<KisLayer*>(it->data())) ||
1599 checkIsChildOf(*it, nodes)) {
1600 //qDebug() << "Skipping node" << ppVar((*it)->name());
1601 it = nodes.erase(it);
1602 } else {
1603 ++it;
1604 }
1605 }
1606 }
1607
1608 void sortMergeableNodes(KisNodeSP root, KisNodeList &inputNodes, KisNodeList &outputNodes)
1609 {
1610 KisNodeList::iterator it = std::find(inputNodes.begin(), inputNodes.end(), root);
1611
1612 if (it != inputNodes.end()) {
1613 outputNodes << *it;
1614 inputNodes.erase(it);
1615 }
1616
1617 if (inputNodes.isEmpty()) {
1618 return;
1619 }
1620
1621 KisNodeSP child = root->firstChild();
1622 while (child) {
1623 sortMergeableNodes(child, inputNodes, outputNodes);
1624 child = child->nextSibling();
1625 }
1626
1630 KIS_ASSERT_RECOVER_NOOP(root->parent() || inputNodes.isEmpty());
1631 }
1632
1634 {
1635 KisNodeList result;
1636 sortMergeableNodes(root, nodes, result);
1637 return result;
1638 }
1639
1641 {
1642 KIS_SAFE_ASSERT_RECOVER(!nodes.isEmpty()) { return nodes; }
1643
1644 KisNodeSP root;
1645 Q_FOREACH(KisNodeSP node, nodes) {
1646 KisNodeSP localRoot = node;
1647 while (localRoot->parent()) {
1648 localRoot = localRoot->parent();
1649 }
1650
1651 if (!root) {
1652 root = localRoot;
1653 }
1654 KIS_SAFE_ASSERT_RECOVER(root == localRoot) { return nodes; }
1655 }
1656
1657 KisNodeList result;
1658 sortMergeableNodes(root, nodes, result);
1659 filterMergeableNodes(result, allowMasks);
1660 return result;
1661 }
1662
1664 {
1665 KIS_SAFE_ASSERT_RECOVER(!nodes.isEmpty()) { return nodes; }
1666
1667 KisNodeSP root;
1668 Q_FOREACH(KisNodeSP node, nodes) {
1669 KisNodeSP localRoot = node;
1670 while (localRoot->parent()) {
1671 localRoot = localRoot->parent();
1672 }
1673
1674 if (!root) {
1675 root = localRoot;
1676 }
1677 KIS_SAFE_ASSERT_RECOVER(root == localRoot) { return nodes; }
1678 }
1679
1680 KisNodeList result;
1681 sortMergeableNodes(root, nodes, result);
1682 return result;
1683 }
1684
1686 KisNodeList filteredNodes = nodes;
1687 KisNodeList sortedNodes;
1688
1689 KisLayerUtils::filterMergeableNodes(filteredNodes, true);
1690
1691 bool haveExternalNodes = false;
1692 Q_FOREACH (KisNodeSP node, nodes) {
1693 if (node->graphListener() != image->root()->graphListener()) {
1694 haveExternalNodes = true;
1695 break;
1696 }
1697 }
1698
1699 if (!haveExternalNodes) {
1700 KisLayerUtils::sortMergeableNodes(image->root(), filteredNodes, sortedNodes);
1701 } else {
1702 sortedNodes = filteredNodes;
1703 }
1704
1705 return sortedNodes;
1706 }
1707
1708
1710 {
1711 if (!KisImageConfig(true).renameDuplicatedLayers()) { return; }
1712
1713 const QString prefix = i18n("Copy of");
1714 QString newName = node->name();
1715 if (!newName.startsWith(prefix)) {
1716 newName = QString("%1 %2").arg(prefix).arg(newName);
1717 node->setName(newName);
1718 }
1719 }
1720
1721 KisNodeList findNodesWithProps(KisNodeSP root, const KoProperties &props, bool excludeRoot)
1722 {
1723 KisNodeList nodes;
1724
1725 if ((!excludeRoot || root->parent()) && root->check(props)) {
1726 nodes << root;
1727 }
1728
1729 KisNodeSP node = root->firstChild();
1730 while (node) {
1731 nodes += findNodesWithProps(node, props, excludeRoot);
1732 node = node->nextSibling();
1733 }
1734
1735 return nodes;
1736 }
1737
1738 KisNodeList filterInvisibleNodes(const KisNodeList &nodes, KisNodeList *invisibleNodes, KisNodeSP *putAfter)
1739 {
1740 KIS_ASSERT_RECOVER(invisibleNodes) { return nodes; }
1741 KIS_ASSERT_RECOVER(putAfter) { return nodes; }
1742
1743 KisNodeList visibleNodes;
1744 int putAfterIndex = -1;
1745
1746 Q_FOREACH(KisNodeSP node, nodes) {
1747 if (node->visible()) {
1748 visibleNodes << node;
1749 } else if (node->userLocked()) {
1754 } else {
1755 *invisibleNodes << node;
1756
1757 if (node == *putAfter) {
1758 putAfterIndex = visibleNodes.size() - 1;
1759 }
1760 }
1761 }
1762
1763 if (!visibleNodes.isEmpty() && putAfterIndex >= 0) {
1764 putAfterIndex = qBound(0, putAfterIndex, visibleNodes.size() - 1);
1765 *putAfter = visibleNodes[putAfterIndex];
1766 }
1767
1768 return visibleNodes;
1769 }
1770
1772 {
1773 KisNodeList::iterator it = nodes.begin();
1774
1775 while (it != nodes.end()) {
1776 if ((*it)->userLocked()) {
1777 it = nodes.erase(it);
1778 } else {
1779 ++it;
1780 }
1781 }
1782 }
1783
1785 {
1786 KisImageSignalVector emitSignals;
1787 KisProcessingApplicator applicator(image,
1788 image->root(),
1790 emitSignals,
1791 kundo2_i18n("Change projection color"),
1792 0,
1795 applicator.end();
1796 }
1797
1799 {
1801 : m_info(info)
1802 , m_commands(commands)
1803 , m_cleanupNodes(cleanupNodes)
1804 {
1805 }
1806
1808 if (!m_cleanupNodes && !m_info->ephemeralCommandsStore) {
1809 m_info->ephemeralCommandsStore.reset(new KisSurrogateUndoStore());
1810 }
1811
1812 Q_FOREACH (KUndo2Command *cmd, m_commands) {
1813 if (m_cleanupNodes) {
1814 addCommand(cmd);
1815 } else {
1816 m_info->ephemeralCommandsStore->addCommand(cmd);
1817 }
1818 }
1819 m_commands.clear();
1820 m_info.reset();
1821 }
1822
1823 private:
1826 bool m_cleanupNodes {true};
1827 };
1828
1830 {
1832 : m_info(info)
1833 {
1834 }
1835
1837 KIS_SAFE_ASSERT_RECOVER_RETURN(m_info->ephemeralCommandsStore);
1838 m_info->ephemeralCommandsStore->undoAll();
1839
1840 }
1841
1842 private:
1844 };
1845
1871 void mergeMultipleLayersImpl(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter,
1872 bool flattenSingleLayer, const KUndo2MagicString &actionName,
1873 bool cleanupNodes = true, const QString layerName = QString(),
1874 MergeFlags flags = None)
1875 {
1876 if (!putAfter) {
1877 putAfter = mergedNodes.first();
1878 }
1879
1880 filterMergeableNodes(mergedNodes);
1881 {
1882 KisNodeList tempNodes;
1883 std::swap(mergedNodes, tempNodes);
1884 sortMergeableNodes(image->root(), tempNodes, mergedNodes);
1885 }
1886
1887 if (mergedNodes.size() <= 1 &&
1888 (!flattenSingleLayer && mergedNodes.size() == 1)) return;
1889
1890 KisImageSignalVector emitSignals;
1891 emitSignals << ComplexNodeReselectionSignal(KisNodeSP(), KisNodeList(), KisNodeSP(), mergedNodes);
1892
1893
1894
1895 KisNodeList originalNodes = mergedNodes;
1896 KisNodeList invisibleNodes;
1897 mergedNodes = filterInvisibleNodes(originalNodes, &invisibleNodes, &putAfter);
1898
1899 if (mergedNodes.isEmpty()) return;
1900
1901
1902 // make sure we don't add the new layer into a locked group
1904 while (putAfter->parent() && !putAfter->parent()->isEditable()) {
1905 putAfter = putAfter->parent();
1906 }
1907
1915 if (!putAfter->parent()) {
1916 return;
1917 }
1918
1919 KisProcessingApplicator applicator(image, 0,
1921 emitSignals,
1922 actionName);
1923
1924
1925 if (!invisibleNodes.isEmpty() && cleanupNodes) {
1926
1927 /* If the putAfter node is invisible,
1928 * we should instead pick one of the nodes
1929 * to be merged to avoid a null putAfter
1930 * after we remove all invisible layers from
1931 * the image.
1932 * (The assumption is that putAfter is among
1933 * the layers to merge, so if it's invisible,
1934 * it's going to be removed)
1935 */
1936 if (!putAfter->visible()){
1937 putAfter = mergedNodes.first();
1938 }
1939
1940 applicator.applyCommand(
1941 new SimpleRemoveLayers(invisibleNodes,
1942 image),
1945 }
1946
1947 if (mergedNodes.size() > 1 || invisibleNodes.isEmpty()) {
1948 MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes, flags));
1949
1950 // disable key strokes on all colorize masks, all onion skins on
1951 // paint layers and wait until update is finished with a barrier
1952 //
1953 // when doing "new layer from visible" we should undo these changes
1954 // before the action stops, because the source layers are **not**
1955 // removed as a result of this action
1956 applicator.applyCommand(
1957 new EphemeralCommandsWrapper(info,
1958 {
1959 new DisableColorizeKeyStrokes(info),
1960 new DisableOnionSkins(info),
1962 },
1963 cleanupNodes));
1965
1966 applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, false));
1967 applicator.applyCommand(new FillSelectionMasks(info));
1968 applicator.applyCommand(new CreateMergedLayerMultiple(info, layerName), KisStrokeJobData::BARRIER);
1969 applicator.applyCommand(new EphemeralCommandsWrapper(info, { new DisableExtraCompositing(info) } , cleanupNodes));
1971
1972 if (!info->frames.isEmpty()) {
1973 foreach (int frame, info->frames) {
1974 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
1975
1976 applicator.applyCommand(new AddNewFrame(info, frame));
1986
1987 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
1988 }
1989 } else {
1990 applicator.applyCommand(new RefreshHiddenAreas(info));
1993 }
1994
1995 //applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
1996 if (cleanupNodes){
1997 applicator.applyCommand(new CleanUpNodes(info, putAfter, flattenSingleLayer),
2000 } else {
2001 applicator.applyCommand(new UndoEphemeralCommands(info));
2002 applicator.applyCommand(new InsertNode(info, putAfter),
2005 }
2006
2007 applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, true));
2008 }
2009
2010 applicator.end();
2011
2012 }
2013
2014 void mergeMultipleLayers(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, MergeFlags flags)
2015 {
2016 mergeMultipleLayersImpl(image, mergedNodes, putAfter,
2017 false, kundo2_i18n("Merge Selected Nodes"),
2018 true, QString(),
2019 flags);
2020 }
2021
2022 void mergeMultipleNodes(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, MergeFlags flags)
2023 {
2024 if (!tryMergeSelectionMasks(image, mergedNodes, putAfter)) {
2025 mergeMultipleLayers(image, mergedNodes, putAfter, flags);
2026 }
2027 }
2028
2029 void newLayerFromVisible(KisImageSP image, KisNodeSP putAfter, MergeFlags flags)
2030 {
2031 KisNodeList mergedNodes;
2032 mergedNodes << image->root();
2033
2034 mergeMultipleLayersImpl(image, mergedNodes, putAfter,
2035 true, kundo2_i18n("New From Visible"),
2036 false, i18nc("New layer created from all the visible layers", "Visible"),
2037 flags);
2038 }
2039
2042 : m_info(info),
2043 m_putAfter(putAfter){}
2044
2045 void populateChildCommands() override {
2046 KisNodeSP parent;
2047 CleanUpNodes::findPerfectParent(m_info->allSrcNodes(), m_putAfter, parent);
2048
2049 KisLayerSP parentLayer;
2050 do {
2051 parentLayer = qobject_cast<KisLayer*>(parent.data());
2052
2053 parent = parent->parent();
2054 } while(!parentLayer && parent);
2055
2056 KisSelectionSP selection = new KisSelection();
2057
2058 foreach (KisNodeSP node, m_info->allSrcNodes()) {
2059 KisMaskSP mask = dynamic_cast<KisMask*>(node.data());
2060 if (!mask) continue;
2061
2062 selection->pixelSelection()->applySelection(
2064 }
2065
2066 KisSelectionMaskSP mergedMask = new KisSelectionMask(m_info->image, i18n("Selection Mask"));
2067 mergedMask->initSelection(parentLayer);
2068 mergedMask->setSelection(selection);
2069
2070 m_info->dstNode = mergedMask;
2071 }
2072
2073 private:
2076 };
2077
2081
2082 void populateChildCommands() override {
2083 KisSelectionMaskSP mergedMask = dynamic_cast<KisSelectionMask*>(m_info->dstNode.data());
2084 addCommand(new KisActivateSelectionMaskCommand(mergedMask, true));
2085 }
2086
2087 private:
2089 };
2090
2091 bool tryMergeSelectionMasks(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter)
2092 {
2093 QList<KisSelectionMaskSP> selectionMasks;
2094
2095 for (auto it = mergedNodes.begin(); it != mergedNodes.end(); /*noop*/) {
2096 KisSelectionMaskSP mask = dynamic_cast<KisSelectionMask*>(it->data());
2097 if (!mask) {
2098 it = mergedNodes.erase(it);
2099 } else {
2100 selectionMasks.append(mask);
2101 ++it;
2102 }
2103 }
2104
2105 if (mergedNodes.isEmpty()) return false;
2106
2107 KisLayerSP parentLayer = qobject_cast<KisLayer*>(selectionMasks.first()->parent().data());
2108 KIS_ASSERT_RECOVER(parentLayer) { return 0; }
2109
2110 KisImageSignalVector emitSignals;
2111
2112 KisProcessingApplicator applicator(image, 0,
2114 emitSignals,
2115 kundo2_i18n("Merge Selection Masks"));
2116
2117 MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes, None));
2118
2119
2120 applicator.applyCommand(new MergeSelectionMasks(info, putAfter));
2121 applicator.applyCommand(new CleanUpNodes(info, putAfter),
2124 applicator.applyCommand(new ActivateSelectionMask(info));
2125 applicator.end();
2126
2127 return true;
2128 }
2129
2130 void flattenLayer(KisImageSP image, KisLayerSP layer, MergeFlags flags)
2131 {
2132 if (!layer->childCount() && !layer->layerStyle())
2133 return;
2134
2135 KisNodeList mergedNodes;
2136 mergedNodes << layer;
2137
2138 mergeMultipleLayersImpl(image, mergedNodes, layer,
2139 true, kundo2_i18n("Flatten Layer"),
2140 true, QString(),
2141 flags);
2142 }
2143
2144 void flattenImage(KisImageSP image, KisNodeSP activeNode, MergeFlags flags)
2145 {
2146 if (!activeNode) {
2147 activeNode = image->root()->lastChild();
2148 }
2149
2150
2151 KisNodeList mergedNodes;
2152 mergedNodes << image->root();
2153
2154 mergeMultipleLayersImpl(image, mergedNodes, activeNode,
2155 true, kundo2_i18n("Flatten Image"),
2156 true, QString(),
2157 flags);
2158 }
2159
2161 : FlipFlopCommand(finalize, parent),
2162 m_nodes(nodes)
2163 {
2164 }
2170 {
2171 Q_FOREACH(KisNodeSP node, nodes) {
2172 node->setDirty(node->extent());
2173 }
2174 }
2175
2176 KisNodeSP recursiveFindNode(KisNodeSP node, std::function<bool(KisNodeSP)> func)
2177 {
2178 if (func(node)) {
2179 return node;
2180 }
2181
2182 node = node->firstChild();
2183 while (node) {
2184 KisNodeSP resultNode = recursiveFindNode(node, func);
2185 if (resultNode) {
2186 return resultNode;
2187 }
2188 node = node->nextSibling();
2189 }
2190
2191 return 0;
2192 }
2193
2194 KisNodeSP findNodeByUuid(KisNodeSP root, const QUuid &uuid)
2195 {
2196 return recursiveFindNode(root,
2197 [uuid] (KisNodeSP node) {
2198 return node->uuid() == uuid;
2199 });
2200 }
2201
2202 QList<KisNodeSP> findNodesByName(KisNodeSP root, const QString &name, bool recursive, bool partialMatch)
2203 {
2204 KisNodeList nodeList;
2205 KisNodeSP child = root->firstChild();
2206
2207 while (child) {
2208 if (name.isEmpty() || (!partialMatch && child->name() == name) || (partialMatch && child->name().contains(name, Qt::CaseInsensitive))) {
2209 nodeList << child;
2210 }
2211 if (recursive && child->childCount() > 0) {
2212 nodeList << findNodesByName(child, name, recursive, partialMatch);
2213 }
2214 child = child->nextSibling();
2215 }
2216
2217 return nodeList;
2218 }
2219
2220 KisNodeSP findNodeByName(KisNodeSP root, const QString &name)
2221 {
2222 return recursiveFindNode(root,
2223 [name] (KisNodeSP node) {
2224 return node->name() == name;
2225 });
2226 }
2227
2229 {
2230 KisImageSP image = node->image();
2231 if (image)
2232 return image->isolationRootNode();
2233 return nullptr;
2234 }
2235
2237 {
2239 [] (KisNodeSP node) {
2240 KisDelayedUpdateNodeInterface *delayedUpdate =
2241 dynamic_cast<KisDelayedUpdateNodeInterface*>(node.data());
2242 if (delayedUpdate) {
2243 delayedUpdate->forceUpdateTimedNode();
2244 }
2245 });
2246 }
2247
2249 {
2250 return recursiveFindNode(root,
2251 [] (KisNodeSP node) {
2252 KisDelayedUpdateNodeInterface *delayedUpdate =
2253 dynamic_cast<KisDelayedUpdateNodeInterface*>(node.data());
2254
2255 return delayedUpdate ? delayedUpdate->hasPendingTimedUpdates() : false;
2256 });
2257 }
2258
2260 {
2262 [] (KisNodeSP node) {
2263 KisCroppedOriginalLayerInterface *croppedUpdate =
2264 dynamic_cast<KisCroppedOriginalLayerInterface*>(node.data());
2265 if (croppedUpdate) {
2266 croppedUpdate->forceUpdateHiddenAreaOnOriginal();
2267 }
2268 });
2269 }
2270
2272 {
2273 while (node) {
2274 const KisLayer *layer = dynamic_cast<const KisLayer*>(node.data());
2275 if (layer) {
2276 return layer->image();
2277 }
2278
2279 node = node->parent();
2280 }
2281
2282 return 0;
2283 }
2284
2285 namespace Private {
2286 QRect realNodeChangeRect(KisNodeSP rootNode, QRect currentRect = QRect()) {
2287 KisNodeSP node = rootNode->firstChild();
2288
2289 while(node) {
2290 currentRect |= realNodeChangeRect(node, currentRect);
2291 node = node->nextSibling();
2292 }
2293
2294 if (!rootNode->isFakeNode() && !rootNode->projectionLeaf()->isMask()) {
2295 // TODO: it would be better to count up changeRect inside
2296 // node's extent() method
2297 //
2298 // NOTE: when flattening a group layer, we should take the change rect of the
2299 // all the child layers as the source of the change. We are calculating
2300 // the change rect **before** the update itself, therefore rootNode->exactBounds()
2301 // is not yet prepared, hence its exact bounds still contail old values.
2302 currentRect |= rootNode->projectionPlane()->changeRect(rootNode->exactBounds() | currentRect);
2303 }
2304
2305 return currentRect;
2306 }
2307 }
2308
2309namespace Private {
2310 void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea, const QRect &extraUpdateRect) {
2311 QRect realNodeRect = Private::realNodeChangeRect(rootNode) | extraUpdateRect;
2312 if (!preparedArea.contains(realNodeRect)) {
2313
2314 QRegion dirtyRegion = realNodeRect;
2315 dirtyRegion -= preparedArea;
2316
2317 auto rc = dirtyRegion.begin();
2318 while (rc != dirtyRegion.end()) {
2319 image->refreshGraphAsync(rootNode, *rc, realNodeRect);
2320 rc++;
2321 }
2322 }
2323 }
2324} // namespace Private
2325
2326 void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea) {
2327 Private::refreshHiddenAreaAsync(image, rootNode, preparedArea, QRect());
2328 }
2329
2331 {
2332 QRect exactBounds;
2333 recursiveApplyNodes(rootNode, [&exactBounds] (KisNodeSP node) {
2334 exactBounds |= node->projectionPlane()->tightUserVisibleBounds();
2335 });
2336 return exactBounds;
2337 }
2338
2340 {
2341 if (!node) return node;
2342
2343 while (node->parent()) {
2344 node = node->parent();
2345 }
2346 return node;
2347 }
2348
2350 {
2351 int numLayers = 0;
2352 bool hasNonNormalLayers = false;
2353 bool hasTransparentLayer = false;
2354
2355
2356 recursiveApplyNodes(image->root(),
2357 [&numLayers, &hasNonNormalLayers, &hasTransparentLayer, image] (KisNodeSP node) {
2358 if (!node->inherits("KisLayer")) return;
2359
2360 numLayers++;
2361
2362 if (node->exactBounds().isEmpty()) return;
2363
2364 // this is only an approximation! it is not exact!
2365 if (!hasTransparentLayer &&
2366 node->exactBounds() != image->bounds()) {
2367
2368 hasTransparentLayer = true;
2369 }
2370
2371 if (!hasNonNormalLayers &&
2372 node->compositeOpId() != COMPOSITE_OVER) {
2373
2374 hasNonNormalLayers = true;
2375 }
2376 });
2377
2378 return numLayers == 1 || (!hasNonNormalLayers && !hasTransparentLayer);
2379 }
2380
2381 void splitAlphaToMask(KisImageSP image, KisNodeSP node, const QString& maskName)
2382 {
2383 SplitAlphaToMaskInfoSP info( new SplitAlphaToMaskInfo(node->image(), node, maskName) );
2384
2385 KisImageSignalVector emitSignals;
2386 KisProcessingApplicator applicator(image, 0,
2388 emitSignals,
2389 kundo2_i18n("Split Alpha into a Mask"));
2390
2391 applicator.applyCommand(new SimpleAddNode(info->image, info->getMask(), info->node), KisStrokeJobData::BARRIER);
2392 applicator.applyCommand(new InitSplitAlphaSelectionMask(info));
2393 if (info->frames.count() > 0) {
2394 Q_FOREACH(const int& frame, info->frames) {
2395 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
2396 applicator.applyCommand(new AddNewFrame(info->getMask(), frame, info->node));
2398 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
2399 }
2400 } else {
2402 }
2403 applicator.end();
2404 }
2405
2406 std::future<KisNodeSP> convertToPaintLayer(KisImageSP image, KisNodeSP src)
2407 {
2408 //Initialize all operation dependencies.
2409 ConvertToPaintLayerInfoSP info( new ConvertToPaintLayerInfo(image, src) );
2410
2411 if (!info->hasTargetNode())
2413
2414 KisImageSignalVector emitSignals;
2415 KisProcessingApplicator applicator(image, 0, KisProcessingApplicator::NONE, emitSignals, kundo2_i18n("Convert to a Paint Layer"));
2416
2417 applicator.applyCommand(new SimpleAddNode(info->image(), info->targetNode(), info->insertionParent(), info->insertionPutAfter()), KisStrokeJobData::BARRIER);
2418
2419 if (info->frames().count() > 0) {
2420 Q_FOREACH(const int& frame, info->frames()) {
2421 applicator.applyCommand(new SwitchFrameCommand(info->image(), frame, false, info->storage));
2422 applicator.applyCommand(new RefreshDelayedUpdateLayers(info->sourceNodes()), KisStrokeJobData::BARRIER);
2423 applicator.applyCommand(new RefreshHiddenAreas(info->image(), info->sourceNode()), KisStrokeJobData::BARRIER);
2424 applicator.applyCommand(new AddNewFrame(info->targetNode(), frame, info->sourceNode()), KisStrokeJobData::BARRIER);
2425 applicator.applyCommand(new UploadProjectionToFrameCommand(info->sourceNode(), info->targetNode(), frame));
2426 applicator.applyCommand(new SwitchFrameCommand(info->image(), frame, true, info->storage));
2427 }
2428 }
2429
2430 applicator.applyCommand(new SimpleRemoveLayers(info->toRemove(), info->image()));
2431
2432 applicator.end();
2433
2434 return kismpl::then(applicator.successfullyCompletedFuture(),
2435 [node = info->targetNode()] (std::future<bool> completed) {
2436 return completed.get() ? node : KisNodeSP();
2437 });
2438 }
2439
2440 //===========================================================
2441
2443 {
2445 KisPaintDeviceSP paintDevice = node->paintDevice();
2446 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, -1);
2447
2448 if (!paintDevice->keyframeChannel()) {
2449 return -1;
2450 }
2451
2452 const int activeTime = paintDevice->keyframeChannel()->activeKeyframeTime();
2453 KisRasterKeyframeSP keyframe = paintDevice->keyframeChannel()->activeKeyframeAt<KisRasterKeyframe>(activeTime);
2455
2456 return keyframe->frameID();
2457 }
2458
2460 {
2462 KisPaintDeviceSP paintDevice = node->paintDevice();
2463 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, -1);
2464
2465 if (!paintDevice->keyframeChannel()) {
2466 return -1;
2467 }
2468
2469 return paintDevice->keyframeChannel()->activeKeyframeTime();
2470 }
2471
2473 {
2475 KisPaintDeviceSP paintDevice = node->paintDevice();
2477 if (!paintDevice->keyframeChannel()) {
2478 return KisTimeSpan::infinite(0);
2479 }
2480
2481 return paintDevice->keyframeChannel()->affectedFrames(time);
2482 }
2483
2484 QSet<int> fetchLayerIdenticalRasterFrameTimes(KisNodeSP node, const int &frameTime)
2485 {
2486 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(node, QSet<int>());
2487 KisPaintDeviceSP paintDevice = node->paintDevice();
2488 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, QSet<int>());
2489 if (!paintDevice->keyframeChannel()) {
2490 return QSet<int>();
2491 }
2492
2493 return paintDevice->keyframeChannel()->clonesOf(node.data(), frameTime);
2494 }
2495
2496 /* Finds all frames matching a specific frame ID. useful to filter out duplicate frames. */
2497 QSet<int> fetchLayerRasterFrameTimesMatchingID(KisNodeSP node, const int frameID) {
2498 KIS_ASSERT(node);
2499 KisRasterKeyframeChannel* rasterChannel = dynamic_cast<KisRasterKeyframeChannel*>(node->getKeyframeChannel(KisKeyframeChannel::Raster.id(), false));
2500
2501 if (!rasterChannel) {
2502 return QSet<int>();
2503 }
2504
2505 return rasterChannel->timesForFrameID(frameID);
2506 }
2507
2508 QSet<int> fetchLayerRasterIDsAtTimes(KisNodeSP node, const QSet<int> &times)
2509 {
2510 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(node, QSet<int>());
2511 KisPaintDeviceSP paintDevice = node->paintDevice();
2512 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, QSet<int>());
2513 if (!paintDevice->keyframeChannel()) {
2514 return QSet<int>();
2515 }
2516
2517 QSet<int> frameIDs;
2518
2519 Q_FOREACH( const int& frame, times ) {
2521 frameIDs << raster->frameID();
2522 }
2523
2524 return frameIDs;
2525 }
2526
2527 QSet<int> filterTimesForOnlyRasterKeyedTimes(KisNodeSP node, const QSet<int> &times)
2528 {
2530 KisPaintDeviceSP paintDevice = node->paintDevice();
2531 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, times);
2532 if (!paintDevice->keyframeChannel()) {
2533 return times;
2534 }
2535
2536 return paintDevice->keyframeChannel()->allKeyframeTimes().intersect(times);
2537 }
2538
2539 QSet<int> fetchLayerUniqueRasterTimesMatchingIDs(KisNodeSP node, QSet<int>& frameIDs)
2540 {
2541 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(node, QSet<int>());
2542 KisPaintDeviceSP paintDevice = node->paintDevice();
2543 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, QSet<int>());
2544 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice->framesInterface(), QSet<int>());
2545
2546 QSet<int> uniqueTimes;
2547
2548 Q_FOREACH( const int& id, frameIDs) {
2549 QSet<int> times = fetchLayerRasterFrameTimesMatchingID(node, id);
2550 if (times.count() > 0) {
2551 uniqueTimes.insert(*times.begin());
2552 }
2553 }
2554
2555 return uniqueTimes;
2556 }
2557
2558 QSet<int> fetchUniqueFrameTimes(KisNodeSP node, QSet<int> selectedTimes, bool filterActiveFrameID)
2559 {
2560 if (selectedTimes.isEmpty() || !node->supportsKeyframeChannel(KisKeyframeChannel::Raster.id()))
2561 return selectedTimes;
2562
2563 // Convert a set of selected keyframe times into set of selected "frameIDs"...
2564 QSet<int> selectedFrameIDs = KisLayerUtils::fetchLayerRasterIDsAtTimes(node, selectedTimes);
2565
2566 if (filterActiveFrameID) {
2567 // Current frame was already filtered e.g. during filter preview in `KisFilterManager::apply`...
2568 // So let's remove it...
2569 const int currentActiveFrameID = KisLayerUtils::fetchLayerActiveRasterFrameID(node);
2570 selectedFrameIDs.remove(currentActiveFrameID);
2571 }
2572
2573 // Convert frameIDs to any arbitrary frame time associated with the frameID...
2574 QSet<int> uniqueFrameTimes = node->paintDevice()->framesInterface() ? KisLayerUtils::fetchLayerUniqueRasterTimesMatchingIDs(node, selectedFrameIDs) : QSet<int>();
2575
2576 return uniqueFrameTimes;
2577 }
2578
2579}
KisMagneticGraph::vertex_descriptor target(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
@ SELECTION_ADD
const quint8 OPACITY_OPAQUE_U8
const QString COMPOSITE_OVER
const QString COMPOSITE_BEHIND
KUndo2Command(KUndo2Command *parent=0)
KisPaintDeviceSP coloringProjection() const
virtual void forceUpdateHiddenAreaOnOriginal()=0
virtual QRect bounds() const =0
The KisDelayedUpdateNodeInterface class is an interface for nodes that delay their real updates with ...
virtual void forceUpdateTimedNode()=0
forceUpdateTimedNode forces the node to regenerate its project. The update might be asynchronous,...
virtual bool hasPendingTimedUpdates() const =0
The command for adding a layer.
The command for layer moves inside the layer stack.
void emitNotification(KisImageSignalType type)
void refreshGraphAsync(KisNodeSP root, const QVector< QRect > &rects, const QRect &cropRect, KisProjectionUpdateFlags flags=KisProjectionUpdateFlag::None) override
KisGroupLayerSP rootLayer() const
const KoColorSpace * colorSpace() const
KisImageAnimationInterface * animationInterface() const
QString nextLayerName(const QString &baseName="") const
Definition kis_image.cc:715
QVector< QRect > enableUIUpdates() override
KisNodeSP isolationRootNode() const
KisImageSignalRouter * signalRouter()
QRect bounds() const override
KisKeyframeChannel stores and manages KisKeyframes. Maps units of time to virtual keyframe values....
QSet< int > allKeyframeTimes() const
Get a set of all integer times that map to a keyframe.
static const KoID Raster
KisKeyframeSP keyframeAt(int time) const
Get a keyframe at specified time. Used primarily when the value of a given keyframe is needed.
void addKeyframe(int time, KUndo2Command *parentUndoCmd=nullptr)
Add a new keyframe to the channel at the specified time.
virtual KisTimeSpan affectedFrames(int time) const
Get the set of frames affected by any changes to the value or content of the active keyframe at the g...
KisKeyframeSP activeKeyframeAt(int time) const
int activeKeyframeTime(int time) const
Get the time of the active keyframe. Useful for snapping any time to that of the most recent keyframe...
static QVariant nodeProperty(KisNodeSP node, const KoID &id, const QVariant &defaultValue)
static void setNodeProperty(KisBaseNode::PropertyList *props, const KoID &id, const QVariant &value)
KeepNodesSelectedCommand(const KisNodeList &selectedBefore, const KisNodeList &selectedAfter, KisNodeSP activeBefore, KisNodeSP activeAfter, KisImageSP image, bool finalize, KUndo2Command *parent=0)
static void updateNodes(const KisNodeList &nodes)
KisSimpleUpdateCommand(KisNodeList nodes, bool finalize, KUndo2Command *parent=0)
virtual void addCommandImpl(KUndo2Command *cmd)=0
void safeReplaceMultipleNodes(KisNodeList removedNodes, KisImageSP image, std::optional< ReplacementNode > replacementNode)
void safeRemoveMultipleNodes(KisNodeList nodes, KisImageSP image)
bool checkIsSourceForClone(KisNodeSP src, const KisNodeList &nodes)
static bool scanForLastLayer(KisImageWSP image, KisNodeList nodesToRemove)
virtual void merge(Store *dst, QList< const Store * > srcs, QList< double > scores) const =0
The command for setting the composite op.
The command for changing the property list of a layer.
void writeFrameToDevice(int frameId, KisPaintDeviceSP targetDevice)
void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice)
KisRasterKeyframeChannel * keyframeChannel() const
void setDefaultPixel(const KoColor &defPixel)
virtual const KoColorSpace * compositionSourceColorSpace() const
KisPaintDeviceFramesInterface * framesInterface()
QRect exactBounds() const
QRect extent() const
const KoColorSpace * colorSpace() const
KoColor defaultPixel() const
KisDefaultBoundsBaseSP defaultBounds() const
static void copyAreaOptimized(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &originalSrcRect)
void applyCommand(KUndo2Command *command, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
std::future< bool > && successfullyCompletedFuture()
The KisRasterKeyframeChannel is a concrete KisKeyframeChannel subclass that stores and manages KisRas...
QSet< int > timesForFrameID(int frameID) const
The KisRasterKeyframe class is a concrete subclass of KisKeyframe that wraps a physical raster image ...
The KisScalarKeyframeChannel is a concrete KisKeyframeChannel subclass that stores and manages KisSca...
virtual KisTimeSpan identicalFrames(int time) const override
Get a span of times for which the channel gives identical results compared to frame at a given time....
ALWAYS_INLINE quint8 * rawData()
int start() const
bool isInfinite() const
static KisTimeSpan infinite(int start)
int end() const
bool isValid() const
virtual void setOpacity(quint8 *pixels, quint8 alpha, qint32 nPixels) const =0
virtual quint8 opacityU8(const quint8 *pixel) const =0
KoColor convertedTo(const KoColorSpace *cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
Definition KoColor.cpp:163
QString id() const
Definition KoID.cpp:63
#define KIS_ASSERT_RECOVER(cond)
Definition kis_assert.h:55
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define bounds(x, a, b)
QList< KisNodeSP > KisNodeList
Definition kis_types.h:264
KisSharedPtr< KisNode > KisNodeSP
Definition kis_types.h:86
KUndo2MagicString kundo2_i18n(const char *text)
QRect realNodeChangeRect(KisNodeSP rootNode, QRect currentRect=QRect())
void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea, const QRect &extraUpdateRect)
void flattenImage(KisImageSP image, KisNodeSP activeNode, MergeFlags flags)
bool checkIsCloneOf(KisNodeSP node, const KisNodeList &nodes)
KisNodeSP findIsolationRoot(KisNodeSP node)
QSet< int > fetchLayerFrames(KisNodeSP node)
QSet< int > fetchUniqueFrameTimes(KisNodeSP node, QSet< int > selectedTimes, bool filterActiveFrameID)
bool hasDelayedNodeWithUpdates(KisNodeSP root)
void updateFrameJobs(FrameJobs *jobs, KisNodeSP node)
QSet< int > filterTimesForOnlyRasterKeyedTimes(KisNodeSP node, const QSet< int > &times)
void updateFrameJobsRecursive(FrameJobs *jobs, KisNodeSP rootNode)
KisNodeSP recursiveFindNode(KisNodeSP node, std::function< bool(KisNodeSP)> func)
void splitAlphaToMask(KisImageSP image, KisNodeSP node, const QString &maskName)
void changeImageDefaultProjectionColor(KisImageSP image, const KoColor &color)
KisTimeSpan fetchLayerActiveRasterFrameSpan(KisNodeSP node, const int time)
KisNodeList sortAndFilterMergeableInternalNodes(KisNodeList nodes, bool allowMasks)
QMap< int, QSet< KisNodeSP > > FrameJobs
int fetchLayerActiveRasterFrameID(KisNodeSP node)
QSharedPointer< SplitAlphaToMaskInfo > SplitAlphaToMaskInfoSP
QSharedPointer< MergeDownInfo > MergeDownInfoSP
void sortMergeableNodes(KisNodeSP root, KisNodeList &inputNodes, KisNodeList &outputNodes)
QRect recursiveTightNodeVisibleBounds(KisNodeSP rootNode)
KisImageSP findImageByHierarchy(KisNodeSP node)
QSet< int > fetchLayerUniqueRasterTimesMatchingIDs(KisNodeSP node, QSet< int > &frameIDs)
KisNodeSP findNodeByName(KisNodeSP root, const QString &name)
KisNodeList filterInvisibleNodes(const KisNodeList &nodes, KisNodeList *invisibleNodes, KisNodeSP *putAfter)
void flattenLayer(KisImageSP image, KisLayerSP layer, MergeFlags flags)
QSet< int > fetchLayerRasterIDsAtTimes(KisNodeSP node, const QSet< int > &times)
KisNodeSP findRoot(KisNodeSP node)
QList< KisNodeSP > findNodesByName(KisNodeSP root, const QString &name, bool recursive, bool partialMatch)
QSharedPointer< ConvertToPaintLayerInfo > ConvertToPaintLayerInfoSP
KisNodeList findNodesWithProps(KisNodeSP root, const KoProperties &props, bool excludeRoot)
void addCopyOfNameTag(KisNodeSP node)
KisNodeSP findNodeByUuid(KisNodeSP root, const QUuid &uuid)
void newLayerFromVisible(KisImageSP image, KisNodeSP putAfter, MergeFlags flags)
void filterUnlockedNodes(KisNodeList &nodes)
void mergeMultipleLayersImpl(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, bool flattenSingleLayer, const KUndo2MagicString &actionName, bool cleanupNodes=true, const QString layerName=QString(), MergeFlags flags=None)
bool canChangeImageProfileInvisibly(KisImageSP image)
void recursiveApplyNodes(NodePointer node, Functor func)
int fetchLayerActiveRasterFrameTime(KisNodeSP node)
void splitNonRemovableNodes(KisNodeList &nodesToRemove, KisNodeList &_nodesToHide)
KisNodeList sortAndFilterAnyMergeableNodesSafe(const KisNodeList &nodes, KisImageSP image)
void filterMergeableNodes(KisNodeList &nodes, bool allowMasks)
void forceAllHiddenOriginalsUpdate(KisNodeSP root)
QSet< int > fetchLayerIdenticalRasterFrameTimes(KisNodeSP node, const int &frameTime)
bool checkIsChildOf(KisNodeSP node, const KisNodeList &parents)
void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea)
void mergeMultipleLayers(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, MergeFlags flags)
void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy *strategy, MergeFlags flags)
bool tryMergeSelectionMasks(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter)
std::future< KisNodeSP > convertToPaintLayer(KisImageSP image, KisNodeSP src)
QSet< int > fetchLayerRasterFrameTimesMatchingID(KisNodeSP node, const int frameID)
void forceAllDelayedNodesUpdate(KisNodeSP root)
QSharedPointer< MergeDownInfoBase > MergeDownInfoBaseSP
QSet< int > fetchLayerFramesRecursive(KisNodeSP rootNode)
void fetchSelectionMasks(KisNodeList mergedNodes, QVector< KisSelectionMaskSP > &selectionMasks)
KisNodeList sortMergeableInternalNodes(KisNodeList nodes)
QSharedPointer< MergeMultipleInfo > MergeMultipleInfoSP
void mergeMultipleNodes(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, MergeFlags flags)
auto then(std::future< T > &&future, Function &&func) -> std::future< decltype(func(std::move(future)))>
std::future< void > make_ready_future()
QMap< QString, KisKeyframeChannel * > keyframeChannels
QUuid uuid() const
virtual KisPaintDeviceSP projection() const =0
const QString & compositeOpId() const
bool isEditable(bool checkVisibility=true) const
bool isPinnedToTimeline() const
KisKeyframeChannel * getKeyframeChannel(const QString &id, bool create)
virtual QRect exactBounds() const
virtual KisPaintDeviceSP original() const =0
void setName(const QString &name)
KisImageWSP image
virtual QRect extent() const
bool userLocked() const
bool isAnimated() const
virtual KisPaintDeviceSP paintDevice() const =0
virtual PropertyList sectionModelProperties() const
QString name() const
void setCompositeOpId(const QString &compositeOpId)
virtual bool isFakeNode() const
quint8 opacity() const
bool check(const KoProperties &properties) const
virtual bool visible(bool recursive=false) const
virtual bool supportsKeyframeChannel(const QString &id)
KisLayerSP copyFrom
KisLayerSP reincarnateAsPaintLayer() const
The AggregateCommand struct is a command with delayed initialization. On first redo() populateChildCo...
void addCommand(KUndo2Command *cmd)
ActivateSelectionMask(MergeDownInfoBaseSP info)
MergeDownInfoBaseSP m_mergeInfo
AddNewFrame(KisNodeSP node, int frame, KisNodeSP source)
AddNewFrame(KisNodeSP node, int frame)
AddNewFrame(KisNodeSP node, int frame, KisNodeList sampleNodes)
void applyKeyframeColorLabel(KisKeyframeSP dstKeyframe, KisNodeList srcNodes)
AddNewFrame(MergeDownInfoBaseSP info, int frame)
static void findPerfectParent(KisNodeList nodesToDelete, KisNodeSP &putAfter, KisNodeSP &parent)
CleanUpNodes(MergeDownInfoBaseSP info, KisNodeSP putAfter, bool relinkClones=false)
void addCommandImpl(KUndo2Command *cmd) override
ConvertToPaintLayerInfo(KisImageSP image, KisNodeSP node)
SwitchFrameCommand::SharedStorageSP storage
CreateMergedLayerMultiple(MergeMultipleInfoSP info, const QString name=QString())
CreateMergedLayer(MergeDownInfoSP info)
DisableColorizeKeyStrokes(MergeDownInfoBaseSP info)
DisableExtraCompositing(MergeMultipleInfoSP info)
DisableOnionSkins(MergeDownInfoBaseSP info)
DisablePassThroughForHeadsOnly(MergeDownInfoBaseSP info, bool skipIfDstIsGroup=false)
EphemeralCommandsWrapper(MergeMultipleInfoSP info, QVector< KUndo2Command * > commands, bool cleanupNodes)
FillSelectionMasks(MergeDownInfoBaseSP info)
InitSplitAlphaSelectionMask(SplitAlphaToMaskInfoSP info)
void populateChildCommands() override
InsertNode(MergeDownInfoBaseSP info, KisNodeSP putAfter)
virtual void addCommandImpl(KUndo2Command *cmd)
KeepMergedNodesSelected(MergeDownInfoSP info, bool finalizing)
KeepMergedNodesSelected(MergeMultipleInfoSP info, KisNodeSP putAfter, bool finalizing)
SwitchFrameCommand::SharedStorageSP storage
QVector< KisSelectionMaskSP > selectionMasks
virtual KisNodeList allSrcNodes()=0
MergeDownInfo(KisImageSP _image, KisLayerSP _prevLayer, KisLayerSP _currLayer, MergeFlags flags)
KisNodeList allSrcNodes() override
MergeLayersMultiple(MergeMultipleInfoSP info)
MergeLayers(MergeDownInfoSP info, bool skipMergingSourceLayer)
void populateChildCommands() override
const KisMetaData::MergeStrategy * m_strategy
MergeMetaData(MergeDownInfoSP info, const KisMetaData::MergeStrategy *strategy)
MergeMultipleInfo(KisImageSP _image, KisNodeList _mergedNodes, MergeFlags flags)
QScopedPointer< KisSurrogateUndoStore > ephemeralCommandsStore
MergeSelectionMasks(MergeDownInfoBaseSP info, KisNodeSP putAfter)
RefreshDelayedUpdateLayers(MergeDownInfoBaseSP info)
RefreshHiddenAreas(KisImageSP image, KisNodeSP node)
RefreshHiddenAreas(MergeDownInfoBaseSP info, refresh_entire_image_t)
RefreshHiddenAreas(MergeDownInfoBaseSP info)
static constexpr refresh_entire_image_t refresh_entire_image
virtual void addCommandImpl(KUndo2Command *cmd)
SimpleAddNode(KisImageSP image, KisNodeSP toAdd, KisNodeSP parent=0, KisNodeSP putAfter=0)
SimpleRemoveLayers(const KisNodeList &nodes, KisImageSP image)
void addCommandImpl(KUndo2Command *cmd) override
SplitAlphaCommand(SplitAlphaToMaskInfoSP info)
SwitchFrameCommand::SharedStorageSP storage
SplitAlphaToMaskInfo(KisImageSP _image, KisNodeSP _node, const QString &maskName)
The SwitchFrameCommand struct Switches to frame with undo/redo support.
UndoEphemeralCommands(MergeMultipleInfoSP info)
UploadProjectionToFrameCommand(KisNodeSP src, KisNodeSP target, int frame)
void disableAlphaChannel(bool disable)
Definition kis_layer.cc:319
QBitArray channelFlags
Definition kis_layer.cc:167
virtual KisSelectionMaskSP selectionMask() const
Definition kis_layer.cc:498
bool alphaChannelDisabled() const
Definition kis_layer.cc:334
KisPSDLayerStyleSP layerStyle
Definition kis_layer.cc:171
const QList< KisCloneLayerWSP > registeredClones() const
Definition kis_layer.cc:478
void initSelection(KisSelectionSP copyFrom, KisLayerSP parentLayer)
initSelection initializes the selection for the mask from the given selection's projection.
Definition kis_mask.cc:157
KisSelectionSP selection
Definition kis_mask.cc:44
KisPaintDeviceSP paintDevice() const override
Definition kis_mask.cc:223
KisNodeSP prevSibling() const
Definition kis_node.cpp:402
KisNodeSP firstChild() const
Definition kis_node.cpp:361
QList< KisNodeSP > childNodes(const QStringList &nodeTypes, const KoProperties &properties) const
Definition kis_node.cpp:439
virtual KisAbstractProjectionPlaneSP projectionPlane() const
Definition kis_node.cpp:240
KisProjectionLeafSP projectionLeaf
Definition kis_node.cpp:93
quint32 childCount() const
Definition kis_node.cpp:414
KisNodeWSP parent
Definition kis_node.cpp:86
KisNodeSP lastChild() const
Definition kis_node.cpp:367
KisNodeSP nextSibling() const
Definition kis_node.cpp:408
KisNodeGraphListener * graphListener
Definition kis_node.cpp:87
virtual void setDirty()
Definition kis_node.cpp:577
virtual bool allowAsChild(KisNodeSP) const =0
void setOnionSkinEnabled(bool state)
bool onionSkinEnabled() const
void setAlphaLocked(bool lock)
void applySelection(KisPixelSelectionSP selection, SelectionAction action)
void setSelection(KisSelectionSP selection)
Set the selection of this adjustment layer to a copy of selection.
KisPixelSelectionSP pixelSelection