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 KisPaintLayerSP rasterizedDstLayer; // is non-null when merging takes a rasterization path
95
97 QSet<int> frames;
98 bool pinnedToTimeline = false;
99 bool enableOnionSkins = false;
100
102
104 return qobject_cast<KisLayer*>(dstNode.data());
105 }
106 };
107
109 SplitAlphaToMaskInfo(KisImageSP _image, KisNodeSP _node, const QString& maskName)
110 : image(_image)
111 , node(_node)
112 , storage(new SwitchFrameCommand::SharedStorage())
113 {
115 mask = new KisTransparencyMask(image, maskName);
116 }
117
121 QSet<int> frames;
122
126
128 return mask;
129 }
130
132 return qobject_cast<KisLayer*>(node.data());
133 }
134
135 private:
137
138 };
139
142 KisLayerSP _prevLayer,
143 KisLayerSP _currLayer,
144 MergeFlags flags)
145 : MergeDownInfoBase(_image),
146 prevLayer(_prevLayer),
147 currLayer(_currLayer)
148 {
149 if (!flags.testFlag(SkipMergingFrames)) {
152 }
153
160 if (!frames.isEmpty() && !currLayer->isAnimated()) {
162 }
163
165
166 const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer*>(currLayer.data());
167 if (paintLayer) enableOnionSkins |= paintLayer->onionSkinEnabled();
168
169 paintLayer = qobject_cast<KisPaintLayer*>(prevLayer.data());
170 if (paintLayer) enableOnionSkins |= paintLayer->onionSkinEnabled();
171 }
172
175
177 KisNodeList mergedNodes;
178 mergedNodes << prevLayer;
179 mergedNodes << currLayer;
180 return mergedNodes;
181 }
182 };
183
186 : storage(new SwitchFrameCommand::SharedStorage())
187 , m_sourceNode(node)
188 , m_image(image)
189 , m_pinnedToTimeline(false)
190 {
192
194
197
199
200 KisColorizeMask *colorizeMask = dynamic_cast<KisColorizeMask*>(m_sourceNode.data());
201 if (colorizeMask) {
202 m_sourcePaintDevice = colorizeMask->coloringProjection();
203 const bool putBehind = colorizeMask->compositeOpId() == COMPOSITE_BEHIND;
204 if (putBehind) {
206 }
207
210
211 } else if (dynamic_cast<KisMask*>(m_sourceNode.data())) {
214 } else {
217 }
218
219 KisCloneLayer *cloneLayer = dynamic_cast<KisCloneLayer*>(m_sourceNode.data());
220 if (cloneLayer) {
222 } else if (m_sourcePaintDevice) {
223 KisPaintDeviceSP clone;
224
227
229 clone->setDefaultPixel(
232
233 QRect rc(m_sourcePaintDevice->extent());
234 KisPainter::copyAreaOptimized(rc.topLeft(), m_sourcePaintDevice, clone, rc);
235 } else {
237 }
238
242 clone);
243
245
246 if (sourceLayer() && targetLayer()) {
247 targetLayer()->disableAlphaChannel(sourceLayer()->alphaChannelDisabled());
248 }
249
252 }
253 }
254 }
255
256 QSet<int> frames() {
257 return m_frames;
258 }
259
261 return m_sourceNode;
262 }
263
265 return qobject_cast<KisLayer*>(m_sourceNode.data());
266 }
267
269 KisNodeList list;
270 list << m_sourceNode;
271 return list;
272 }
273
275 return qobject_cast<KisPaintLayer*>(m_sourceNode.data());
276 }
277
279 return m_targetNode != nullptr;
280 }
281
283 return m_targetNode;
284 }
285
287 return qobject_cast<KisLayer*>(m_targetNode.data());
288 }
289
291 return qobject_cast<KisPaintLayer*>(m_targetNode.data());
292 }
293
294
296 return m_image;
297 }
298
302
304 KisNodeList lst;
305 lst << m_sourceNode;
306 return lst;
307 }
308
312
314 return m_insertionParent;
315 }
316
318
319 private:
324 QSet<int> m_frames;
327
330 };
331
334 KisNodeList _mergedNodes,
335 MergeFlags flags)
336 : MergeDownInfoBase(_image),
337 mergedNodes(_mergedNodes)
338 {
339 foreach (KisNodeSP node, mergedNodes) {
340 if (!flags.testFlag(SkipMergingFrames)) {
342 }
344
345 const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer*>(node.data());
346 if (paintLayer) {
347 enableOnionSkins |= paintLayer->onionSkinEnabled();
348 }
349 }
350 }
351
352 QScopedPointer<KisSurrogateUndoStore> ephemeralCommandsStore;
355
357 return mergedNodes;
358 }
359 };
360
366
369
370 void redo() override {
371 fetchSelectionMasks(m_info->allSrcNodes(), m_info->selectionMasks);
372 }
373
374 private:
376 };
377
380
381 void populateChildCommands() override {
382 Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
384 [this] (KisNodeSP node) {
385 if (dynamic_cast<KisColorizeMask*>(node.data()) &&
387
388 KisBaseNode::PropertyList props = node->sectionModelProperties();
389 KisLayerPropertiesIcons::setNodeProperty(&props,
390 KisLayerPropertiesIcons::colorizeEditKeyStrokes,
391 false);
392
393 addCommand(new KisNodePropertyListCommand(node, props));
394 }
395 });
396 }
397 }
398
399 private:
401 };
402
405
406 void populateChildCommands() override {
407 Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
409 [this] (KisNodeSP node) {
411
415 false);
416
417 addCommand(new KisNodePropertyListCommand(node, props));
418 }
419 });
420 }
421 }
422
423 private:
425 };
426
429
430 void populateChildCommands() override {
436 if (m_info->nodesCompositingVaries) return;
437
438 // we should disable dirty requests on **redo only**, otherwise
439 // the state of the layers will not be recovered on undo
440 m_info->image->disableDirtyRequests();
441
442 Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
443 if (node->compositeOpId() != COMPOSITE_OVER) {
445 }
446
448
452 false);
453
454 addCommand(new KisNodePropertyListCommand(node, props));
455 }
456 }
457
458 m_info->image->enableDirtyRequests();
459 }
460
461 private:
463 };
464
466 DisablePassThroughForHeadsOnly(MergeDownInfoBaseSP info, bool skipIfDstIsGroup = false)
467 : m_info(info),
468 m_skipIfDstIsGroup(skipIfDstIsGroup)
469 {
470 }
471
472 void populateChildCommands() override {
473 if (m_skipIfDstIsGroup &&
474 m_info->dstLayer() &&
475 m_info->dstLayer()->inherits("KisGroupLayer")) {
476
477 return;
478 }
479
480
481 Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) {
483
487 false);
488
489 addCommand(new KisNodePropertyListCommand(node, props));
490 }
491 }
492 }
493
494 private:
497 };
498
502
503 RefreshHiddenAreas(MergeDownInfoBaseSP info) : m_image(info->image), m_nodes(info->allSrcNodes()) {}
504 RefreshHiddenAreas(MergeDownInfoBaseSP info, refresh_entire_image_t) : m_image(info->image), m_nodes(info->allSrcNodes()), m_extraUpdateRect(info->image->bounds()) {}
506 m_nodes << node;
507 }
508
509 void populateChildCommands() override {
510 KisImageAnimationInterface *interface = m_image->animationInterface();
511 const QRect preparedRect = !interface->externalFrameActive() ?
512 m_image->bounds() : QRect();
513
514 foreach (KisNodeSP node, m_nodes) {
516 }
517 }
518
519 private:
523 };
524
527 : m_nodes(info->allSrcNodes()) {}
528
530 m_nodes << nodes;
531 }
532
533 void redo() override {
534 if (m_info) {
535 m_nodes << m_info->allSrcNodes();
536 }
537
538 foreach (KisNodeSP node, m_nodes) {
540 }
541 }
542
543 private:
546 };
547
550 : m_singleInfo(info),
551 m_finalizing(finalizing) {}
552
554 : m_multipleInfo(info),
555 m_finalizing(finalizing),
556 m_putAfter(putAfter) {}
557
558 void populateChildCommands() override {
559 KisNodeSP prevNode;
560 KisNodeSP nextNode;
561 KisNodeList prevSelection;
562 KisNodeList nextSelection;
563 KisImageSP image;
564
565 if (m_singleInfo) {
566 prevNode = m_singleInfo->currLayer;
567 nextNode = m_singleInfo->dstNode;
568 image = m_singleInfo->image;
569 } else if (m_multipleInfo) {
570 prevNode = m_putAfter;
571 nextNode = m_multipleInfo->dstNode;
572 prevSelection = m_multipleInfo->allSrcNodes();
573 image = m_multipleInfo->image;
574 }
575
576 if (!m_finalizing) {
578 prevNode, KisNodeSP(),
579 image, false));
580 } else {
582 KisNodeSP(), nextNode,
583 image, true));
584 }
585 }
586
587 private:
592 };
593
596
597 void populateChildCommands() override {
598 // actual merging done by KisLayer::createMergedLayer (or specialized descendant)
599 m_info->dstNode = m_info->currLayer->createMergedLayerTemplate(m_info->prevLayer);
600
601 if (m_info->frames.size() > 0) {
602 m_info->dstNode->enableAnimation();
603 m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Raster.id(), true);
604 }
605
606 m_info->dstNode->setPinnedToTimeline(m_info->pinnedToTimeline);
607 m_info->dstNode->setColorLabelIndex(m_info->allSrcNodes().first()->colorLabelIndex());
608
609 KisPaintLayer *dstPaintLayer = qobject_cast<KisPaintLayer*>(m_info->dstNode.data());
610 if (dstPaintLayer) {
611 dstPaintLayer->setOnionSkinEnabled(m_info->enableOnionSkins);
612 }
613 }
614
615 private:
617 };
618
620 CreateMergedLayerMultiple(MergeMultipleInfoSP info, const QString name = QString() )
621 : m_info(info),
622 m_name(name) {}
623
624 void populateChildCommands() override {
625 QString mergedLayerName;
626 if (m_name.isEmpty()){
627 const QString mergedLayerSuffix = i18n("Merged");
628 mergedLayerName = m_info->mergedNodes.first()->name();
629
630 if (KisImageConfig(true).renameMergedLayers() && !mergedLayerName.endsWith(mergedLayerSuffix)) {
631 mergedLayerName = QString("%1 %2")
632 .arg(mergedLayerName).arg(mergedLayerSuffix);
633 }
634 } else {
635 mergedLayerName = m_name;
636 }
637
638 auto channelFlagsLazy = [](KisNodeSP node) {
639 KisLayer *layer = dynamic_cast<KisLayer*>(node.data());
640 return layer ? layer->channelFlags() : QBitArray();
641 };
642
643 QString compositeOpId;
644 QBitArray channelFlags;
645 bool compositionVaries = false;
646 bool isFirstCycle = true;
647
648 foreach (KisNodeSP node, m_info->allSrcNodes()) {
649 if (isFirstCycle) {
650 compositeOpId = node->compositeOpId();
651 channelFlags = channelFlagsLazy(node);
652 isFirstCycle = false;
653 } else if (compositeOpId != node->compositeOpId() ||
654 channelFlags != channelFlagsLazy(node)) {
655 compositionVaries = true;
656 break;
657 }
658
659 KisLayerSP layer = qobject_cast<KisLayer*>(node.data());
660 if (layer && layer->layerStyle()) {
661 compositionVaries = true;
662 break;
663 }
664 }
665
670 if (!compositionVaries && m_info->frames.isEmpty()) {
671 QList<KisLayerSP> layers;
672 Q_FOREACH(KisNodeSP node, m_info->allSrcNodes()) {
673 KisLayer *layer = dynamic_cast<KisLayer*>(node.data());
674 if (!layer) {
675 layers.clear();
676 break;
677 }
678 layers << layer;
679 }
680
681 if (!layers.isEmpty()) {
686 m_info->dstNode = layers.first()->tryCreateInternallyMergedLayerFromMutipleLayers(layers);
687 }
688 }
689
693 if (!m_info->dstNode) {
694 m_info->rasterizedDstLayer = new KisPaintLayer(m_info->image, mergedLayerName, OPACITY_OPAQUE_U8);
695 m_info->dstNode = m_info->rasterizedDstLayer;
696
697 if (m_info->frames.size() > 0) {
698 m_info->dstNode->enableAnimation();
699 m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Raster.id(), true);
700 }
701
702 if (!compositionVaries) {
703 if (!compositeOpId.isEmpty()) {
704 m_info->dstNode->setCompositeOpId(compositeOpId);
705 }
706 if (m_info->dstLayer() && !channelFlags.isEmpty()) {
707 m_info->dstLayer()->setChannelFlags(channelFlags);
708 }
709 }
710
711 m_info->dstNode->setPinnedToTimeline(m_info->pinnedToTimeline);
712 m_info->dstNode->setColorLabelIndex(m_info->allSrcNodes().first()->colorLabelIndex());
713
714 m_info->rasterizedDstLayer->setOnionSkinEnabled(m_info->enableOnionSkins);
715 }
716
717 m_info->nodesCompositingVaries = compositionVaries;
718 }
719
720 private:
722 QString m_name;
723 };
724
726 MergeLayers(MergeDownInfoSP info, bool skipMergingSourceLayer)
727 : m_info(info), m_skipMergingSourceLayer(skipMergingSourceLayer) {}
728
729 void populateChildCommands() override {
730 // actual merging done by KisLayer::createMergedLayer (or specialized descendant)
731 m_info->currLayer->fillMergedLayerTemplate(m_info->dstLayer(), m_info->prevLayer, m_skipMergingSourceLayer);
732 }
733
734 private:
737 };
738
741
742 void populateChildCommands() override {
743 if (!m_info->rasterizedDstLayer) return;
744
745 KisPainter gc(m_info->rasterizedDstLayer->paintDevice());
746
747 foreach (KisNodeSP node, m_info->allSrcNodes()) {
748 QRect rc = node->exactBounds() | m_info->image->bounds();
749 node->projectionPlane()->apply(&gc, rc);
750 }
751 }
752
753 private:
755 };
756
757 struct MergeMetaData : public KUndo2Command {
759 : m_info(info),
760 m_strategy(strategy) {}
761
762 void redo() override {
763 QRect layerProjectionExtent = m_info->currLayer->projection()->extent();
764 QRect prevLayerProjectionExtent = m_info->prevLayer->projection()->extent();
765 int prevLayerArea = prevLayerProjectionExtent.width() * prevLayerProjectionExtent.height();
766 int layerArea = layerProjectionExtent.width() * layerProjectionExtent.height();
767
768 QList<double> scores;
769 double norm = qMax(prevLayerArea, layerArea);
770 scores.append(prevLayerArea / norm);
771 scores.append(layerArea / norm);
772
774 srcs.append(m_info->prevLayer->metaData());
775 srcs.append(m_info->currLayer->metaData());
776 m_strategy->merge(m_info->dstLayer()->metaData(), srcs, scores);
777 }
778
779 private:
782 };
783
787
788 void populateChildCommands() override {
789 m_info->getMask()->initSelection(m_info->getLayer());
790 }
791
792 private:
794 };
795
798 : m_info(info) {
799 m_cached = new KisPaintDevice(*m_info->node->paintDevice(), KritaUtils::CopyAllFrames);
800 }
801
802 void redo() override {
803 KisPaintDeviceSP srcDevice = m_info->node->paintDevice();
804 const KoColorSpace *srcCS = srcDevice->colorSpace();
805 const QRect processRect =
806 srcDevice->exactBounds() |
807 srcDevice->defaultBounds()->bounds();
808
809 KisSequentialIterator srcIt(srcDevice, processRect);
810 KisSequentialIterator dstIt(m_info->getMaskDevice(), processRect);
811
812 while (srcIt.nextPixel() && dstIt.nextPixel()) {
813 quint8 *srcPtr = srcIt.rawData();
814 quint8 *alpha8Ptr = dstIt.rawData();
815
816 *alpha8Ptr = srcCS->opacityU8(srcPtr);
817 srcCS->setOpacity(srcPtr, OPACITY_OPAQUE_U8, 1);
818 }
819 }
820
821 void undo() override {
822 KisPaintDeviceSP srcDevice = m_info->node->paintDevice();
823
824 if (srcDevice->framesInterface()) { //Swap contents of all frames to reflect the pre-operation state.
826 Q_FOREACH(const int& frame, srcDevice->framesInterface()->frames() ) {
827 if (m_cached->framesInterface()->frames().contains(frame)) {
829 srcDevice->framesInterface()->uploadFrame(frame, tempPD);
830 }
831 }
832 } else {
833 const QRect processRect =
834 srcDevice->exactBounds() |
835 srcDevice->defaultBounds()->bounds();
836
837 const KoColorSpace *srcCS = srcDevice->colorSpace();
838 KisSequentialIterator srcIt(m_cached, processRect);
839 KisSequentialIterator dstIt(srcDevice, processRect);
840
841 while (srcIt.nextPixel() && dstIt.nextPixel()) {
842 quint8 *srcPtr = srcIt.rawData();
843 quint8 *dstPtr = dstIt.rawData();
844 srcCS->setOpacity(dstPtr, srcCS->opacityU8(srcPtr), 1);
845 }
846 }
847 }
848
849 private:
852 };
853
856 : m_source(src)
858 , m_frame(frame)
859 {}
860
861 void populateChildCommands() override {
863 if (!channel)
864 return;
865
866
869 m_target->paintDevice()->framesInterface()->uploadFrame(key->frameID(), clone);
870 }
871
872 private:
876 };
877
879 const KisNodeList &selectedAfter,
880 KisNodeSP activeBefore,
881 KisNodeSP activeAfter,
882 KisImageSP image,
883 bool finalize, KUndo2Command *parent)
884 : FlipFlopCommand(finalize, parent),
885 m_selectedBefore(selectedBefore),
886 m_selectedAfter(selectedAfter),
887 m_activeBefore(activeBefore),
888 m_activeAfter(activeAfter),
889 m_image(image)
890 {
891 }
892
902
907
911
919
923
934 safeReplaceMultipleNodes(nodes, image, std::nullopt);
935 }
936
938 std::optional<ReplacementNode> replacementNode) {
939
940 const bool lastLayer = !replacementNode && scanForLastLayer(image, removedNodes);
941
942 auto isNodeWeird = [] (KisNodeSP node) {
943 const bool normalCompositeMode = node->compositeOpId() == COMPOSITE_OVER;
944
945 KisLayer *layer = dynamic_cast<KisLayer*>(node.data());
946 const bool hasInheritAlpha = layer && layer->alphaChannelDisabled();
947 return !normalCompositeMode && !hasInheritAlpha;
948 };
949
950
953 RecipeSP updateRecipe(new Recipe());
954
955 if (replacementNode) {
956 updateRecipe->nodesToAdd.push_back({replacementNode->node,
957 replacementNode->doRedoUpdates,
958 replacementNode->doUndoUpdates});
959 }
960
961 Q_FOREACH (KisNodeSP node, removedNodes) {
962 updateRecipe->nodesToRemove.push_back({node, !isNodeWeird(node), true});
963 }
964
966
967 if (replacementNode) {
969 replacementNode->node,
970 replacementNode->parent,
971 replacementNode->putAfter,
972 false, false));
973
974 Q_FOREACH (KisSelectionMaskSP mask, replacementNode->selectionMasks) {
975 addCommandImpl(new KisImageLayerMoveCommand(image, mask, replacementNode->node, replacementNode->node->lastChild(), false));
977 }
978
979 // relink all the clone layers onto the new replacement node
980 if (replacementNode->relinkClones) {
982
983 Q_FOREACH (KisNodeSP node, removedNodes) {
984 KisLayerSP originalSource = dynamic_cast<KisLayer*>(node.data());
985 if (originalSource) {
986 clones.append(originalSource->registeredClones());
987 }
988 }
989
990 KisLayerSP finalSource = dynamic_cast<KisLayer*>(replacementNode->node.data());
991
992 if (finalSource && !clones.isEmpty()) {
993 addCommandImpl(new KisChangeCloneLayersCommand(implicitCastList<KisCloneLayerSP>(clones), finalSource));
994 }
995 }
996 }
997
998 while (!removedNodes.isEmpty()) {
999 KisNodeList::iterator it = removedNodes.begin();
1000
1001 while (it != removedNodes.end()) {
1002 if (!checkIsSourceForClone(*it, removedNodes)) {
1003 KisNodeSP node = *it;
1004
1005 addCommandImpl(new KisImageLayerRemoveCommand(image, node, false, false));
1006 it = removedNodes.erase(it);
1007 } else {
1008 ++it;
1009 }
1010 }
1011 }
1012
1014
1015 // Hint: we shouldn't include that into the batch update since this layer doesn't trigger any updates
1016 if (lastLayer) {
1017 KisLayerSP newLayer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace());
1018 addCommandImpl(new KisImageLayerAddCommand(image, newLayer,
1019 image->root(),
1020 KisNodeSP(),
1021 false, false));
1022 }
1023 }
1024
1026 foreach (KisNodeSP node, nodes) {
1027 if (node == src) continue;
1028
1029 KisCloneLayer *clone = dynamic_cast<KisCloneLayer*>(node.data());
1030
1031 if (clone && KisNodeSP(clone->copyFrom()) == src) {
1032 return true;
1033 }
1034 }
1035
1036 return false;
1037 }
1038
1040 bool removeLayers = false;
1041 Q_FOREACH(KisNodeSP nodeToRemove, nodesToRemove) {
1042 if (qobject_cast<KisLayer*>(nodeToRemove.data())) {
1043 removeLayers = true;
1044 break;
1045 }
1046 }
1047 if (!removeLayers) return false;
1048
1049 bool lastLayer = true;
1050 KisNodeSP node = image->root()->firstChild();
1051 while (node) {
1052 if (!nodesToRemove.contains(node) &&
1053 qobject_cast<KisLayer*>(node.data()) &&
1054 !node->isFakeNode()) {
1055
1056 lastLayer = false;
1057 break;
1058 }
1059 node = node->nextSibling();
1060 }
1061
1062 return lastLayer;
1063 }
1064
1066 KisImageSP image)
1067 : m_nodes(nodes),
1068 m_image(image)
1069 {
1070 }
1071
1076
1080
1083 : m_info(info), m_putAfter(putAfter) {}
1084
1085 void populateChildCommands() override {
1087 m_info->dstNode,
1088 m_putAfter->parent(),
1089 m_putAfter,
1090 true, false));
1091
1092 }
1093
1094 private:
1095 virtual void addCommandImpl(KUndo2Command *cmd) {
1096 addCommand(cmd);
1097 }
1098
1101 };
1102
1104 SimpleAddNode(KisImageSP image, KisNodeSP toAdd, KisNodeSP parent = 0, KisNodeSP putAfter = 0)
1105 : m_image(image)
1106 , m_toAdd(toAdd)
1107 , m_parent(parent)
1108 , m_putAfter(putAfter)
1109 {
1110 while (m_parent && !m_parent->allowAsChild(m_toAdd)) {
1113 }
1114
1115 if (!m_parent) {
1116 m_parent = m_image->root();
1117 }
1118 }
1119
1120
1121 void populateChildCommands() override {
1123 m_toAdd,
1124 m_parent,
1125 m_putAfter,
1126 true, false));
1127 }
1128
1129 private:
1130 virtual void addCommandImpl(KUndo2Command *cmd) {
1131 addCommand(cmd);
1132 }
1133
1138
1139 };
1140
1141
1142 void splitNonRemovableNodes(KisNodeList &nodesToRemove, KisNodeList &_nodesToHide)
1143 {
1144 QSet<KisNodeSP> nodesToHide;
1145 QSet<KisNodeSP> extraNodesToRemove;
1146
1147 for (auto it = nodesToRemove.begin(); it != nodesToRemove.end(); ++it) {
1148 KisNodeSP root = *it;
1150
1151 if (!root->isEditable(false)) {
1152 nodesToHide.insert(root);
1153 } else {
1154 bool rootNeedsCarefulRemoval = false;
1155
1157 [root, &nodesToHide, &rootNeedsCarefulRemoval] (KisNodeSP node) {
1158 if (!node->isEditable(false)) {
1159 while (node != root) {
1160 nodesToHide.insert(node);
1161 node = node->parent();
1162 KIS_SAFE_ASSERT_RECOVER_BREAK(node);
1163 }
1164 nodesToHide.insert(root);
1165 rootNeedsCarefulRemoval = true;
1166 }
1167 });
1168
1169 if (rootNeedsCarefulRemoval) {
1171 [&extraNodesToRemove] (KisNodeSP node) {
1172 extraNodesToRemove.insert(node);
1173 });
1174 }
1175 }
1176 }
1177 nodesToRemove += KisNodeList(extraNodesToRemove.begin(), extraNodesToRemove.end());
1178 KritaUtils::filterContainer<KisNodeList>(nodesToRemove,
1179 [nodesToHide](KisNodeSP node) {
1180 return !nodesToHide.contains(node);
1181 });
1182 _nodesToHide = KisNodeList(nodesToHide.begin(), nodesToHide.end());
1183 }
1184
1186 CleanUpNodes(MergeDownInfoBaseSP info, KisNodeSP putAfter, bool relinkClones = false)
1187 : m_info(info), m_putAfter(putAfter), m_relinkClones(relinkClones) {}
1188
1189 static void findPerfectParent(KisNodeList nodesToDelete, KisNodeSP &putAfter, KisNodeSP &parent) {
1190 if (!putAfter) {
1191 putAfter = nodesToDelete.last();
1192 }
1193
1194 // Add the new merged node on top of the active node
1195 // -- checking all parents if they are included in nodesToDelete
1196 // Not every descendant is included in nodesToDelete even if in fact
1197 // they are going to be deleted, so we need to check it.
1198 // If we consider the path from root to the putAfter node,
1199 // if there are any nodes marked for deletion, any node afterwards
1200 // is going to be deleted, too.
1201 // example: root . . . . . ! ! . . ! ! ! ! . . . . putAfter
1202 // it should be: root . . . . . ! ! ! ! ! ! ! ! ! ! ! ! !putAfter
1203 // and here: root . . . . X ! ! . . ! ! ! ! . . . . putAfter
1204 // you can see which node is "the perfect ancestor"
1205 // (marked X; called "parent" in the function arguments).
1206 // and here: root . . . . . O ! . . ! ! ! ! . . . . putAfter
1207 // you can see which node is "the topmost deleted ancestor" (marked 'O')
1208
1209 KisNodeSP node = putAfter->parent();
1210 bool foundDeletedAncestor = false;
1211 KisNodeSP topmostAncestorToDelete = nullptr;
1212
1213 while (node) {
1214
1215 if (nodesToDelete.contains(node)
1216 && !nodesToDelete.contains(node->parent())) {
1217 foundDeletedAncestor = true;
1218 topmostAncestorToDelete = node;
1219 // Here node is to be deleted and its parent is not,
1220 // so its parent is the one of the first not deleted (="perfect") ancestors.
1221 // We need the one that is closest to the top (root)
1222 }
1223
1224 node = node->parent();
1225 }
1226
1227 if (foundDeletedAncestor) {
1228 parent = topmostAncestorToDelete->parent();
1229 putAfter = topmostAncestorToDelete;
1230 }
1231 else {
1232 parent = putAfter->parent(); // putAfter (and none of its ancestors) is to be deleted, so its parent is the first not deleted ancestor
1233 }
1234
1235 }
1236
1237 void populateChildCommands() override {
1238 KisNodeList nodesToDelete = m_info->allSrcNodes();
1239
1240 KisNodeSP parent;
1241 findPerfectParent(nodesToDelete, m_putAfter, parent);
1242
1243 if (!parent) {
1244 KisNodeSP oldRoot = m_info->image->root();
1245 KisNodeSP newRoot(new KisGroupLayer(m_info->image, "root", OPACITY_OPAQUE_U8));
1246
1247 // copy all fake nodes into the new image
1248 KisLayerUtils::recursiveApplyNodes(oldRoot, [this, oldRoot, newRoot] (KisNodeSP node) {
1249 if (node->isFakeNode() && node->parent() == oldRoot) {
1250 addCommand(new KisImageLayerAddCommand(m_info->image,
1251 node->clone(),
1252 newRoot,
1253 KisNodeSP(),
1254 false, false));
1255
1256 }
1257 });
1258
1259 addCommand(new KisImageLayerAddCommand(m_info->image,
1260 m_info->dstNode,
1261 newRoot,
1262 KisNodeSP(),
1263 true, false));
1264 addCommand(new KisImageChangeLayersCommand(m_info->image, oldRoot, newRoot));
1265
1266 }
1267 else {
1268 KisNodeList safeNodesToDelete = m_info->allSrcNodes();
1269 KisNodeList safeNodesToHide;
1270
1271 splitNonRemovableNodes(safeNodesToDelete, safeNodesToHide);
1272
1273 Q_FOREACH(KisNodeSP node, safeNodesToHide) {
1274 addCommand(new KisImageChangeVisibilityCommand(false, node));
1275 }
1276
1277 safeReplaceMultipleNodes(safeNodesToDelete, m_info->image,
1278 std::make_optional<ReplacementNode>(
1279 {m_info->dstNode,
1280 parent,
1281 m_putAfter,
1282 true, false,
1283 m_info->selectionMasks,
1284 m_relinkClones}));
1285 }
1286
1287
1288 }
1289
1290 private:
1291 void addCommandImpl(KUndo2Command *cmd) override {
1292 addCommand(cmd);
1293 }
1294
1295 private:
1298 bool m_relinkClones {false};
1299 };
1300
1301 SwitchFrameCommand::SharedStorage::~SharedStorage() {
1302 }
1303
1304 SwitchFrameCommand::SwitchFrameCommand(KisImageSP image, int time, bool finalize, SharedStorageSP storage)
1305 : FlipFlopCommand(finalize),
1306 m_image(image),
1307 m_newTime(time),
1308 m_storage(storage) {}
1309
1311
1313 KisImageAnimationInterface *interface = m_image->animationInterface();
1314 const int currentTime = interface->currentTime();
1315 if (currentTime == m_newTime) {
1316 m_storage->value = m_newTime;
1317 return;
1318 }
1319
1320 interface->image()->disableUIUpdates();
1321 interface->saveAndResetCurrentTime(m_newTime, &m_storage->value);
1322 }
1323
1325 KisImageAnimationInterface *interface = m_image->animationInterface();
1326 const int currentTime = interface->currentTime();
1327 if (currentTime == m_storage->value) {
1328 return;
1329 }
1330
1331 interface->restoreCurrentTime(&m_storage->value);
1332 interface->image()->enableUIUpdates();
1333 }
1334
1336 AddNewFrame(KisNodeSP node, int frame) : m_node(node), m_frame(frame) {}
1337 AddNewFrame(KisNodeSP node, int frame, KisNodeList sampleNodes) : m_node(node), m_frame(frame), m_sampledNodes(sampleNodes) {}
1338 AddNewFrame(KisNodeSP node, int frame, KisNodeSP source) : m_node(node), m_frame(frame) { m_sampledNodes << source; }
1339 AddNewFrame(MergeDownInfoBaseSP info, int frame) : m_frame(frame), m_sampledNodes(info->allSrcNodes()), m_mergeInfo(info) {}
1340
1341 void populateChildCommands() override {
1342 KUndo2Command *cmd = new KUndo2Command;
1343 KisNodeSP node = m_node ? m_node : m_mergeInfo->dstNode;
1345 channel->addKeyframe(m_frame, cmd);
1346
1347 if (m_sampledNodes.count() > 0) {
1349 }
1350
1351 addCommand(cmd);
1352 }
1353
1355 Q_FOREACH(KisNodeSP srcNode, srcNodes) {
1356 Q_FOREACH(KisKeyframeChannel *channel, srcNode->keyframeChannels().values()) {
1357 KisKeyframeSP keyframe = channel->keyframeAt(m_frame);
1358 if (!keyframe.isNull() && keyframe->colorLabel() != 0) {
1359 dstKeyframe->setColorLabel(keyframe->colorLabel());
1360 return;
1361 }
1362 }
1363 }
1364
1365 dstKeyframe->setColorLabel(0);
1366 }
1367
1368 private:
1373 };
1374
1375 QSet<int> fetchLayerFrames(KisNodeSP node) {
1376 QSet<int> frames;
1377 Q_FOREACH(KisKeyframeChannel *channel, node->keyframeChannels()) {
1378 if (!channel) {
1379 continue;
1380 }
1381
1382 KisRasterKeyframeChannel *rasterChan = dynamic_cast<KisRasterKeyframeChannel*>(channel);
1383 if (rasterChan) {
1384 frames.unite(rasterChan->allKeyframeTimes());
1385 continue;
1386 }
1387
1388 KisScalarKeyframeChannel *scalarChan = dynamic_cast<KisScalarKeyframeChannel*>(channel);
1389 if (scalarChan) {
1390 const int initialKeyframe = scalarChan->firstKeyframeTime();
1391
1392 if (initialKeyframe == -1) {
1393 continue;
1394 }
1395
1396 const int lastKeyframe = scalarChan->lastKeyframeTime();
1397 KisTimeSpan currentSpan = scalarChan->identicalFrames(initialKeyframe);
1398 while (!currentSpan.isInfinite() && currentSpan.isValid() && currentSpan.start() < lastKeyframe) {
1399 frames.insert(currentSpan.start());
1400 currentSpan = scalarChan->identicalFrames(currentSpan.end() + 1);
1401 }
1402
1403 frames.insert(lastKeyframe);
1404 }
1405
1406 }
1407
1408 return frames;
1409 }
1410
1412 if (!rootNode->visible()) return QSet<int>();
1413
1414 QSet<int> frames = fetchLayerFrames(rootNode);
1415
1416 KisNodeSP node = rootNode->firstChild();
1417 while(node) {
1418 frames |= fetchLayerFramesRecursive(node);
1419 node = node->nextSibling();
1420 }
1421
1422 return frames;
1423 }
1424
1426 QSet<int> frames = fetchLayerFrames(node);
1427 frames = fetchUniqueFrameTimes(node, frames, false);
1428
1429 if (frames.isEmpty()) {
1430 (*jobs)[0].insert(node);
1431 } else {
1432 foreach (int frame, frames) {
1433 (*jobs)[frame].insert(node);
1434 }
1435 }
1436 }
1437
1439 updateFrameJobs(jobs, rootNode);
1440
1441 KisNodeSP node = rootNode->firstChild();
1442 while(node) {
1443 updateFrameJobsRecursive(jobs, node);
1444 node = node->nextSibling();
1445 }
1446 }
1447
1448 void updateFrameJobsNonRecursive(FrameJobs *jobs, KisNodeSP rootNode, bool frameTimesRecursive)
1449 {
1450 if (frameTimesRecursive) {
1451 QSet<int> frames = fetchLayerFramesRecursive(rootNode);
1452
1453 if (frames.isEmpty()) {
1454 (*jobs)[0].insert(rootNode);
1455 } else {
1456 foreach (int frame, frames) {
1457 (*jobs)[frame].insert(rootNode);
1458 }
1459 }
1460 } else {
1461 updateFrameJobs(jobs, rootNode);
1462 }
1463 }
1464
1468 void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy* strategy, MergeFlags flags)
1469 {
1470 if (!layer->prevSibling()) return;
1471
1472 // XXX: this breaks if we allow free mixing of masks and layers
1473 KisLayerSP prevLayer = qobject_cast<KisLayer*>(layer->prevSibling().data());
1474
1475 while (prevLayer && prevLayer->isFakeNode()) {
1476 prevLayer = qobject_cast<KisLayer*>(prevLayer->prevSibling().data());
1477 }
1478
1479 if (!prevLayer) return;
1480
1481 if (!layer->visible() && !prevLayer->visible()) {
1482 return;
1483 }
1484
1485 KisImageSignalVector emitSignals;
1486 KisProcessingApplicator applicator(image, 0,
1488 emitSignals,
1489 kundo2_i18n("Merge Down"));
1490
1491 if (layer->visible() && prevLayer->visible()) {
1492 MergeDownInfoSP info(new MergeDownInfo(image, prevLayer, layer, flags));
1493
1494 // disable key strokes on all colorize masks, all onion skins on
1495 // paint layers and wait until update is finished with a barrier
1496 applicator.applyCommand(new DisableColorizeKeyStrokes(info));
1497 applicator.applyCommand(new DisableOnionSkins(info));
1499
1500 applicator.applyCommand(new KeepMergedNodesSelected(info, false));
1501 applicator.applyCommand(new FillSelectionMasks(info));
1503
1504 // NOTE: shape layer may have emitted spontaneous jobs during layer creation,
1505 // wait for them to complete!
1508
1509 // in two-layer mode we disable pass through only when the destination layer
1510 // is not a group layer
1511 applicator.applyCommand(new DisablePassThroughForHeadsOnly(info, true));
1513
1514 if (info->frames.size() > 0) {
1519 const int currentTimeOnStart = info->image->animationInterface()->currentTime();
1520
1521 foreach (int frame, info->frames) {
1522 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
1523
1524 applicator.applyCommand(new AddNewFrame(info, frame));
1533
1542 const bool skipMergingSourceLayer = !layer->isAnimated() &&
1543 frame != currentTimeOnStart;
1544
1545 applicator.applyCommand(new MergeLayers(info, skipMergingSourceLayer), KisStrokeJobData::BARRIER);
1546
1547 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage), KisStrokeJobData::BARRIER);
1548 }
1549 } else {
1550 applicator.applyCommand(new RefreshHiddenAreas(info));
1552 applicator.applyCommand(new MergeLayers(info, false), KisStrokeJobData::BARRIER);
1553 }
1554
1555 applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
1556 applicator.applyCommand(new CleanUpNodes(info, layer),
1559 applicator.applyCommand(new KeepMergedNodesSelected(info, true));
1560 } else if (layer->visible()) {
1562 layer, KisNodeSP(),
1563 image, false));
1564
1565 applicator.applyCommand(
1566 new SimpleRemoveLayers(KisNodeList() << prevLayer,
1567 image),
1570
1572 KisNodeSP(), layer,
1573 image, true));
1574 } else if (prevLayer->visible()) {
1576 layer, KisNodeSP(),
1577 image, false));
1578
1579 applicator.applyCommand(
1580 new SimpleRemoveLayers(KisNodeList() << layer,
1581 image),
1584
1586 KisNodeSP(), prevLayer,
1587 image, true));
1588 }
1589
1590 applicator.end();
1591 }
1592
1593 bool checkIsChildOf(KisNodeSP node, const KisNodeList &parents)
1594 {
1595 KisNodeList nodeParents;
1596
1597 KisNodeSP parent = node->parent();
1598 while (parent) {
1599 nodeParents << parent;
1600 parent = parent->parent();
1601 }
1602
1603 foreach(KisNodeSP perspectiveParent, parents) {
1604 if (nodeParents.contains(perspectiveParent)) {
1605 return true;
1606 }
1607 }
1608
1609 return false;
1610 }
1611
1612 bool checkIsCloneOf(KisNodeSP node, const KisNodeList &nodes)
1613 {
1614 bool result = false;
1615
1616 KisCloneLayer *clone = dynamic_cast<KisCloneLayer*>(node.data());
1617 if (clone) {
1618 KisNodeSP cloneSource = KisNodeSP(clone->copyFrom());
1619
1620 Q_FOREACH(KisNodeSP subtree, nodes) {
1621 result =
1622 recursiveFindNode(subtree,
1623 [cloneSource](KisNodeSP node) -> bool
1624 {
1625 return node == cloneSource;
1626 });
1627
1628 if (!result) {
1629 result = checkIsCloneOf(cloneSource, nodes);
1630 }
1631
1632 if (result) {
1633 break;
1634 }
1635 }
1636 }
1637
1638 return result;
1639 }
1640
1641 void filterMergeableNodes(KisNodeList &nodes, bool allowMasks)
1642 {
1643 KisNodeList::iterator it = nodes.begin();
1644
1645 while (it != nodes.end()) {
1646 if ((!allowMasks && !qobject_cast<KisLayer*>(it->data())) ||
1647 checkIsChildOf(*it, nodes)) {
1648 //qDebug() << "Skipping node" << ppVar((*it)->name());
1649 it = nodes.erase(it);
1650 } else {
1651 ++it;
1652 }
1653 }
1654 }
1655
1656 void sortMergeableNodes(KisNodeSP root, KisNodeList &inputNodes, KisNodeList &outputNodes)
1657 {
1658 KisNodeList::iterator it = std::find(inputNodes.begin(), inputNodes.end(), root);
1659
1660 if (it != inputNodes.end()) {
1661 outputNodes << *it;
1662 inputNodes.erase(it);
1663 }
1664
1665 if (inputNodes.isEmpty()) {
1666 return;
1667 }
1668
1669 KisNodeSP child = root->firstChild();
1670 while (child) {
1671 sortMergeableNodes(child, inputNodes, outputNodes);
1672 child = child->nextSibling();
1673 }
1674
1678 KIS_ASSERT_RECOVER_NOOP(root->parent() || inputNodes.isEmpty());
1679 }
1680
1682 {
1683 KisNodeList result;
1684 sortMergeableNodes(root, nodes, result);
1685 return result;
1686 }
1687
1689 {
1690 KIS_SAFE_ASSERT_RECOVER(!nodes.isEmpty()) { return nodes; }
1691
1692 KisNodeSP root;
1693 Q_FOREACH(KisNodeSP node, nodes) {
1694 KisNodeSP localRoot = node;
1695 while (localRoot->parent()) {
1696 localRoot = localRoot->parent();
1697 }
1698
1699 if (!root) {
1700 root = localRoot;
1701 }
1702 KIS_SAFE_ASSERT_RECOVER(root == localRoot) { return nodes; }
1703 }
1704
1705 KisNodeList result;
1706 sortMergeableNodes(root, nodes, result);
1707 filterMergeableNodes(result, allowMasks);
1708 return result;
1709 }
1710
1712 {
1713 KIS_SAFE_ASSERT_RECOVER(!nodes.isEmpty()) { return nodes; }
1714
1715 KisNodeSP root;
1716 Q_FOREACH(KisNodeSP node, nodes) {
1717 KisNodeSP localRoot = node;
1718 while (localRoot->parent()) {
1719 localRoot = localRoot->parent();
1720 }
1721
1722 if (!root) {
1723 root = localRoot;
1724 }
1725 KIS_SAFE_ASSERT_RECOVER(root == localRoot) { return nodes; }
1726 }
1727
1728 KisNodeList result;
1729 sortMergeableNodes(root, nodes, result);
1730 return result;
1731 }
1732
1734 KisNodeList filteredNodes = nodes;
1735 KisNodeList sortedNodes;
1736
1737 KisLayerUtils::filterMergeableNodes(filteredNodes, true);
1738
1739 bool haveExternalNodes = false;
1740 Q_FOREACH (KisNodeSP node, nodes) {
1741 if (node->graphListener() != image->root()->graphListener()) {
1742 haveExternalNodes = true;
1743 break;
1744 }
1745 }
1746
1747 if (!haveExternalNodes) {
1748 KisLayerUtils::sortMergeableNodes(image->root(), filteredNodes, sortedNodes);
1749 } else {
1750 sortedNodes = filteredNodes;
1751 }
1752
1753 return sortedNodes;
1754 }
1755
1756
1758 {
1759 if (!KisImageConfig(true).renameDuplicatedLayers()) { return; }
1760
1761 const QString prefix = i18n("Copy of");
1762 QString newName = node->name();
1763 if (!newName.startsWith(prefix)) {
1764 newName = QString("%1 %2").arg(prefix).arg(newName);
1765 node->setName(newName);
1766 }
1767 }
1768
1769 KisNodeList findNodesWithProps(KisNodeSP root, const KoProperties &props, bool excludeRoot)
1770 {
1771 KisNodeList nodes;
1772
1773 if ((!excludeRoot || root->parent()) && root->check(props)) {
1774 nodes << root;
1775 }
1776
1777 KisNodeSP node = root->firstChild();
1778 while (node) {
1779 nodes += findNodesWithProps(node, props, excludeRoot);
1780 node = node->nextSibling();
1781 }
1782
1783 return nodes;
1784 }
1785
1786 KisNodeList filterInvisibleNodes(const KisNodeList &nodes, KisNodeList *invisibleNodes, KisNodeSP *putAfter)
1787 {
1788 KIS_ASSERT_RECOVER(invisibleNodes) { return nodes; }
1789 KIS_ASSERT_RECOVER(putAfter) { return nodes; }
1790
1791 KisNodeList visibleNodes;
1792 int putAfterIndex = -1;
1793
1794 Q_FOREACH(KisNodeSP node, nodes) {
1795 if (node->visible()) {
1796 visibleNodes << node;
1797 } else if (node->userLocked()) {
1802 } else {
1803 *invisibleNodes << node;
1804
1805 if (node == *putAfter) {
1806 putAfterIndex = visibleNodes.size() - 1;
1807 }
1808 }
1809 }
1810
1811 if (!visibleNodes.isEmpty() && putAfterIndex >= 0) {
1812 putAfterIndex = qBound(0, putAfterIndex, visibleNodes.size() - 1);
1813 *putAfter = visibleNodes[putAfterIndex];
1814 }
1815
1816 return visibleNodes;
1817 }
1818
1820 {
1821 KisNodeList::iterator it = nodes.begin();
1822
1823 while (it != nodes.end()) {
1824 if ((*it)->userLocked()) {
1825 it = nodes.erase(it);
1826 } else {
1827 ++it;
1828 }
1829 }
1830 }
1831
1833 {
1834 KisImageSignalVector emitSignals;
1835 KisProcessingApplicator applicator(image,
1836 image->root(),
1838 emitSignals,
1839 kundo2_i18n("Change projection color"),
1840 0,
1843 applicator.end();
1844 }
1845
1847 {
1849 : m_info(info)
1850 , m_commands(commands)
1851 , m_cleanupNodes(cleanupNodes)
1852 {
1853 }
1854
1856 if (!m_cleanupNodes && !m_info->ephemeralCommandsStore) {
1857 m_info->ephemeralCommandsStore.reset(new KisSurrogateUndoStore());
1858 }
1859
1860 Q_FOREACH (KUndo2Command *cmd, m_commands) {
1861 if (m_cleanupNodes) {
1862 addCommand(cmd);
1863 } else {
1864 m_info->ephemeralCommandsStore->addCommand(cmd);
1865 }
1866 }
1867 m_commands.clear();
1868 m_info.reset();
1869 }
1870
1871 private:
1874 bool m_cleanupNodes {true};
1875 };
1876
1878 {
1880 : m_info(info)
1881 {
1882 }
1883
1885 KIS_SAFE_ASSERT_RECOVER_RETURN(m_info->ephemeralCommandsStore);
1886 m_info->ephemeralCommandsStore->undoAll();
1887
1888 }
1889
1890 private:
1892 };
1893
1919 void mergeMultipleLayersImpl(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter,
1920 bool flattenSingleLayer, const KUndo2MagicString &actionName,
1921 bool cleanupNodes = true, const QString layerName = QString(),
1922 MergeFlags flags = None)
1923 {
1924 if (!putAfter) {
1925 putAfter = mergedNodes.first();
1926 }
1927
1928 filterMergeableNodes(mergedNodes);
1929 {
1930 KisNodeList tempNodes;
1931 std::swap(mergedNodes, tempNodes);
1932 sortMergeableNodes(image->root(), tempNodes, mergedNodes);
1933 }
1934
1935 if (mergedNodes.size() <= 1 &&
1936 (!flattenSingleLayer && mergedNodes.size() == 1)) return;
1937
1938 KisImageSignalVector emitSignals;
1939 emitSignals << ComplexNodeReselectionSignal(KisNodeSP(), KisNodeList(), KisNodeSP(), mergedNodes);
1940
1941
1942
1943 KisNodeList originalNodes = mergedNodes;
1944 KisNodeList invisibleNodes;
1945 mergedNodes = filterInvisibleNodes(originalNodes, &invisibleNodes, &putAfter);
1946
1947 if (mergedNodes.isEmpty()) return;
1948
1949
1950 // make sure we don't add the new layer into a locked group
1952 while (putAfter->parent() && !putAfter->parent()->isEditable()) {
1953 putAfter = putAfter->parent();
1954 }
1955
1963 if (!putAfter->parent()) {
1964 return;
1965 }
1966
1967 KisProcessingApplicator applicator(image, 0,
1969 emitSignals,
1970 actionName);
1971
1972
1973 if (!invisibleNodes.isEmpty() && cleanupNodes) {
1974
1975 /* If the putAfter node is invisible,
1976 * we should instead pick one of the nodes
1977 * to be merged to avoid a null putAfter
1978 * after we remove all invisible layers from
1979 * the image.
1980 * (The assumption is that putAfter is among
1981 * the layers to merge, so if it's invisible,
1982 * it's going to be removed)
1983 */
1984 if (!putAfter->visible()){
1985 putAfter = mergedNodes.first();
1986 }
1987
1988 applicator.applyCommand(
1989 new SimpleRemoveLayers(invisibleNodes,
1990 image),
1993 }
1994
1995 if (mergedNodes.size() > 1 || invisibleNodes.isEmpty()) {
1996 MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes, flags));
1997
1998 // disable key strokes on all colorize masks, all onion skins on
1999 // paint layers and wait until update is finished with a barrier
2000 //
2001 // when doing "new layer from visible" we should undo these changes
2002 // before the action stops, because the source layers are **not**
2003 // removed as a result of this action
2004 applicator.applyCommand(
2005 new EphemeralCommandsWrapper(info,
2006 {
2007 new DisableColorizeKeyStrokes(info),
2008 new DisableOnionSkins(info),
2010 },
2011 cleanupNodes));
2013
2014 applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, false));
2015 applicator.applyCommand(new FillSelectionMasks(info));
2016 applicator.applyCommand(new CreateMergedLayerMultiple(info, layerName), KisStrokeJobData::BARRIER);
2017 applicator.applyCommand(new EphemeralCommandsWrapper(info, { new DisableExtraCompositing(info) } , cleanupNodes));
2019
2020 if (!info->frames.isEmpty()) {
2021 foreach (int frame, info->frames) {
2022 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
2023
2024 applicator.applyCommand(new AddNewFrame(info, frame));
2034
2035 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
2036 }
2037 } else {
2038 applicator.applyCommand(new RefreshHiddenAreas(info));
2041 }
2042
2043 //applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
2044 if (cleanupNodes){
2045 applicator.applyCommand(new CleanUpNodes(info, putAfter, flattenSingleLayer),
2048 } else {
2049 applicator.applyCommand(new UndoEphemeralCommands(info));
2050 applicator.applyCommand(new InsertNode(info, putAfter),
2053 }
2054
2055 applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, true));
2056 }
2057
2058 applicator.end();
2059
2060 }
2061
2062 void mergeMultipleLayers(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, MergeFlags flags)
2063 {
2064 mergeMultipleLayersImpl(image, mergedNodes, putAfter,
2065 false, kundo2_i18n("Merge Selected Nodes"),
2066 true, QString(),
2067 flags);
2068 }
2069
2070 void mergeMultipleNodes(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, MergeFlags flags)
2071 {
2072 if (!tryMergeSelectionMasks(image, mergedNodes, putAfter)) {
2073 mergeMultipleLayers(image, mergedNodes, putAfter, flags);
2074 }
2075 }
2076
2077 void newLayerFromVisible(KisImageSP image, KisNodeSP putAfter, MergeFlags flags)
2078 {
2079 KisNodeList mergedNodes;
2080 mergedNodes << image->root();
2081
2082 mergeMultipleLayersImpl(image, mergedNodes, putAfter,
2083 true, kundo2_i18n("New From Visible"),
2084 false, i18nc("New layer created from all the visible layers", "Visible"),
2085 flags);
2086 }
2087
2090 : m_info(info),
2091 m_putAfter(putAfter){}
2092
2093 void populateChildCommands() override {
2094 KisNodeSP parent;
2095 CleanUpNodes::findPerfectParent(m_info->allSrcNodes(), m_putAfter, parent);
2096
2097 KisLayerSP parentLayer;
2098 do {
2099 parentLayer = qobject_cast<KisLayer*>(parent.data());
2100
2101 parent = parent->parent();
2102 } while(!parentLayer && parent);
2103
2104 KisSelectionSP selection = new KisSelection();
2105
2106 foreach (KisNodeSP node, m_info->allSrcNodes()) {
2107 KisMaskSP mask = dynamic_cast<KisMask*>(node.data());
2108 if (!mask) continue;
2109
2110 selection->pixelSelection()->applySelection(
2111 mask->selection()->pixelSelection(), SELECTION_ADD);
2112 }
2113
2114 KisSelectionMaskSP mergedMask = new KisSelectionMask(m_info->image, i18n("Selection Mask"));
2115 mergedMask->initSelection(parentLayer);
2116 mergedMask->setSelection(selection);
2117
2118 m_info->dstNode = mergedMask;
2119 }
2120
2121 private:
2124 };
2125
2129
2130 void populateChildCommands() override {
2131 KisSelectionMaskSP mergedMask = dynamic_cast<KisSelectionMask*>(m_info->dstNode.data());
2132 addCommand(new KisActivateSelectionMaskCommand(mergedMask, true));
2133 }
2134
2135 private:
2137 };
2138
2139 bool tryMergeSelectionMasks(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter)
2140 {
2141 QList<KisSelectionMaskSP> selectionMasks;
2142
2143 for (auto it = mergedNodes.begin(); it != mergedNodes.end(); /*noop*/) {
2144 KisSelectionMaskSP mask = dynamic_cast<KisSelectionMask*>(it->data());
2145 if (!mask) {
2146 it = mergedNodes.erase(it);
2147 } else {
2148 selectionMasks.append(mask);
2149 ++it;
2150 }
2151 }
2152
2153 if (mergedNodes.isEmpty()) return false;
2154
2155 KisLayerSP parentLayer = qobject_cast<KisLayer*>(selectionMasks.first()->parent().data());
2156 KIS_ASSERT_RECOVER(parentLayer) { return 0; }
2157
2158 KisImageSignalVector emitSignals;
2159
2160 KisProcessingApplicator applicator(image, 0,
2162 emitSignals,
2163 kundo2_i18n("Merge Selection Masks"));
2164
2165 MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes, None));
2166
2167
2168 applicator.applyCommand(new MergeSelectionMasks(info, putAfter));
2169 applicator.applyCommand(new CleanUpNodes(info, putAfter),
2172 applicator.applyCommand(new ActivateSelectionMask(info));
2173 applicator.end();
2174
2175 return true;
2176 }
2177
2178 void flattenLayer(KisImageSP image, KisLayerSP layer, MergeFlags flags)
2179 {
2180 if (!layer->childCount() && !layer->layerStyle())
2181 return;
2182
2183 KisNodeList mergedNodes;
2184 mergedNodes << layer;
2185
2186 mergeMultipleLayersImpl(image, mergedNodes, layer,
2187 true, kundo2_i18n("Flatten Layer"),
2188 true, QString(),
2189 flags);
2190 }
2191
2192 void flattenImage(KisImageSP image, KisNodeSP activeNode, MergeFlags flags)
2193 {
2194 if (!activeNode) {
2195 activeNode = image->root()->lastChild();
2196 }
2197
2198
2199 KisNodeList mergedNodes;
2200 mergedNodes << image->root();
2201
2202 mergeMultipleLayersImpl(image, mergedNodes, activeNode,
2203 true, kundo2_i18n("Flatten Image"),
2204 true, QString(),
2205 flags);
2206 }
2207
2209 : FlipFlopCommand(finalize, parent),
2210 m_nodes(nodes)
2211 {
2212 }
2218 {
2219 Q_FOREACH(KisNodeSP node, nodes) {
2220 node->setDirty(node->extent());
2221 }
2222 }
2223
2224 KisNodeSP recursiveFindNode(KisNodeSP node, std::function<bool(KisNodeSP)> func)
2225 {
2226 if (func(node)) {
2227 return node;
2228 }
2229
2230 node = node->firstChild();
2231 while (node) {
2232 KisNodeSP resultNode = recursiveFindNode(node, func);
2233 if (resultNode) {
2234 return resultNode;
2235 }
2236 node = node->nextSibling();
2237 }
2238
2239 return 0;
2240 }
2241
2242 KisNodeSP findNodeByUuid(KisNodeSP root, const QUuid &uuid)
2243 {
2244 return recursiveFindNode(root,
2245 [uuid] (KisNodeSP node) {
2246 return node->uuid() == uuid;
2247 });
2248 }
2249
2250 QList<KisNodeSP> findNodesByName(KisNodeSP root, const QString &name, bool recursive, bool partialMatch)
2251 {
2252 KisNodeList nodeList;
2253 KisNodeSP child = root->firstChild();
2254
2255 while (child) {
2256 if (name.isEmpty() || (!partialMatch && child->name() == name) || (partialMatch && child->name().contains(name, Qt::CaseInsensitive))) {
2257 nodeList << child;
2258 }
2259 if (recursive && child->childCount() > 0) {
2260 nodeList << findNodesByName(child, name, recursive, partialMatch);
2261 }
2262 child = child->nextSibling();
2263 }
2264
2265 return nodeList;
2266 }
2267
2268 KisNodeSP findNodeByName(KisNodeSP root, const QString &name)
2269 {
2270 return recursiveFindNode(root,
2271 [name] (KisNodeSP node) {
2272 return node->name() == name;
2273 });
2274 }
2275
2277 {
2278 KisImageSP image = node->image();
2279 if (image)
2280 return image->isolationRootNode();
2281 return nullptr;
2282 }
2283
2285 {
2287 [] (KisNodeSP node) {
2288 KisDelayedUpdateNodeInterface *delayedUpdate =
2289 dynamic_cast<KisDelayedUpdateNodeInterface*>(node.data());
2290 if (delayedUpdate) {
2291 delayedUpdate->forceUpdateTimedNode();
2292 }
2293 });
2294 }
2295
2297 {
2298 return recursiveFindNode(root,
2299 [] (KisNodeSP node) {
2300 KisDelayedUpdateNodeInterface *delayedUpdate =
2301 dynamic_cast<KisDelayedUpdateNodeInterface*>(node.data());
2302
2303 return delayedUpdate ? delayedUpdate->hasPendingTimedUpdates() : false;
2304 });
2305 }
2306
2308 {
2310 [] (KisNodeSP node) {
2311 KisCroppedOriginalLayerInterface *croppedUpdate =
2312 dynamic_cast<KisCroppedOriginalLayerInterface*>(node.data());
2313 if (croppedUpdate) {
2314 croppedUpdate->forceUpdateHiddenAreaOnOriginal();
2315 }
2316 });
2317 }
2318
2320 {
2321 while (node) {
2322 const KisLayer *layer = dynamic_cast<const KisLayer*>(node.data());
2323 if (layer) {
2324 return layer->image();
2325 }
2326
2327 node = node->parent();
2328 }
2329
2330 return 0;
2331 }
2332
2333 namespace Private {
2334 QRect realNodeChangeRect(KisNodeSP rootNode, QRect currentRect = QRect()) {
2335 KisNodeSP node = rootNode->firstChild();
2336
2337 while(node) {
2338 currentRect |= realNodeChangeRect(node, currentRect);
2339 node = node->nextSibling();
2340 }
2341
2342 if (!rootNode->isFakeNode() && !rootNode->projectionLeaf()->isMask()) {
2343 // TODO: it would be better to count up changeRect inside
2344 // node's extent() method
2345 //
2346 // NOTE: when flattening a group layer, we should take the change rect of the
2347 // all the child layers as the source of the change. We are calculating
2348 // the change rect **before** the update itself, therefore rootNode->exactBounds()
2349 // is not yet prepared, hence its exact bounds still contail old values.
2350 currentRect |= rootNode->projectionPlane()->changeRect(rootNode->exactBounds() | currentRect);
2351 }
2352
2353 return currentRect;
2354 }
2355 }
2356
2357namespace Private {
2358 void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea, const QRect &extraUpdateRect) {
2359 QRect realNodeRect = Private::realNodeChangeRect(rootNode) | extraUpdateRect;
2360 if (!preparedArea.contains(realNodeRect)) {
2361
2362 QRegion dirtyRegion = realNodeRect;
2363 dirtyRegion -= preparedArea;
2364
2365 auto rc = dirtyRegion.begin();
2366 while (rc != dirtyRegion.end()) {
2367 image->refreshGraphAsync(rootNode, *rc, realNodeRect);
2368 rc++;
2369 }
2370 }
2371 }
2372} // namespace Private
2373
2374 void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea) {
2375 Private::refreshHiddenAreaAsync(image, rootNode, preparedArea, QRect());
2376 }
2377
2379 {
2380 QRect exactBounds;
2381 recursiveApplyNodes(rootNode, [&exactBounds] (KisNodeSP node) {
2382 exactBounds |= node->projectionPlane()->tightUserVisibleBounds();
2383 });
2384 return exactBounds;
2385 }
2386
2388 {
2389 if (!node) return node;
2390
2391 while (node->parent()) {
2392 node = node->parent();
2393 }
2394 return node;
2395 }
2396
2398 {
2399 int numLayers = 0;
2400 bool hasNonNormalLayers = false;
2401 bool hasTransparentLayer = false;
2402
2403
2404 recursiveApplyNodes(image->root(),
2405 [&numLayers, &hasNonNormalLayers, &hasTransparentLayer, image] (KisNodeSP node) {
2406 if (!node->inherits("KisLayer")) return;
2407
2408 numLayers++;
2409
2410 if (node->exactBounds().isEmpty()) return;
2411
2412 // this is only an approximation! it is not exact!
2413 if (!hasTransparentLayer &&
2414 node->exactBounds() != image->bounds()) {
2415
2416 hasTransparentLayer = true;
2417 }
2418
2419 if (!hasNonNormalLayers &&
2420 node->compositeOpId() != COMPOSITE_OVER) {
2421
2422 hasNonNormalLayers = true;
2423 }
2424 });
2425
2426 return numLayers == 1 || (!hasNonNormalLayers && !hasTransparentLayer);
2427 }
2428
2429 void splitAlphaToMask(KisImageSP image, KisNodeSP node, const QString& maskName)
2430 {
2431 SplitAlphaToMaskInfoSP info( new SplitAlphaToMaskInfo(node->image(), node, maskName) );
2432
2433 KisImageSignalVector emitSignals;
2434 KisProcessingApplicator applicator(image, 0,
2436 emitSignals,
2437 kundo2_i18n("Split Alpha into a Mask"));
2438
2439 applicator.applyCommand(new SimpleAddNode(info->image, info->getMask(), info->node), KisStrokeJobData::BARRIER);
2440 applicator.applyCommand(new InitSplitAlphaSelectionMask(info));
2441 if (info->frames.count() > 0) {
2442 Q_FOREACH(const int& frame, info->frames) {
2443 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
2444 applicator.applyCommand(new AddNewFrame(info->getMask(), frame, info->node));
2446 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
2447 }
2448 } else {
2450 }
2451 applicator.end();
2452 }
2453
2454 std::future<KisNodeSP> convertToPaintLayer(KisImageSP image, KisNodeSP src)
2455 {
2456 //Initialize all operation dependencies.
2457 ConvertToPaintLayerInfoSP info( new ConvertToPaintLayerInfo(image, src) );
2458
2459 if (!info->hasTargetNode())
2461
2462 KisImageSignalVector emitSignals;
2463 KisProcessingApplicator applicator(image, 0, KisProcessingApplicator::NONE, emitSignals, kundo2_i18n("Convert to a Paint Layer"));
2464
2465 applicator.applyCommand(new SimpleAddNode(info->image(), info->targetNode(), info->insertionParent(), info->insertionPutAfter()), KisStrokeJobData::BARRIER);
2466
2467 if (info->frames().count() > 0) {
2468 Q_FOREACH(const int& frame, info->frames()) {
2469 applicator.applyCommand(new SwitchFrameCommand(info->image(), frame, false, info->storage));
2470 applicator.applyCommand(new RefreshDelayedUpdateLayers(info->sourceNodes()), KisStrokeJobData::BARRIER);
2471 applicator.applyCommand(new RefreshHiddenAreas(info->image(), info->sourceNode()), KisStrokeJobData::BARRIER);
2472 applicator.applyCommand(new AddNewFrame(info->targetNode(), frame, info->sourceNode()), KisStrokeJobData::BARRIER);
2473 applicator.applyCommand(new UploadProjectionToFrameCommand(info->sourceNode(), info->targetNode(), frame));
2474 applicator.applyCommand(new SwitchFrameCommand(info->image(), frame, true, info->storage));
2475 }
2476 }
2477
2478 applicator.applyCommand(new SimpleRemoveLayers(info->toRemove(), info->image()));
2479
2480 applicator.end();
2481
2482 return kismpl::then(applicator.successfullyCompletedFuture(),
2483 [node = info->targetNode()] (std::future<bool> completed) {
2484 return completed.get() ? node : KisNodeSP();
2485 });
2486 }
2487
2488 //===========================================================
2489
2491 {
2493 KisPaintDeviceSP paintDevice = node->paintDevice();
2494 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, -1);
2495
2496 if (!paintDevice->keyframeChannel()) {
2497 return -1;
2498 }
2499
2500 const int activeTime = paintDevice->keyframeChannel()->activeKeyframeTime();
2501 KisRasterKeyframeSP keyframe = paintDevice->keyframeChannel()->activeKeyframeAt<KisRasterKeyframe>(activeTime);
2503
2504 return keyframe->frameID();
2505 }
2506
2508 {
2510 KisPaintDeviceSP paintDevice = node->paintDevice();
2511 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, -1);
2512
2513 if (!paintDevice->keyframeChannel()) {
2514 return -1;
2515 }
2516
2517 return paintDevice->keyframeChannel()->activeKeyframeTime();
2518 }
2519
2521 {
2523 KisPaintDeviceSP paintDevice = node->paintDevice();
2525 if (!paintDevice->keyframeChannel()) {
2526 return KisTimeSpan::infinite(0);
2527 }
2528
2529 return paintDevice->keyframeChannel()->affectedFrames(time);
2530 }
2531
2532 QSet<int> fetchLayerIdenticalRasterFrameTimes(KisNodeSP node, const int &frameTime)
2533 {
2534 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(node, QSet<int>());
2535 KisPaintDeviceSP paintDevice = node->paintDevice();
2536 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, QSet<int>());
2537 if (!paintDevice->keyframeChannel()) {
2538 return QSet<int>();
2539 }
2540
2541 return paintDevice->keyframeChannel()->clonesOf(node.data(), frameTime);
2542 }
2543
2544 /* Finds all frames matching a specific frame ID. useful to filter out duplicate frames. */
2545 QSet<int> fetchLayerRasterFrameTimesMatchingID(KisNodeSP node, const int frameID) {
2546 KIS_ASSERT(node);
2547 KisRasterKeyframeChannel* rasterChannel = dynamic_cast<KisRasterKeyframeChannel*>(node->getKeyframeChannel(KisKeyframeChannel::Raster.id(), false));
2548
2549 if (!rasterChannel) {
2550 return QSet<int>();
2551 }
2552
2553 return rasterChannel->timesForFrameID(frameID);
2554 }
2555
2556 QSet<int> fetchLayerRasterIDsAtTimes(KisNodeSP node, const QSet<int> &times)
2557 {
2558 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(node, QSet<int>());
2559 KisPaintDeviceSP paintDevice = node->paintDevice();
2560 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, QSet<int>());
2561 if (!paintDevice->keyframeChannel()) {
2562 return QSet<int>();
2563 }
2564
2565 QSet<int> frameIDs;
2566
2567 Q_FOREACH( const int& frame, times ) {
2569 frameIDs << raster->frameID();
2570 }
2571
2572 return frameIDs;
2573 }
2574
2575 QSet<int> filterTimesForOnlyRasterKeyedTimes(KisNodeSP node, const QSet<int> &times)
2576 {
2578 KisPaintDeviceSP paintDevice = node->paintDevice();
2579 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, times);
2580 if (!paintDevice->keyframeChannel()) {
2581 return times;
2582 }
2583
2584 return paintDevice->keyframeChannel()->allKeyframeTimes().intersect(times);
2585 }
2586
2587 QSet<int> fetchLayerUniqueRasterTimesMatchingIDs(KisNodeSP node, QSet<int>& frameIDs)
2588 {
2589 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(node, QSet<int>());
2590 KisPaintDeviceSP paintDevice = node->paintDevice();
2591 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, QSet<int>());
2592 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice->framesInterface(), QSet<int>());
2593
2594 QSet<int> uniqueTimes;
2595
2596 Q_FOREACH( const int& id, frameIDs) {
2597 QSet<int> times = fetchLayerRasterFrameTimesMatchingID(node, id);
2598 if (times.count() > 0) {
2599 uniqueTimes.insert(*times.begin());
2600 }
2601 }
2602
2603 return uniqueTimes;
2604 }
2605
2606 QSet<int> fetchUniqueFrameTimes(KisNodeSP node, QSet<int> selectedTimes, bool filterActiveFrameID)
2607 {
2608 if (selectedTimes.isEmpty() || !node->supportsKeyframeChannel(KisKeyframeChannel::Raster.id()))
2609 return selectedTimes;
2610
2611 // Convert a set of selected keyframe times into set of selected "frameIDs"...
2612 QSet<int> selectedFrameIDs = KisLayerUtils::fetchLayerRasterIDsAtTimes(node, selectedTimes);
2613
2614 if (filterActiveFrameID) {
2615 // Current frame was already filtered e.g. during filter preview in `KisFilterManager::apply`...
2616 // So let's remove it...
2617 const int currentActiveFrameID = KisLayerUtils::fetchLayerActiveRasterFrameID(node);
2618 selectedFrameIDs.remove(currentActiveFrameID);
2619 }
2620
2621 // Convert frameIDs to any arbitrary frame time associated with the frameID...
2622 QSet<int> uniqueFrameTimes = node->paintDevice()->framesInterface() ? KisLayerUtils::fetchLayerUniqueRasterTimesMatchingIDs(node, selectedFrameIDs) : QSet<int>();
2623
2624 return uniqueFrameTimes;
2625 }
2626
2627}
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:742
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 updateFrameJobsNonRecursive(FrameJobs *jobs, KisNodeSP rootNode, bool frameTimesRecursive)
updateFrameJobsNonRecursive updates the frame jobs non-recursively, that is, on the rootNode only.
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:504
bool alphaChannelDisabled() const
Definition kis_layer.cc:334
KisPSDLayerStyleSP layerStyle
Definition kis_layer.cc:171
const QList< KisCloneLayerWSP > registeredClones() const
Definition kis_layer.cc:484
void initSelection(KisSelectionSP copyFrom, KisLayerSP parentLayer)
initSelection initializes the selection for the mask from the given selection's projection.
Definition kis_mask.cc:157
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