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 {
2231 [] (KisNodeSP node) {
2232 KisDelayedUpdateNodeInterface *delayedUpdate =
2233 dynamic_cast<KisDelayedUpdateNodeInterface*>(node.data());
2234 if (delayedUpdate) {
2235 delayedUpdate->forceUpdateTimedNode();
2236 }
2237 });
2238 }
2239
2241 {
2242 return recursiveFindNode(root,
2243 [] (KisNodeSP node) {
2244 KisDelayedUpdateNodeInterface *delayedUpdate =
2245 dynamic_cast<KisDelayedUpdateNodeInterface*>(node.data());
2246
2247 return delayedUpdate ? delayedUpdate->hasPendingTimedUpdates() : false;
2248 });
2249 }
2250
2252 {
2254 [] (KisNodeSP node) {
2255 KisCroppedOriginalLayerInterface *croppedUpdate =
2256 dynamic_cast<KisCroppedOriginalLayerInterface*>(node.data());
2257 if (croppedUpdate) {
2258 croppedUpdate->forceUpdateHiddenAreaOnOriginal();
2259 }
2260 });
2261 }
2262
2264 {
2265 while (node) {
2266 const KisLayer *layer = dynamic_cast<const KisLayer*>(node.data());
2267 if (layer) {
2268 return layer->image();
2269 }
2270
2271 node = node->parent();
2272 }
2273
2274 return 0;
2275 }
2276
2277 namespace Private {
2278 QRect realNodeChangeRect(KisNodeSP rootNode, QRect currentRect = QRect()) {
2279 KisNodeSP node = rootNode->firstChild();
2280
2281 while(node) {
2282 currentRect |= realNodeChangeRect(node, currentRect);
2283 node = node->nextSibling();
2284 }
2285
2286 if (!rootNode->isFakeNode() && !rootNode->projectionLeaf()->isMask()) {
2287 // TODO: it would be better to count up changeRect inside
2288 // node's extent() method
2289 //
2290 // NOTE: when flattening a group layer, we should take the change rect of the
2291 // all the child layers as the source of the change. We are calculating
2292 // the change rect **before** the update itself, therefore rootNode->exactBounds()
2293 // is not yet prepared, hence its exact bounds still contail old values.
2294 currentRect |= rootNode->projectionPlane()->changeRect(rootNode->exactBounds() | currentRect);
2295 }
2296
2297 return currentRect;
2298 }
2299 }
2300
2301namespace Private {
2302 void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea, const QRect &extraUpdateRect) {
2303 QRect realNodeRect = Private::realNodeChangeRect(rootNode) | extraUpdateRect;
2304 if (!preparedArea.contains(realNodeRect)) {
2305
2306 QRegion dirtyRegion = realNodeRect;
2307 dirtyRegion -= preparedArea;
2308
2309 auto rc = dirtyRegion.begin();
2310 while (rc != dirtyRegion.end()) {
2311 image->refreshGraphAsync(rootNode, *rc, realNodeRect);
2312 rc++;
2313 }
2314 }
2315 }
2316} // namespace Private
2317
2318 void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea) {
2319 Private::refreshHiddenAreaAsync(image, rootNode, preparedArea, QRect());
2320 }
2321
2323 {
2324 QRect exactBounds;
2325 recursiveApplyNodes(rootNode, [&exactBounds] (KisNodeSP node) {
2326 exactBounds |= node->projectionPlane()->tightUserVisibleBounds();
2327 });
2328 return exactBounds;
2329 }
2330
2332 {
2333 if (!node) return node;
2334
2335 while (node->parent()) {
2336 node = node->parent();
2337 }
2338 return node;
2339 }
2340
2342 {
2343 int numLayers = 0;
2344 bool hasNonNormalLayers = false;
2345 bool hasTransparentLayer = false;
2346
2347
2348 recursiveApplyNodes(image->root(),
2349 [&numLayers, &hasNonNormalLayers, &hasTransparentLayer, image] (KisNodeSP node) {
2350 if (!node->inherits("KisLayer")) return;
2351
2352 numLayers++;
2353
2354 if (node->exactBounds().isEmpty()) return;
2355
2356 // this is only an approximation! it is not exact!
2357 if (!hasTransparentLayer &&
2358 node->exactBounds() != image->bounds()) {
2359
2360 hasTransparentLayer = true;
2361 }
2362
2363 if (!hasNonNormalLayers &&
2364 node->compositeOpId() != COMPOSITE_OVER) {
2365
2366 hasNonNormalLayers = true;
2367 }
2368 });
2369
2370 return numLayers == 1 || (!hasNonNormalLayers && !hasTransparentLayer);
2371 }
2372
2373 void splitAlphaToMask(KisImageSP image, KisNodeSP node, const QString& maskName)
2374 {
2375 SplitAlphaToMaskInfoSP info( new SplitAlphaToMaskInfo(node->image(), node, maskName) );
2376
2377 KisImageSignalVector emitSignals;
2378 KisProcessingApplicator applicator(image, 0,
2380 emitSignals,
2381 kundo2_i18n("Split Alpha into a Mask"));
2382
2383 applicator.applyCommand(new SimpleAddNode(info->image, info->getMask(), info->node), KisStrokeJobData::BARRIER);
2384 applicator.applyCommand(new InitSplitAlphaSelectionMask(info));
2385 if (info->frames.count() > 0) {
2386 Q_FOREACH(const int& frame, info->frames) {
2387 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
2388 applicator.applyCommand(new AddNewFrame(info->getMask(), frame, info->node));
2390 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
2391 }
2392 } else {
2394 }
2395 applicator.end();
2396 }
2397
2398 std::future<KisNodeSP> convertToPaintLayer(KisImageSP image, KisNodeSP src)
2399 {
2400 //Initialize all operation dependencies.
2401 ConvertToPaintLayerInfoSP info( new ConvertToPaintLayerInfo(image, src) );
2402
2403 if (!info->hasTargetNode())
2405
2406 KisImageSignalVector emitSignals;
2407 KisProcessingApplicator applicator(image, 0, KisProcessingApplicator::NONE, emitSignals, kundo2_i18n("Convert to a Paint Layer"));
2408
2409 applicator.applyCommand(new SimpleAddNode(info->image(), info->targetNode(), info->insertionParent(), info->insertionPutAfter()), KisStrokeJobData::BARRIER);
2410
2411 if (info->frames().count() > 0) {
2412 Q_FOREACH(const int& frame, info->frames()) {
2413 applicator.applyCommand(new SwitchFrameCommand(info->image(), frame, false, info->storage));
2414 applicator.applyCommand(new RefreshDelayedUpdateLayers(info->sourceNodes()), KisStrokeJobData::BARRIER);
2415 applicator.applyCommand(new RefreshHiddenAreas(info->image(), info->sourceNode()), KisStrokeJobData::BARRIER);
2416 applicator.applyCommand(new AddNewFrame(info->targetNode(), frame, info->sourceNode()), KisStrokeJobData::BARRIER);
2417 applicator.applyCommand(new UploadProjectionToFrameCommand(info->sourceNode(), info->targetNode(), frame));
2418 applicator.applyCommand(new SwitchFrameCommand(info->image(), frame, true, info->storage));
2419 }
2420 }
2421
2422 applicator.applyCommand(new SimpleRemoveLayers(info->toRemove(), info->image()));
2423
2424 applicator.end();
2425
2426 return kismpl::then(applicator.successfullyCompletedFuture(),
2427 [node = info->targetNode()] (std::future<bool> completed) {
2428 return completed.get() ? node : KisNodeSP();
2429 });
2430 }
2431
2432 //===========================================================
2433
2435 {
2437 KisPaintDeviceSP paintDevice = node->paintDevice();
2438 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, -1);
2439
2440 if (!paintDevice->keyframeChannel()) {
2441 return -1;
2442 }
2443
2444 const int activeTime = paintDevice->keyframeChannel()->activeKeyframeTime();
2445 KisRasterKeyframeSP keyframe = paintDevice->keyframeChannel()->activeKeyframeAt<KisRasterKeyframe>(activeTime);
2447
2448 return keyframe->frameID();
2449 }
2450
2452 {
2454 KisPaintDeviceSP paintDevice = node->paintDevice();
2455 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, -1);
2456
2457 if (!paintDevice->keyframeChannel()) {
2458 return -1;
2459 }
2460
2461 return paintDevice->keyframeChannel()->activeKeyframeTime();
2462 }
2463
2465 {
2467 KisPaintDeviceSP paintDevice = node->paintDevice();
2469 if (!paintDevice->keyframeChannel()) {
2470 return KisTimeSpan::infinite(0);
2471 }
2472
2473 return paintDevice->keyframeChannel()->affectedFrames(time);
2474 }
2475
2476 QSet<int> fetchLayerIdenticalRasterFrameTimes(KisNodeSP node, const int &frameTime)
2477 {
2478 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(node, QSet<int>());
2479 KisPaintDeviceSP paintDevice = node->paintDevice();
2480 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, QSet<int>());
2481 if (!paintDevice->keyframeChannel()) {
2482 return QSet<int>();
2483 }
2484
2485 return paintDevice->keyframeChannel()->clonesOf(node.data(), frameTime);
2486 }
2487
2488 /* Finds all frames matching a specific frame ID. useful to filter out duplicate frames. */
2489 QSet<int> fetchLayerRasterFrameTimesMatchingID(KisNodeSP node, const int frameID) {
2490 KIS_ASSERT(node);
2491 KisRasterKeyframeChannel* rasterChannel = dynamic_cast<KisRasterKeyframeChannel*>(node->getKeyframeChannel(KisKeyframeChannel::Raster.id(), false));
2492
2493 if (!rasterChannel) {
2494 return QSet<int>();
2495 }
2496
2497 return rasterChannel->timesForFrameID(frameID);
2498 }
2499
2500 QSet<int> fetchLayerRasterIDsAtTimes(KisNodeSP node, const QSet<int> &times)
2501 {
2502 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(node, QSet<int>());
2503 KisPaintDeviceSP paintDevice = node->paintDevice();
2504 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, QSet<int>());
2505 if (!paintDevice->keyframeChannel()) {
2506 return QSet<int>();
2507 }
2508
2509 QSet<int> frameIDs;
2510
2511 Q_FOREACH( const int& frame, times ) {
2513 frameIDs << raster->frameID();
2514 }
2515
2516 return frameIDs;
2517 }
2518
2519 QSet<int> filterTimesForOnlyRasterKeyedTimes(KisNodeSP node, const QSet<int> &times)
2520 {
2522 KisPaintDeviceSP paintDevice = node->paintDevice();
2523 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, times);
2524 if (!paintDevice->keyframeChannel()) {
2525 return times;
2526 }
2527
2528 return paintDevice->keyframeChannel()->allKeyframeTimes().intersect(times);
2529 }
2530
2531 QSet<int> fetchLayerUniqueRasterTimesMatchingIDs(KisNodeSP node, QSet<int>& frameIDs)
2532 {
2533 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(node, QSet<int>());
2534 KisPaintDeviceSP paintDevice = node->paintDevice();
2535 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, QSet<int>());
2536 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice->framesInterface(), QSet<int>());
2537
2538 QSet<int> uniqueTimes;
2539
2540 Q_FOREACH( const int& id, frameIDs) {
2541 QSet<int> times = fetchLayerRasterFrameTimesMatchingID(node, id);
2542 if (times.count() > 0) {
2543 uniqueTimes.insert(*times.begin());
2544 }
2545 }
2546
2547 return uniqueTimes;
2548 }
2549
2550 QSet<int> fetchUniqueFrameTimes(KisNodeSP node, QSet<int> selectedTimes, bool filterActiveFrameID)
2551 {
2552 if (selectedTimes.isEmpty() || !node->supportsKeyframeChannel(KisKeyframeChannel::Raster.id()))
2553 return selectedTimes;
2554
2555 // Convert a set of selected keyframe times into set of selected "frameIDs"...
2556 QSet<int> selectedFrameIDs = KisLayerUtils::fetchLayerRasterIDsAtTimes(node, selectedTimes);
2557
2558 if (filterActiveFrameID) {
2559 // Current frame was already filtered e.g. during filter preview in `KisFilterManager::apply`...
2560 // So let's remove it...
2561 const int currentActiveFrameID = KisLayerUtils::fetchLayerActiveRasterFrameID(node);
2562 selectedFrameIDs.remove(currentActiveFrameID);
2563 }
2564
2565 // Convert frameIDs to any arbitrary frame time associated with the frameID...
2566 QSet<int> uniqueFrameTimes = node->paintDevice()->framesInterface() ? KisLayerUtils::fetchLayerUniqueRasterTimesMatchingIDs(node, selectedFrameIDs) : QSet<int>();
2567
2568 return uniqueFrameTimes;
2569 }
2570
2571}
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
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)
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