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
1451 void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy* strategy, MergeFlags flags)
1452 {
1453 if (!layer->prevSibling()) return;
1454
1455 // XXX: this breaks if we allow free mixing of masks and layers
1456 KisLayerSP prevLayer = qobject_cast<KisLayer*>(layer->prevSibling().data());
1457
1458 while (prevLayer && prevLayer->isFakeNode()) {
1459 prevLayer = qobject_cast<KisLayer*>(prevLayer->prevSibling().data());
1460 }
1461
1462 if (!prevLayer) return;
1463
1464 if (!layer->visible() && !prevLayer->visible()) {
1465 return;
1466 }
1467
1468 KisImageSignalVector emitSignals;
1469 KisProcessingApplicator applicator(image, 0,
1471 emitSignals,
1472 kundo2_i18n("Merge Down"));
1473
1474 if (layer->visible() && prevLayer->visible()) {
1475 MergeDownInfoSP info(new MergeDownInfo(image, prevLayer, layer, flags));
1476
1477 // disable key strokes on all colorize masks, all onion skins on
1478 // paint layers and wait until update is finished with a barrier
1479 applicator.applyCommand(new DisableColorizeKeyStrokes(info));
1480 applicator.applyCommand(new DisableOnionSkins(info));
1482
1483 applicator.applyCommand(new KeepMergedNodesSelected(info, false));
1484 applicator.applyCommand(new FillSelectionMasks(info));
1486
1487 // NOTE: shape layer may have emitted spontaneous jobs during layer creation,
1488 // wait for them to complete!
1491
1492 // in two-layer mode we disable pass through only when the destination layer
1493 // is not a group layer
1494 applicator.applyCommand(new DisablePassThroughForHeadsOnly(info, true));
1496
1497 if (info->frames.size() > 0) {
1502 const int currentTimeOnStart = info->image->animationInterface()->currentTime();
1503
1504 foreach (int frame, info->frames) {
1505 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
1506
1507 applicator.applyCommand(new AddNewFrame(info, frame));
1516
1525 const bool skipMergingSourceLayer = !layer->isAnimated() &&
1526 frame != currentTimeOnStart;
1527
1528 applicator.applyCommand(new MergeLayers(info, skipMergingSourceLayer), KisStrokeJobData::BARRIER);
1529
1530 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage), KisStrokeJobData::BARRIER);
1531 }
1532 } else {
1533 applicator.applyCommand(new RefreshHiddenAreas(info));
1535 applicator.applyCommand(new MergeLayers(info, false), KisStrokeJobData::BARRIER);
1536 }
1537
1538 applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
1539 applicator.applyCommand(new CleanUpNodes(info, layer),
1542 applicator.applyCommand(new KeepMergedNodesSelected(info, true));
1543 } else if (layer->visible()) {
1545 layer, KisNodeSP(),
1546 image, false));
1547
1548 applicator.applyCommand(
1549 new SimpleRemoveLayers(KisNodeList() << prevLayer,
1550 image),
1553
1555 KisNodeSP(), layer,
1556 image, true));
1557 } else if (prevLayer->visible()) {
1559 layer, KisNodeSP(),
1560 image, false));
1561
1562 applicator.applyCommand(
1563 new SimpleRemoveLayers(KisNodeList() << layer,
1564 image),
1567
1569 KisNodeSP(), prevLayer,
1570 image, true));
1571 }
1572
1573 applicator.end();
1574 }
1575
1576 bool checkIsChildOf(KisNodeSP node, const KisNodeList &parents)
1577 {
1578 KisNodeList nodeParents;
1579
1580 KisNodeSP parent = node->parent();
1581 while (parent) {
1582 nodeParents << parent;
1583 parent = parent->parent();
1584 }
1585
1586 foreach(KisNodeSP perspectiveParent, parents) {
1587 if (nodeParents.contains(perspectiveParent)) {
1588 return true;
1589 }
1590 }
1591
1592 return false;
1593 }
1594
1595 bool checkIsCloneOf(KisNodeSP node, const KisNodeList &nodes)
1596 {
1597 bool result = false;
1598
1599 KisCloneLayer *clone = dynamic_cast<KisCloneLayer*>(node.data());
1600 if (clone) {
1601 KisNodeSP cloneSource = KisNodeSP(clone->copyFrom());
1602
1603 Q_FOREACH(KisNodeSP subtree, nodes) {
1604 result =
1605 recursiveFindNode(subtree,
1606 [cloneSource](KisNodeSP node) -> bool
1607 {
1608 return node == cloneSource;
1609 });
1610
1611 if (!result) {
1612 result = checkIsCloneOf(cloneSource, nodes);
1613 }
1614
1615 if (result) {
1616 break;
1617 }
1618 }
1619 }
1620
1621 return result;
1622 }
1623
1624 void filterMergeableNodes(KisNodeList &nodes, bool allowMasks)
1625 {
1626 KisNodeList::iterator it = nodes.begin();
1627
1628 while (it != nodes.end()) {
1629 if ((!allowMasks && !qobject_cast<KisLayer*>(it->data())) ||
1630 checkIsChildOf(*it, nodes)) {
1631 //qDebug() << "Skipping node" << ppVar((*it)->name());
1632 it = nodes.erase(it);
1633 } else {
1634 ++it;
1635 }
1636 }
1637 }
1638
1639 void sortMergeableNodes(KisNodeSP root, KisNodeList &inputNodes, KisNodeList &outputNodes)
1640 {
1641 KisNodeList::iterator it = std::find(inputNodes.begin(), inputNodes.end(), root);
1642
1643 if (it != inputNodes.end()) {
1644 outputNodes << *it;
1645 inputNodes.erase(it);
1646 }
1647
1648 if (inputNodes.isEmpty()) {
1649 return;
1650 }
1651
1652 KisNodeSP child = root->firstChild();
1653 while (child) {
1654 sortMergeableNodes(child, inputNodes, outputNodes);
1655 child = child->nextSibling();
1656 }
1657
1661 KIS_ASSERT_RECOVER_NOOP(root->parent() || inputNodes.isEmpty());
1662 }
1663
1665 {
1666 KisNodeList result;
1667 sortMergeableNodes(root, nodes, result);
1668 return result;
1669 }
1670
1672 {
1673 KIS_SAFE_ASSERT_RECOVER(!nodes.isEmpty()) { return nodes; }
1674
1675 KisNodeSP root;
1676 Q_FOREACH(KisNodeSP node, nodes) {
1677 KisNodeSP localRoot = node;
1678 while (localRoot->parent()) {
1679 localRoot = localRoot->parent();
1680 }
1681
1682 if (!root) {
1683 root = localRoot;
1684 }
1685 KIS_SAFE_ASSERT_RECOVER(root == localRoot) { return nodes; }
1686 }
1687
1688 KisNodeList result;
1689 sortMergeableNodes(root, nodes, result);
1690 filterMergeableNodes(result, allowMasks);
1691 return result;
1692 }
1693
1695 {
1696 KIS_SAFE_ASSERT_RECOVER(!nodes.isEmpty()) { return nodes; }
1697
1698 KisNodeSP root;
1699 Q_FOREACH(KisNodeSP node, nodes) {
1700 KisNodeSP localRoot = node;
1701 while (localRoot->parent()) {
1702 localRoot = localRoot->parent();
1703 }
1704
1705 if (!root) {
1706 root = localRoot;
1707 }
1708 KIS_SAFE_ASSERT_RECOVER(root == localRoot) { return nodes; }
1709 }
1710
1711 KisNodeList result;
1712 sortMergeableNodes(root, nodes, result);
1713 return result;
1714 }
1715
1717 KisNodeList filteredNodes = nodes;
1718 KisNodeList sortedNodes;
1719
1720 KisLayerUtils::filterMergeableNodes(filteredNodes, true);
1721
1722 bool haveExternalNodes = false;
1723 Q_FOREACH (KisNodeSP node, nodes) {
1724 if (node->graphListener() != image->root()->graphListener()) {
1725 haveExternalNodes = true;
1726 break;
1727 }
1728 }
1729
1730 if (!haveExternalNodes) {
1731 KisLayerUtils::sortMergeableNodes(image->root(), filteredNodes, sortedNodes);
1732 } else {
1733 sortedNodes = filteredNodes;
1734 }
1735
1736 return sortedNodes;
1737 }
1738
1739
1741 {
1742 if (!KisImageConfig(true).renameDuplicatedLayers()) { return; }
1743
1744 const QString prefix = i18n("Copy of");
1745 QString newName = node->name();
1746 if (!newName.startsWith(prefix)) {
1747 newName = QString("%1 %2").arg(prefix).arg(newName);
1748 node->setName(newName);
1749 }
1750 }
1751
1752 KisNodeList findNodesWithProps(KisNodeSP root, const KoProperties &props, bool excludeRoot)
1753 {
1754 KisNodeList nodes;
1755
1756 if ((!excludeRoot || root->parent()) && root->check(props)) {
1757 nodes << root;
1758 }
1759
1760 KisNodeSP node = root->firstChild();
1761 while (node) {
1762 nodes += findNodesWithProps(node, props, excludeRoot);
1763 node = node->nextSibling();
1764 }
1765
1766 return nodes;
1767 }
1768
1769 KisNodeList filterInvisibleNodes(const KisNodeList &nodes, KisNodeList *invisibleNodes, KisNodeSP *putAfter)
1770 {
1771 KIS_ASSERT_RECOVER(invisibleNodes) { return nodes; }
1772 KIS_ASSERT_RECOVER(putAfter) { return nodes; }
1773
1774 KisNodeList visibleNodes;
1775 int putAfterIndex = -1;
1776
1777 Q_FOREACH(KisNodeSP node, nodes) {
1778 if (node->visible()) {
1779 visibleNodes << node;
1780 } else if (node->userLocked()) {
1785 } else {
1786 *invisibleNodes << node;
1787
1788 if (node == *putAfter) {
1789 putAfterIndex = visibleNodes.size() - 1;
1790 }
1791 }
1792 }
1793
1794 if (!visibleNodes.isEmpty() && putAfterIndex >= 0) {
1795 putAfterIndex = qBound(0, putAfterIndex, visibleNodes.size() - 1);
1796 *putAfter = visibleNodes[putAfterIndex];
1797 }
1798
1799 return visibleNodes;
1800 }
1801
1803 {
1804 KisNodeList::iterator it = nodes.begin();
1805
1806 while (it != nodes.end()) {
1807 if ((*it)->userLocked()) {
1808 it = nodes.erase(it);
1809 } else {
1810 ++it;
1811 }
1812 }
1813 }
1814
1816 {
1817 KisImageSignalVector emitSignals;
1818 KisProcessingApplicator applicator(image,
1819 image->root(),
1821 emitSignals,
1822 kundo2_i18n("Change projection color"),
1823 0,
1826 applicator.end();
1827 }
1828
1830 {
1832 : m_info(info)
1833 , m_commands(commands)
1834 , m_cleanupNodes(cleanupNodes)
1835 {
1836 }
1837
1839 if (!m_cleanupNodes && !m_info->ephemeralCommandsStore) {
1840 m_info->ephemeralCommandsStore.reset(new KisSurrogateUndoStore());
1841 }
1842
1843 Q_FOREACH (KUndo2Command *cmd, m_commands) {
1844 if (m_cleanupNodes) {
1845 addCommand(cmd);
1846 } else {
1847 m_info->ephemeralCommandsStore->addCommand(cmd);
1848 }
1849 }
1850 m_commands.clear();
1851 m_info.reset();
1852 }
1853
1854 private:
1857 bool m_cleanupNodes {true};
1858 };
1859
1861 {
1863 : m_info(info)
1864 {
1865 }
1866
1868 KIS_SAFE_ASSERT_RECOVER_RETURN(m_info->ephemeralCommandsStore);
1869 m_info->ephemeralCommandsStore->undoAll();
1870
1871 }
1872
1873 private:
1875 };
1876
1902 void mergeMultipleLayersImpl(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter,
1903 bool flattenSingleLayer, const KUndo2MagicString &actionName,
1904 bool cleanupNodes = true, const QString layerName = QString(),
1905 MergeFlags flags = None)
1906 {
1907 if (!putAfter) {
1908 putAfter = mergedNodes.first();
1909 }
1910
1911 filterMergeableNodes(mergedNodes);
1912 {
1913 KisNodeList tempNodes;
1914 std::swap(mergedNodes, tempNodes);
1915 sortMergeableNodes(image->root(), tempNodes, mergedNodes);
1916 }
1917
1918 if (mergedNodes.size() <= 1 &&
1919 (!flattenSingleLayer && mergedNodes.size() == 1)) return;
1920
1921 KisImageSignalVector emitSignals;
1922 emitSignals << ComplexNodeReselectionSignal(KisNodeSP(), KisNodeList(), KisNodeSP(), mergedNodes);
1923
1924
1925
1926 KisNodeList originalNodes = mergedNodes;
1927 KisNodeList invisibleNodes;
1928 mergedNodes = filterInvisibleNodes(originalNodes, &invisibleNodes, &putAfter);
1929
1930 if (mergedNodes.isEmpty()) return;
1931
1932
1933 // make sure we don't add the new layer into a locked group
1935 while (putAfter->parent() && !putAfter->parent()->isEditable()) {
1936 putAfter = putAfter->parent();
1937 }
1938
1946 if (!putAfter->parent()) {
1947 return;
1948 }
1949
1950 KisProcessingApplicator applicator(image, 0,
1952 emitSignals,
1953 actionName);
1954
1955
1956 if (!invisibleNodes.isEmpty() && cleanupNodes) {
1957
1958 /* If the putAfter node is invisible,
1959 * we should instead pick one of the nodes
1960 * to be merged to avoid a null putAfter
1961 * after we remove all invisible layers from
1962 * the image.
1963 * (The assumption is that putAfter is among
1964 * the layers to merge, so if it's invisible,
1965 * it's going to be removed)
1966 */
1967 if (!putAfter->visible()){
1968 putAfter = mergedNodes.first();
1969 }
1970
1971 applicator.applyCommand(
1972 new SimpleRemoveLayers(invisibleNodes,
1973 image),
1976 }
1977
1978 if (mergedNodes.size() > 1 || invisibleNodes.isEmpty()) {
1979 MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes, flags));
1980
1981 // disable key strokes on all colorize masks, all onion skins on
1982 // paint layers and wait until update is finished with a barrier
1983 //
1984 // when doing "new layer from visible" we should undo these changes
1985 // before the action stops, because the source layers are **not**
1986 // removed as a result of this action
1987 applicator.applyCommand(
1988 new EphemeralCommandsWrapper(info,
1989 {
1990 new DisableColorizeKeyStrokes(info),
1991 new DisableOnionSkins(info),
1993 },
1994 cleanupNodes));
1996
1997 applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, false));
1998 applicator.applyCommand(new FillSelectionMasks(info));
1999 applicator.applyCommand(new CreateMergedLayerMultiple(info, layerName), KisStrokeJobData::BARRIER);
2000 applicator.applyCommand(new EphemeralCommandsWrapper(info, { new DisableExtraCompositing(info) } , cleanupNodes));
2002
2003 if (!info->frames.isEmpty()) {
2004 foreach (int frame, info->frames) {
2005 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
2006
2007 applicator.applyCommand(new AddNewFrame(info, frame));
2017
2018 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
2019 }
2020 } else {
2021 applicator.applyCommand(new RefreshHiddenAreas(info));
2024 }
2025
2026 //applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER);
2027 if (cleanupNodes){
2028 applicator.applyCommand(new CleanUpNodes(info, putAfter, flattenSingleLayer),
2031 } else {
2032 applicator.applyCommand(new UndoEphemeralCommands(info));
2033 applicator.applyCommand(new InsertNode(info, putAfter),
2036 }
2037
2038 applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, true));
2039 }
2040
2041 applicator.end();
2042
2043 }
2044
2045 void mergeMultipleLayers(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, MergeFlags flags)
2046 {
2047 mergeMultipleLayersImpl(image, mergedNodes, putAfter,
2048 false, kundo2_i18n("Merge Selected Nodes"),
2049 true, QString(),
2050 flags);
2051 }
2052
2053 void mergeMultipleNodes(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, MergeFlags flags)
2054 {
2055 if (!tryMergeSelectionMasks(image, mergedNodes, putAfter)) {
2056 mergeMultipleLayers(image, mergedNodes, putAfter, flags);
2057 }
2058 }
2059
2060 void newLayerFromVisible(KisImageSP image, KisNodeSP putAfter, MergeFlags flags)
2061 {
2062 KisNodeList mergedNodes;
2063 mergedNodes << image->root();
2064
2065 mergeMultipleLayersImpl(image, mergedNodes, putAfter,
2066 true, kundo2_i18n("New From Visible"),
2067 false, i18nc("New layer created from all the visible layers", "Visible"),
2068 flags);
2069 }
2070
2073 : m_info(info),
2074 m_putAfter(putAfter){}
2075
2076 void populateChildCommands() override {
2077 KisNodeSP parent;
2078 CleanUpNodes::findPerfectParent(m_info->allSrcNodes(), m_putAfter, parent);
2079
2080 KisLayerSP parentLayer;
2081 do {
2082 parentLayer = qobject_cast<KisLayer*>(parent.data());
2083
2084 parent = parent->parent();
2085 } while(!parentLayer && parent);
2086
2087 KisSelectionSP selection = new KisSelection();
2088
2089 foreach (KisNodeSP node, m_info->allSrcNodes()) {
2090 KisMaskSP mask = dynamic_cast<KisMask*>(node.data());
2091 if (!mask) continue;
2092
2093 selection->pixelSelection()->applySelection(
2095 }
2096
2097 KisSelectionMaskSP mergedMask = new KisSelectionMask(m_info->image, i18n("Selection Mask"));
2098 mergedMask->initSelection(parentLayer);
2099 mergedMask->setSelection(selection);
2100
2101 m_info->dstNode = mergedMask;
2102 }
2103
2104 private:
2107 };
2108
2112
2113 void populateChildCommands() override {
2114 KisSelectionMaskSP mergedMask = dynamic_cast<KisSelectionMask*>(m_info->dstNode.data());
2115 addCommand(new KisActivateSelectionMaskCommand(mergedMask, true));
2116 }
2117
2118 private:
2120 };
2121
2122 bool tryMergeSelectionMasks(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter)
2123 {
2124 QList<KisSelectionMaskSP> selectionMasks;
2125
2126 for (auto it = mergedNodes.begin(); it != mergedNodes.end(); /*noop*/) {
2127 KisSelectionMaskSP mask = dynamic_cast<KisSelectionMask*>(it->data());
2128 if (!mask) {
2129 it = mergedNodes.erase(it);
2130 } else {
2131 selectionMasks.append(mask);
2132 ++it;
2133 }
2134 }
2135
2136 if (mergedNodes.isEmpty()) return false;
2137
2138 KisLayerSP parentLayer = qobject_cast<KisLayer*>(selectionMasks.first()->parent().data());
2139 KIS_ASSERT_RECOVER(parentLayer) { return 0; }
2140
2141 KisImageSignalVector emitSignals;
2142
2143 KisProcessingApplicator applicator(image, 0,
2145 emitSignals,
2146 kundo2_i18n("Merge Selection Masks"));
2147
2148 MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes, None));
2149
2150
2151 applicator.applyCommand(new MergeSelectionMasks(info, putAfter));
2152 applicator.applyCommand(new CleanUpNodes(info, putAfter),
2155 applicator.applyCommand(new ActivateSelectionMask(info));
2156 applicator.end();
2157
2158 return true;
2159 }
2160
2161 void flattenLayer(KisImageSP image, KisLayerSP layer, MergeFlags flags)
2162 {
2163 if (!layer->childCount() && !layer->layerStyle())
2164 return;
2165
2166 KisNodeList mergedNodes;
2167 mergedNodes << layer;
2168
2169 mergeMultipleLayersImpl(image, mergedNodes, layer,
2170 true, kundo2_i18n("Flatten Layer"),
2171 true, QString(),
2172 flags);
2173 }
2174
2175 void flattenImage(KisImageSP image, KisNodeSP activeNode, MergeFlags flags)
2176 {
2177 if (!activeNode) {
2178 activeNode = image->root()->lastChild();
2179 }
2180
2181
2182 KisNodeList mergedNodes;
2183 mergedNodes << image->root();
2184
2185 mergeMultipleLayersImpl(image, mergedNodes, activeNode,
2186 true, kundo2_i18n("Flatten Image"),
2187 true, QString(),
2188 flags);
2189 }
2190
2192 : FlipFlopCommand(finalize, parent),
2193 m_nodes(nodes)
2194 {
2195 }
2201 {
2202 Q_FOREACH(KisNodeSP node, nodes) {
2203 node->setDirty(node->extent());
2204 }
2205 }
2206
2207 KisNodeSP recursiveFindNode(KisNodeSP node, std::function<bool(KisNodeSP)> func)
2208 {
2209 if (func(node)) {
2210 return node;
2211 }
2212
2213 node = node->firstChild();
2214 while (node) {
2215 KisNodeSP resultNode = recursiveFindNode(node, func);
2216 if (resultNode) {
2217 return resultNode;
2218 }
2219 node = node->nextSibling();
2220 }
2221
2222 return 0;
2223 }
2224
2225 KisNodeSP findNodeByUuid(KisNodeSP root, const QUuid &uuid)
2226 {
2227 return recursiveFindNode(root,
2228 [uuid] (KisNodeSP node) {
2229 return node->uuid() == uuid;
2230 });
2231 }
2232
2233 QList<KisNodeSP> findNodesByName(KisNodeSP root, const QString &name, bool recursive, bool partialMatch)
2234 {
2235 KisNodeList nodeList;
2236 KisNodeSP child = root->firstChild();
2237
2238 while (child) {
2239 if (name.isEmpty() || (!partialMatch && child->name() == name) || (partialMatch && child->name().contains(name, Qt::CaseInsensitive))) {
2240 nodeList << child;
2241 }
2242 if (recursive && child->childCount() > 0) {
2243 nodeList << findNodesByName(child, name, recursive, partialMatch);
2244 }
2245 child = child->nextSibling();
2246 }
2247
2248 return nodeList;
2249 }
2250
2251 KisNodeSP findNodeByName(KisNodeSP root, const QString &name)
2252 {
2253 return recursiveFindNode(root,
2254 [name] (KisNodeSP node) {
2255 return node->name() == name;
2256 });
2257 }
2258
2260 {
2261 KisImageSP image = node->image();
2262 if (image)
2263 return image->isolationRootNode();
2264 return nullptr;
2265 }
2266
2268 {
2270 [] (KisNodeSP node) {
2271 KisDelayedUpdateNodeInterface *delayedUpdate =
2272 dynamic_cast<KisDelayedUpdateNodeInterface*>(node.data());
2273 if (delayedUpdate) {
2274 delayedUpdate->forceUpdateTimedNode();
2275 }
2276 });
2277 }
2278
2280 {
2281 return recursiveFindNode(root,
2282 [] (KisNodeSP node) {
2283 KisDelayedUpdateNodeInterface *delayedUpdate =
2284 dynamic_cast<KisDelayedUpdateNodeInterface*>(node.data());
2285
2286 return delayedUpdate ? delayedUpdate->hasPendingTimedUpdates() : false;
2287 });
2288 }
2289
2291 {
2293 [] (KisNodeSP node) {
2294 KisCroppedOriginalLayerInterface *croppedUpdate =
2295 dynamic_cast<KisCroppedOriginalLayerInterface*>(node.data());
2296 if (croppedUpdate) {
2297 croppedUpdate->forceUpdateHiddenAreaOnOriginal();
2298 }
2299 });
2300 }
2301
2303 {
2304 while (node) {
2305 const KisLayer *layer = dynamic_cast<const KisLayer*>(node.data());
2306 if (layer) {
2307 return layer->image();
2308 }
2309
2310 node = node->parent();
2311 }
2312
2313 return 0;
2314 }
2315
2316 namespace Private {
2317 QRect realNodeChangeRect(KisNodeSP rootNode, QRect currentRect = QRect()) {
2318 KisNodeSP node = rootNode->firstChild();
2319
2320 while(node) {
2321 currentRect |= realNodeChangeRect(node, currentRect);
2322 node = node->nextSibling();
2323 }
2324
2325 if (!rootNode->isFakeNode() && !rootNode->projectionLeaf()->isMask()) {
2326 // TODO: it would be better to count up changeRect inside
2327 // node's extent() method
2328 //
2329 // NOTE: when flattening a group layer, we should take the change rect of the
2330 // all the child layers as the source of the change. We are calculating
2331 // the change rect **before** the update itself, therefore rootNode->exactBounds()
2332 // is not yet prepared, hence its exact bounds still contail old values.
2333 currentRect |= rootNode->projectionPlane()->changeRect(rootNode->exactBounds() | currentRect);
2334 }
2335
2336 return currentRect;
2337 }
2338 }
2339
2340namespace Private {
2341 void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea, const QRect &extraUpdateRect) {
2342 QRect realNodeRect = Private::realNodeChangeRect(rootNode) | extraUpdateRect;
2343 if (!preparedArea.contains(realNodeRect)) {
2344
2345 QRegion dirtyRegion = realNodeRect;
2346 dirtyRegion -= preparedArea;
2347
2348 auto rc = dirtyRegion.begin();
2349 while (rc != dirtyRegion.end()) {
2350 image->refreshGraphAsync(rootNode, *rc, realNodeRect);
2351 rc++;
2352 }
2353 }
2354 }
2355} // namespace Private
2356
2357 void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea) {
2358 Private::refreshHiddenAreaAsync(image, rootNode, preparedArea, QRect());
2359 }
2360
2362 {
2363 QRect exactBounds;
2364 recursiveApplyNodes(rootNode, [&exactBounds] (KisNodeSP node) {
2365 exactBounds |= node->projectionPlane()->tightUserVisibleBounds();
2366 });
2367 return exactBounds;
2368 }
2369
2371 {
2372 if (!node) return node;
2373
2374 while (node->parent()) {
2375 node = node->parent();
2376 }
2377 return node;
2378 }
2379
2381 {
2382 int numLayers = 0;
2383 bool hasNonNormalLayers = false;
2384 bool hasTransparentLayer = false;
2385
2386
2387 recursiveApplyNodes(image->root(),
2388 [&numLayers, &hasNonNormalLayers, &hasTransparentLayer, image] (KisNodeSP node) {
2389 if (!node->inherits("KisLayer")) return;
2390
2391 numLayers++;
2392
2393 if (node->exactBounds().isEmpty()) return;
2394
2395 // this is only an approximation! it is not exact!
2396 if (!hasTransparentLayer &&
2397 node->exactBounds() != image->bounds()) {
2398
2399 hasTransparentLayer = true;
2400 }
2401
2402 if (!hasNonNormalLayers &&
2403 node->compositeOpId() != COMPOSITE_OVER) {
2404
2405 hasNonNormalLayers = true;
2406 }
2407 });
2408
2409 return numLayers == 1 || (!hasNonNormalLayers && !hasTransparentLayer);
2410 }
2411
2412 void splitAlphaToMask(KisImageSP image, KisNodeSP node, const QString& maskName)
2413 {
2414 SplitAlphaToMaskInfoSP info( new SplitAlphaToMaskInfo(node->image(), node, maskName) );
2415
2416 KisImageSignalVector emitSignals;
2417 KisProcessingApplicator applicator(image, 0,
2419 emitSignals,
2420 kundo2_i18n("Split Alpha into a Mask"));
2421
2422 applicator.applyCommand(new SimpleAddNode(info->image, info->getMask(), info->node), KisStrokeJobData::BARRIER);
2423 applicator.applyCommand(new InitSplitAlphaSelectionMask(info));
2424 if (info->frames.count() > 0) {
2425 Q_FOREACH(const int& frame, info->frames) {
2426 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage));
2427 applicator.applyCommand(new AddNewFrame(info->getMask(), frame, info->node));
2429 applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage));
2430 }
2431 } else {
2433 }
2434 applicator.end();
2435 }
2436
2437 std::future<KisNodeSP> convertToPaintLayer(KisImageSP image, KisNodeSP src)
2438 {
2439 //Initialize all operation dependencies.
2440 ConvertToPaintLayerInfoSP info( new ConvertToPaintLayerInfo(image, src) );
2441
2442 if (!info->hasTargetNode())
2444
2445 KisImageSignalVector emitSignals;
2446 KisProcessingApplicator applicator(image, 0, KisProcessingApplicator::NONE, emitSignals, kundo2_i18n("Convert to a Paint Layer"));
2447
2448 applicator.applyCommand(new SimpleAddNode(info->image(), info->targetNode(), info->insertionParent(), info->insertionPutAfter()), KisStrokeJobData::BARRIER);
2449
2450 if (info->frames().count() > 0) {
2451 Q_FOREACH(const int& frame, info->frames()) {
2452 applicator.applyCommand(new SwitchFrameCommand(info->image(), frame, false, info->storage));
2453 applicator.applyCommand(new RefreshDelayedUpdateLayers(info->sourceNodes()), KisStrokeJobData::BARRIER);
2454 applicator.applyCommand(new RefreshHiddenAreas(info->image(), info->sourceNode()), KisStrokeJobData::BARRIER);
2455 applicator.applyCommand(new AddNewFrame(info->targetNode(), frame, info->sourceNode()), KisStrokeJobData::BARRIER);
2456 applicator.applyCommand(new UploadProjectionToFrameCommand(info->sourceNode(), info->targetNode(), frame));
2457 applicator.applyCommand(new SwitchFrameCommand(info->image(), frame, true, info->storage));
2458 }
2459 }
2460
2461 applicator.applyCommand(new SimpleRemoveLayers(info->toRemove(), info->image()));
2462
2463 applicator.end();
2464
2465 return kismpl::then(applicator.successfullyCompletedFuture(),
2466 [node = info->targetNode()] (std::future<bool> completed) {
2467 return completed.get() ? node : KisNodeSP();
2468 });
2469 }
2470
2471 //===========================================================
2472
2474 {
2476 KisPaintDeviceSP paintDevice = node->paintDevice();
2477 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, -1);
2478
2479 if (!paintDevice->keyframeChannel()) {
2480 return -1;
2481 }
2482
2483 const int activeTime = paintDevice->keyframeChannel()->activeKeyframeTime();
2484 KisRasterKeyframeSP keyframe = paintDevice->keyframeChannel()->activeKeyframeAt<KisRasterKeyframe>(activeTime);
2486
2487 return keyframe->frameID();
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 return paintDevice->keyframeChannel()->activeKeyframeTime();
2501 }
2502
2504 {
2506 KisPaintDeviceSP paintDevice = node->paintDevice();
2508 if (!paintDevice->keyframeChannel()) {
2509 return KisTimeSpan::infinite(0);
2510 }
2511
2512 return paintDevice->keyframeChannel()->affectedFrames(time);
2513 }
2514
2515 QSet<int> fetchLayerIdenticalRasterFrameTimes(KisNodeSP node, const int &frameTime)
2516 {
2517 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(node, QSet<int>());
2518 KisPaintDeviceSP paintDevice = node->paintDevice();
2519 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, QSet<int>());
2520 if (!paintDevice->keyframeChannel()) {
2521 return QSet<int>();
2522 }
2523
2524 return paintDevice->keyframeChannel()->clonesOf(node.data(), frameTime);
2525 }
2526
2527 /* Finds all frames matching a specific frame ID. useful to filter out duplicate frames. */
2528 QSet<int> fetchLayerRasterFrameTimesMatchingID(KisNodeSP node, const int frameID) {
2529 KIS_ASSERT(node);
2530 KisRasterKeyframeChannel* rasterChannel = dynamic_cast<KisRasterKeyframeChannel*>(node->getKeyframeChannel(KisKeyframeChannel::Raster.id(), false));
2531
2532 if (!rasterChannel) {
2533 return QSet<int>();
2534 }
2535
2536 return rasterChannel->timesForFrameID(frameID);
2537 }
2538
2539 QSet<int> fetchLayerRasterIDsAtTimes(KisNodeSP node, const QSet<int> &times)
2540 {
2541 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(node, QSet<int>());
2542 KisPaintDeviceSP paintDevice = node->paintDevice();
2543 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, QSet<int>());
2544 if (!paintDevice->keyframeChannel()) {
2545 return QSet<int>();
2546 }
2547
2548 QSet<int> frameIDs;
2549
2550 Q_FOREACH( const int& frame, times ) {
2552 frameIDs << raster->frameID();
2553 }
2554
2555 return frameIDs;
2556 }
2557
2558 QSet<int> filterTimesForOnlyRasterKeyedTimes(KisNodeSP node, const QSet<int> &times)
2559 {
2561 KisPaintDeviceSP paintDevice = node->paintDevice();
2562 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, times);
2563 if (!paintDevice->keyframeChannel()) {
2564 return times;
2565 }
2566
2567 return paintDevice->keyframeChannel()->allKeyframeTimes().intersect(times);
2568 }
2569
2570 QSet<int> fetchLayerUniqueRasterTimesMatchingIDs(KisNodeSP node, QSet<int>& frameIDs)
2571 {
2572 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(node, QSet<int>());
2573 KisPaintDeviceSP paintDevice = node->paintDevice();
2574 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice, QSet<int>());
2575 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(paintDevice->framesInterface(), QSet<int>());
2576
2577 QSet<int> uniqueTimes;
2578
2579 Q_FOREACH( const int& id, frameIDs) {
2580 QSet<int> times = fetchLayerRasterFrameTimesMatchingID(node, id);
2581 if (times.count() > 0) {
2582 uniqueTimes.insert(*times.begin());
2583 }
2584 }
2585
2586 return uniqueTimes;
2587 }
2588
2589 QSet<int> fetchUniqueFrameTimes(KisNodeSP node, QSet<int> selectedTimes, bool filterActiveFrameID)
2590 {
2591 if (selectedTimes.isEmpty() || !node->supportsKeyframeChannel(KisKeyframeChannel::Raster.id()))
2592 return selectedTimes;
2593
2594 // Convert a set of selected keyframe times into set of selected "frameIDs"...
2595 QSet<int> selectedFrameIDs = KisLayerUtils::fetchLayerRasterIDsAtTimes(node, selectedTimes);
2596
2597 if (filterActiveFrameID) {
2598 // Current frame was already filtered e.g. during filter preview in `KisFilterManager::apply`...
2599 // So let's remove it...
2600 const int currentActiveFrameID = KisLayerUtils::fetchLayerActiveRasterFrameID(node);
2601 selectedFrameIDs.remove(currentActiveFrameID);
2602 }
2603
2604 // Convert frameIDs to any arbitrary frame time associated with the frameID...
2605 QSet<int> uniqueFrameTimes = node->paintDevice()->framesInterface() ? KisLayerUtils::fetchLayerUniqueRasterTimesMatchingIDs(node, selectedFrameIDs) : QSet<int>();
2606
2607 return uniqueFrameTimes;
2608 }
2609
2610}
KisMagneticGraph::vertex_descriptor target(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
@ SELECTION_ADD
const quint8 OPACITY_OPAQUE_U8
const QString COMPOSITE_OVER
const QString COMPOSITE_BEHIND
KUndo2Command(KUndo2Command *parent=0)
KisPaintDeviceSP coloringProjection() const
virtual void forceUpdateHiddenAreaOnOriginal()=0
virtual QRect bounds() const =0
The KisDelayedUpdateNodeInterface class is an interface for nodes that delay their real updates with ...
virtual void forceUpdateTimedNode()=0
forceUpdateTimedNode forces the node to regenerate its project. The update might be asynchronous,...
virtual bool hasPendingTimedUpdates() const =0
The command for adding a layer.
The command for layer moves inside the layer stack.
void emitNotification(KisImageSignalType type)
void refreshGraphAsync(KisNodeSP root, const QVector< QRect > &rects, const QRect &cropRect, KisProjectionUpdateFlags flags=KisProjectionUpdateFlag::None) override
KisGroupLayerSP rootLayer() const
const KoColorSpace * colorSpace() const
KisImageAnimationInterface * animationInterface() const
QString nextLayerName(const QString &baseName="") const
Definition kis_image.cc:715
QVector< QRect > enableUIUpdates() override
KisNodeSP isolationRootNode() const
KisImageSignalRouter * signalRouter()
QRect bounds() const override
KisKeyframeChannel stores and manages KisKeyframes. Maps units of time to virtual keyframe values....
QSet< int > allKeyframeTimes() const
Get a set of all integer times that map to a keyframe.
static const KoID Raster
KisKeyframeSP keyframeAt(int time) const
Get a keyframe at specified time. Used primarily when the value of a given keyframe is needed.
void addKeyframe(int time, KUndo2Command *parentUndoCmd=nullptr)
Add a new keyframe to the channel at the specified time.
virtual KisTimeSpan affectedFrames(int time) const
Get the set of frames affected by any changes to the value or content of the active keyframe at the g...
KisKeyframeSP activeKeyframeAt(int time) const
int activeKeyframeTime(int time) const
Get the time of the active keyframe. Useful for snapping any time to that of the most recent keyframe...
static QVariant nodeProperty(KisNodeSP node, const KoID &id, const QVariant &defaultValue)
static void setNodeProperty(KisBaseNode::PropertyList *props, const KoID &id, const QVariant &value)
KeepNodesSelectedCommand(const KisNodeList &selectedBefore, const KisNodeList &selectedAfter, KisNodeSP activeBefore, KisNodeSP activeAfter, KisImageSP image, bool finalize, KUndo2Command *parent=0)
static void updateNodes(const KisNodeList &nodes)
KisSimpleUpdateCommand(KisNodeList nodes, bool finalize, KUndo2Command *parent=0)
virtual void addCommandImpl(KUndo2Command *cmd)=0
void safeReplaceMultipleNodes(KisNodeList removedNodes, KisImageSP image, std::optional< ReplacementNode > replacementNode)
void safeRemoveMultipleNodes(KisNodeList nodes, KisImageSP image)
bool checkIsSourceForClone(KisNodeSP src, const KisNodeList &nodes)
static bool scanForLastLayer(KisImageWSP image, KisNodeList nodesToRemove)
virtual void merge(Store *dst, QList< const Store * > srcs, QList< double > scores) const =0
The command for setting the composite op.
The command for changing the property list of a layer.
void writeFrameToDevice(int frameId, KisPaintDeviceSP targetDevice)
void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice)
KisRasterKeyframeChannel * keyframeChannel() const
void setDefaultPixel(const KoColor &defPixel)
virtual const KoColorSpace * compositionSourceColorSpace() const
KisPaintDeviceFramesInterface * framesInterface()
QRect exactBounds() const
QRect extent() const
const KoColorSpace * colorSpace() const
KoColor defaultPixel() const
KisDefaultBoundsBaseSP defaultBounds() const
static void copyAreaOptimized(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &originalSrcRect)
void applyCommand(KUndo2Command *command, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
std::future< bool > && successfullyCompletedFuture()
The KisRasterKeyframeChannel is a concrete KisKeyframeChannel subclass that stores and manages KisRas...
QSet< int > timesForFrameID(int frameID) const
The KisRasterKeyframe class is a concrete subclass of KisKeyframe that wraps a physical raster image ...
The KisScalarKeyframeChannel is a concrete KisKeyframeChannel subclass that stores and manages KisSca...
virtual KisTimeSpan identicalFrames(int time) const override
Get a span of times for which the channel gives identical results compared to frame at a given time....
ALWAYS_INLINE quint8 * rawData()
int start() const
bool isInfinite() const
static KisTimeSpan infinite(int start)
int end() const
bool isValid() const
virtual void setOpacity(quint8 *pixels, quint8 alpha, qint32 nPixels) const =0
virtual quint8 opacityU8(const quint8 *pixel) const =0
KoColor convertedTo(const KoColorSpace *cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
Definition KoColor.cpp:163
QString id() const
Definition KoID.cpp:63
#define KIS_ASSERT_RECOVER(cond)
Definition kis_assert.h:55
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define bounds(x, a, b)
QList< KisNodeSP > KisNodeList
Definition kis_types.h:264
KisSharedPtr< KisNode > KisNodeSP
Definition kis_types.h:86
KUndo2MagicString kundo2_i18n(const char *text)
QRect realNodeChangeRect(KisNodeSP rootNode, QRect currentRect=QRect())
void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea, const QRect &extraUpdateRect)
void flattenImage(KisImageSP image, KisNodeSP activeNode, MergeFlags flags)
bool checkIsCloneOf(KisNodeSP node, const KisNodeList &nodes)
KisNodeSP findIsolationRoot(KisNodeSP node)
QSet< int > fetchLayerFrames(KisNodeSP node)
QSet< int > fetchUniqueFrameTimes(KisNodeSP node, QSet< int > selectedTimes, bool filterActiveFrameID)
bool hasDelayedNodeWithUpdates(KisNodeSP root)
void updateFrameJobs(FrameJobs *jobs, KisNodeSP node)
QSet< int > filterTimesForOnlyRasterKeyedTimes(KisNodeSP node, const QSet< int > &times)
void updateFrameJobsRecursive(FrameJobs *jobs, KisNodeSP rootNode)
KisNodeSP recursiveFindNode(KisNodeSP node, std::function< bool(KisNodeSP)> func)
void splitAlphaToMask(KisImageSP image, KisNodeSP node, const QString &maskName)
void changeImageDefaultProjectionColor(KisImageSP image, const KoColor &color)
KisTimeSpan fetchLayerActiveRasterFrameSpan(KisNodeSP node, const int time)
KisNodeList sortAndFilterMergeableInternalNodes(KisNodeList nodes, bool allowMasks)
QMap< int, QSet< KisNodeSP > > FrameJobs
int fetchLayerActiveRasterFrameID(KisNodeSP node)
QSharedPointer< SplitAlphaToMaskInfo > SplitAlphaToMaskInfoSP
QSharedPointer< MergeDownInfo > MergeDownInfoSP
void sortMergeableNodes(KisNodeSP root, KisNodeList &inputNodes, KisNodeList &outputNodes)
QRect recursiveTightNodeVisibleBounds(KisNodeSP rootNode)
KisImageSP findImageByHierarchy(KisNodeSP node)
QSet< int > fetchLayerUniqueRasterTimesMatchingIDs(KisNodeSP node, QSet< int > &frameIDs)
KisNodeSP findNodeByName(KisNodeSP root, const QString &name)
KisNodeList filterInvisibleNodes(const KisNodeList &nodes, KisNodeList *invisibleNodes, KisNodeSP *putAfter)
void flattenLayer(KisImageSP image, KisLayerSP layer, MergeFlags flags)
QSet< int > fetchLayerRasterIDsAtTimes(KisNodeSP node, const QSet< int > &times)
KisNodeSP findRoot(KisNodeSP node)
QList< KisNodeSP > findNodesByName(KisNodeSP root, const QString &name, bool recursive, bool partialMatch)
QSharedPointer< ConvertToPaintLayerInfo > ConvertToPaintLayerInfoSP
KisNodeList findNodesWithProps(KisNodeSP root, const KoProperties &props, bool excludeRoot)
void addCopyOfNameTag(KisNodeSP node)
KisNodeSP findNodeByUuid(KisNodeSP root, const QUuid &uuid)
void newLayerFromVisible(KisImageSP image, KisNodeSP putAfter, MergeFlags flags)
void filterUnlockedNodes(KisNodeList &nodes)
void mergeMultipleLayersImpl(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, bool flattenSingleLayer, const KUndo2MagicString &actionName, bool cleanupNodes=true, const QString layerName=QString(), MergeFlags flags=None)
bool canChangeImageProfileInvisibly(KisImageSP image)
void recursiveApplyNodes(NodePointer node, Functor func)
int fetchLayerActiveRasterFrameTime(KisNodeSP node)
void splitNonRemovableNodes(KisNodeList &nodesToRemove, KisNodeList &_nodesToHide)
KisNodeList sortAndFilterAnyMergeableNodesSafe(const KisNodeList &nodes, KisImageSP image)
void filterMergeableNodes(KisNodeList &nodes, bool allowMasks)
void forceAllHiddenOriginalsUpdate(KisNodeSP root)
QSet< int > fetchLayerIdenticalRasterFrameTimes(KisNodeSP node, const int &frameTime)
bool checkIsChildOf(KisNodeSP node, const KisNodeList &parents)
void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea)
void mergeMultipleLayers(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, MergeFlags flags)
void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy *strategy, MergeFlags flags)
bool tryMergeSelectionMasks(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter)
std::future< KisNodeSP > convertToPaintLayer(KisImageSP image, KisNodeSP src)
QSet< int > fetchLayerRasterFrameTimesMatchingID(KisNodeSP node, const int frameID)
void forceAllDelayedNodesUpdate(KisNodeSP root)
QSharedPointer< MergeDownInfoBase > MergeDownInfoBaseSP
QSet< int > fetchLayerFramesRecursive(KisNodeSP rootNode)
void fetchSelectionMasks(KisNodeList mergedNodes, QVector< KisSelectionMaskSP > &selectionMasks)
KisNodeList sortMergeableInternalNodes(KisNodeList nodes)
QSharedPointer< MergeMultipleInfo > MergeMultipleInfoSP
void mergeMultipleNodes(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, MergeFlags flags)
auto then(std::future< T > &&future, Function &&func) -> std::future< decltype(func(std::move(future)))>
std::future< void > make_ready_future()
QMap< QString, KisKeyframeChannel * > keyframeChannels
QUuid uuid() const
virtual KisPaintDeviceSP projection() const =0
const QString & compositeOpId() const
bool isEditable(bool checkVisibility=true) const
bool isPinnedToTimeline() const
KisKeyframeChannel * getKeyframeChannel(const QString &id, bool create)
virtual QRect exactBounds() const
virtual KisPaintDeviceSP original() const =0
void setName(const QString &name)
KisImageWSP image
virtual QRect extent() const
bool userLocked() const
bool isAnimated() const
virtual KisPaintDeviceSP paintDevice() const =0
virtual PropertyList sectionModelProperties() const
QString name() const
void setCompositeOpId(const QString &compositeOpId)
virtual bool isFakeNode() const
quint8 opacity() const
bool check(const KoProperties &properties) const
virtual bool visible(bool recursive=false) const
virtual bool supportsKeyframeChannel(const QString &id)
KisLayerSP copyFrom
KisLayerSP reincarnateAsPaintLayer() const
The AggregateCommand struct is a command with delayed initialization. On first redo() populateChildCo...
void addCommand(KUndo2Command *cmd)
ActivateSelectionMask(MergeDownInfoBaseSP info)
MergeDownInfoBaseSP m_mergeInfo
AddNewFrame(KisNodeSP node, int frame, KisNodeSP source)
AddNewFrame(KisNodeSP node, int frame)
AddNewFrame(KisNodeSP node, int frame, KisNodeList sampleNodes)
void applyKeyframeColorLabel(KisKeyframeSP dstKeyframe, KisNodeList srcNodes)
AddNewFrame(MergeDownInfoBaseSP info, int frame)
static void findPerfectParent(KisNodeList nodesToDelete, KisNodeSP &putAfter, KisNodeSP &parent)
CleanUpNodes(MergeDownInfoBaseSP info, KisNodeSP putAfter, bool relinkClones=false)
void addCommandImpl(KUndo2Command *cmd) override
ConvertToPaintLayerInfo(KisImageSP image, KisNodeSP node)
SwitchFrameCommand::SharedStorageSP storage
CreateMergedLayerMultiple(MergeMultipleInfoSP info, const QString name=QString())
CreateMergedLayer(MergeDownInfoSP info)
DisableColorizeKeyStrokes(MergeDownInfoBaseSP info)
DisableExtraCompositing(MergeMultipleInfoSP info)
DisableOnionSkins(MergeDownInfoBaseSP info)
DisablePassThroughForHeadsOnly(MergeDownInfoBaseSP info, bool skipIfDstIsGroup=false)
EphemeralCommandsWrapper(MergeMultipleInfoSP info, QVector< KUndo2Command * > commands, bool cleanupNodes)
FillSelectionMasks(MergeDownInfoBaseSP info)
InitSplitAlphaSelectionMask(SplitAlphaToMaskInfoSP info)
void populateChildCommands() override
InsertNode(MergeDownInfoBaseSP info, KisNodeSP putAfter)
virtual void addCommandImpl(KUndo2Command *cmd)
KeepMergedNodesSelected(MergeDownInfoSP info, bool finalizing)
KeepMergedNodesSelected(MergeMultipleInfoSP info, KisNodeSP putAfter, bool finalizing)
SwitchFrameCommand::SharedStorageSP storage
QVector< KisSelectionMaskSP > selectionMasks
virtual KisNodeList allSrcNodes()=0
MergeDownInfo(KisImageSP _image, KisLayerSP _prevLayer, KisLayerSP _currLayer, MergeFlags flags)
KisNodeList allSrcNodes() override
MergeLayersMultiple(MergeMultipleInfoSP info)
MergeLayers(MergeDownInfoSP info, bool skipMergingSourceLayer)
void populateChildCommands() override
const KisMetaData::MergeStrategy * m_strategy
MergeMetaData(MergeDownInfoSP info, const KisMetaData::MergeStrategy *strategy)
MergeMultipleInfo(KisImageSP _image, KisNodeList _mergedNodes, MergeFlags flags)
QScopedPointer< KisSurrogateUndoStore > ephemeralCommandsStore
MergeSelectionMasks(MergeDownInfoBaseSP info, KisNodeSP putAfter)
RefreshDelayedUpdateLayers(MergeDownInfoBaseSP info)
RefreshHiddenAreas(KisImageSP image, KisNodeSP node)
RefreshHiddenAreas(MergeDownInfoBaseSP info, refresh_entire_image_t)
RefreshHiddenAreas(MergeDownInfoBaseSP info)
static constexpr refresh_entire_image_t refresh_entire_image
virtual void addCommandImpl(KUndo2Command *cmd)
SimpleAddNode(KisImageSP image, KisNodeSP toAdd, KisNodeSP parent=0, KisNodeSP putAfter=0)
SimpleRemoveLayers(const KisNodeList &nodes, KisImageSP image)
void addCommandImpl(KUndo2Command *cmd) override
SplitAlphaCommand(SplitAlphaToMaskInfoSP info)
SwitchFrameCommand::SharedStorageSP storage
SplitAlphaToMaskInfo(KisImageSP _image, KisNodeSP _node, const QString &maskName)
The SwitchFrameCommand struct Switches to frame with undo/redo support.
UndoEphemeralCommands(MergeMultipleInfoSP info)
UploadProjectionToFrameCommand(KisNodeSP src, KisNodeSP target, int frame)
void disableAlphaChannel(bool disable)
Definition kis_layer.cc:319
QBitArray channelFlags
Definition kis_layer.cc:167
virtual KisSelectionMaskSP selectionMask() const
Definition kis_layer.cc: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
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