Krita Source Code Documentation
Loading...
Searching...
No Matches
KisMergeLabeledLayersCommand.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2020 Agata Cacko <cacko.azh@gmail.com>
3 * SPDX-FileCopyrightText: 2022 Deif Lou <ginoba@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
9
11
12#include "kis_layer_utils.h"
13#include "kis_node.h"
14#include "kis_image.h"
15#include "kis_painter.h"
16#include "kis_layer.h"
17#include "kis_clone_layer.h"
18#include "kis_paint_layer.h"
19#include "kis_assert.h"
22
24 KisPaintDeviceSP newRefPaintDevice,
25 QList<int> selectedLabels,
26 GroupSelectionPolicy groupSelectionPolicy)
27 : KUndo2Command(kundo2_noi18n("MERGE_LABELED_LAYERS"))
28 , m_refImage(new KisImage(new KisSurrogateUndoStore(), image->width(), image->height(), image->colorSpace(), "Merge Labeled Layers Reference Image"))
29 , m_prevRefNodeInfoList(nullptr)
30 , m_newRefNodeInfoList(nullptr)
31 , m_prevRefPaintDevice(nullptr)
32 , m_newRefPaintDevice(newRefPaintDevice)
33 , m_currentRoot(image->root())
34 , m_selectedLabels(selectedLabels)
35 , m_groupSelectionPolicy(groupSelectionPolicy)
36 , m_forceRegeneration(true)
37 , m_activeNode(nullptr)
38{
39 KIS_ASSERT(newRefPaintDevice);
40 if (image->animationInterface()->hasAnimation()) {
43 }
44}
45
47 ReferenceNodeInfoListSP prevRefNodeInfoList,
48 ReferenceNodeInfoListSP newRefNodeInfoList,
49 KisPaintDeviceSP prevRefPaintDevice,
50 KisPaintDeviceSP newRefPaintDevice,
51 QList<int> selectedLabels,
52 GroupSelectionPolicy groupSelectionPolicy,
53 bool forceRegeneration,
54 KisNodeSP activeNode)
55 : KUndo2Command(kundo2_noi18n("MERGE_LABELED_LAYERS"))
56 , m_refImage(new KisImage(new KisSurrogateUndoStore(), image->width(), image->height(), image->colorSpace(), "Merge Labeled Layers Reference Image"))
57 , m_prevRefNodeInfoList(prevRefNodeInfoList)
58 , m_newRefNodeInfoList(newRefNodeInfoList)
59 , m_prevRefPaintDevice(prevRefPaintDevice)
60 , m_newRefPaintDevice(newRefPaintDevice)
61 , m_currentRoot(image->root())
62 , m_selectedLabels(selectedLabels)
63 , m_groupSelectionPolicy(groupSelectionPolicy)
64 , m_forceRegeneration(forceRegeneration)
65 , m_activeNode(activeNode)
66{
67 KIS_SAFE_ASSERT_RECOVER_NOOP(prevRefNodeInfoList);
68 KIS_SAFE_ASSERT_RECOVER_NOOP(newRefNodeInfoList);
69 KIS_SAFE_ASSERT_RECOVER_NOOP(prevRefPaintDevice);
70 KIS_ASSERT(newRefPaintDevice);
71 if (image->animationInterface()->hasAnimation()) {
74 }
75}
76
79
84
92
94{
95 return KisPaintDeviceSP(new KisPaintDevice(originalImage->colorSpace(), name));
96}
97
98QPair<KisNodeSP, QPair<bool, bool>> KisMergeLabeledLayersCommand::collectNode(KisNodeSP node) const
99{
100 if (!node->parent()) {
101 // This is the root node. Do not use nor visit the siblings,
102 // but always visit the children
103 return {nullptr, {false, true}};
104 }
105
106 if (node->inherits("KisMask")) {
107 // This is the a mask node. Do not use, nor visit the children,
108 // but visit the siblings as they may be normal layers
109 return {nullptr, {true, false}};
110 }
111
112 if (!node->visible()) {
113 // Do not use the node and do not visit the children if the node is
114 // hidden, but do visit the next sibling
115 return {nullptr, {true, false}};
116 }
117
118 if (!m_selectedLabels.contains(node->colorLabelIndex())) {
119 // Do not use this node if it is not labeled appropriately. The children
120 // should still be visited if it is a group. The next sibling should
121 // also be visited
122 if (!(m_activeNode != nullptr && node->uuid() == m_activeNode->uuid())) {
123 // If the active node has been passed and it is being considered,
124 // it should be treated as if it is labeled to match.
125 return {nullptr, {true, node->inherits("KisGroupLayer")}};
126 }
127 }
128
129 if (node->inherits("KisCloneLayer")) {
130 // Make a copy of the clone layer as a paint layer. The source layer
131 // may not be color labeled and therefore not added to the temporary
132 // image. So this ensures that the real contents are represented
133 // in any case
134 KisCloneLayerSP cloneLayer = dynamic_cast<KisCloneLayer*>(node.data());
135 KisNodeSP transformedNode = cloneLayer->reincarnateAsPaintLayer();
136 // Use the transformed node. The next sibling is visited, but not the
137 // children, which may be some masks
138 return {transformedNode, {true, false}};
139 }
140
141 if (node->inherits("KisAdjustmentLayer")) {
142 // Similar to the clone layer case. The filter layer uses the
143 // composition of the layers below as source to apply the effect
144 // so we must use a paint layer representation of the result
145 // because the next sibling nodes may not be color labeled and
146 // therefore not added to the temporary image
147 KisPaintDeviceSP proj = new KisPaintDevice(*(node->projection()));
148 KisPaintLayerSP transformedNode = new KisPaintLayer(node->image(), node->name(), node->opacity(), proj);
149 transformedNode->setX(transformedNode->x() + node->x());
150 transformedNode->setY(transformedNode->y() + node->y());
151 transformedNode->mergeNodeProperties(node->nodeProperties());
152 // Use the transformed node. This new node already has the contents of
153 // the next layers on the same level baked, so the next sibling nodes
154 // are not visited. Do not visit the children, which may be some masks
155 return {transformedNode, {false, false}};
156 }
157
158 if (node->inherits("KisGroupLayer")) {
161 // If this group node should not be used (that is, the projection of
162 // it's contents), it's children are always visited as well as
163 // the next sibling
164 return {nullptr, {true, true}};
165 } else {
166 // If this group node should be used (that is, the projection of
167 // it's contents), just add the node, which adds
168 // also all the children. This group node already has all the
169 // children, so the child nodes are not visited.
170 // The next sibling is visited
171 return {node, {true, false}};
172 }
173 }
174
175 // By default, visit the next sibling, but not the children
176 return {node, {true, false}};
177}
178
180{
181 QPair<KisNodeSP, QPair<bool, bool>> result = collectNode(node);
182 KisNodeSP collectedNode = result.first;
183 const bool visitNextSibling = result.second.first;
184 const bool visitChildren = result.second.second;
185
186 if (collectedNode) {
187 // If the node should be selected, it is added to the list
188 nodeList << collectedNode;
189 // Store additional info to check if the new list of reference nodes
190 // is different. Use the original node to extract the info
192 const QUuid uuid = node->uuid();
193 const int sequenceNumber = node->projection()->sequenceNumber();
194 const int opacity = node->opacity();
195 nodeInfoList.append({uuid, sequenceNumber, opacity});
196 }
197 }
198
199 if (visitChildren) {
200 node = node->lastChild();
201 while (node) {
202 const bool mustVisitNextSibling = collectNodes(node, nodeList, nodeInfoList);
203 if (!mustVisitNextSibling) {
204 break;
205 }
206 node = node->prevSibling();
207 }
208 }
209
210 return visitNextSibling;
211}
212
214{
215 QList<KisNodeSP> currentNodesList;
216 ReferenceNodeInfoList currentNodeInfoList;
217
218 collectNodes(m_currentRoot, currentNodesList, currentNodeInfoList);
219
221 *m_newRefNodeInfoList = currentNodeInfoList;
222 }
223
224 if (hasToCheckForChangesInNodes() && !m_forceRegeneration && (currentNodeInfoList == *m_prevRefNodeInfoList)) {
227 } else {
228 QList<KisNodeSP> currentNodesListCopy;
229 for (KisNodeSP node : currentNodesList) {
230 KisNodeSP copy = node->clone();
231
232 if (copy.isNull()) {
233 continue;
234 }
235
236 if (copy->inherits("KisLayer")) {
237 KisLayer* layerCopy = dynamic_cast<KisLayer*>(copy.data());
238 KIS_ASSERT(layerCopy);
239 layerCopy->setChannelFlags(QBitArray());
240 }
241
242 copy->setCompositeOpId(COMPOSITE_OVER);
243
244 bool success = m_refImage->addNode(copy, m_refImage->root(), 0);
245
246 if (!success) {
247 continue;
248 }
249
250 currentNodesListCopy << copy;
251 }
252
253 currentNodesListCopy = KisLayerUtils::sortAndFilterAnyMergeableNodesSafe(currentNodesListCopy, m_refImage);
254
258
259 if (m_refImage->root()->childCount() == 0) {
260 return;
261 }
262
266
268 }
269
270 // release resources: they are still owned by the caller
271 // (or by some other object the caller passed them to)
275 m_prevRefNodeInfoList.clear();
276 m_newRefNodeInfoList.clear();
277
278 // KisImage should be deleted only in the GUI thread (it has timers)
281}
282
KisDeleteLaterWrapper< T > * makeKisDeleteLaterWrapper(T value)
const QString COMPOSITE_OVER
virtual void undo()
virtual void redo()
void switchCurrentTimeAsync(int frameId, SwitchTimeAsyncFlags options=STAO_NONE)
void waitForDone()
const KoColorSpace * colorSpace() const
KisImageAnimationInterface * animationInterface() const
KisPaintDeviceSP projection() const
void initialRefreshGraph()
QRect bounds() const override
bool collectNodes(KisNodeSP node, QList< KisNodeSP > &nodeList, ReferenceNodeInfoList &nodeInfoList) const
static KisPaintDeviceSP createRefPaintDevice(KisImageSP originalImage, QString name="Merge Labeled Layers Reference Paint Device")
QPair< KisNodeSP, QPair< bool, bool > > collectNode(KisNodeSP node) const
KisMergeLabeledLayersCommand(KisImageSP image, KisPaintDeviceSP newRefPaintDevice, QList< int > selectedLabels, GroupSelectionPolicy groupSelectionPolicy=GroupSelectionPolicy_SelectAlways)
Construct a new KisMergeLabeledLayersCommand that does not use a cache.
GroupSelectionPolicy
Policies to stablish how the groups should be treated.
@ GroupSelectionPolicy_SelectIfColorLabeled
Groups will be taken into account only if they have set an explicit color label. This ignores groups ...
@ GroupSelectionPolicy_NeverSelect
Groups will not be taken into account.
void makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect)
int sequenceNumber() const
QRect exactBounds() const
QRect extent() const
void prepareClone(KisPaintDeviceSP src)
static void copyAreaOptimized(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &originalSrcRect)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
KisSharedPtr< KisPaintDevice > KisPaintDeviceSP
Definition kis_types.h:73
KUndo2MagicString kundo2_noi18n(const QString &text)
KisNodeList sortAndFilterAnyMergeableNodesSafe(const KisNodeList &nodes, KisImageSP image)
void refreshHiddenAreaAsync(KisImageSP image, KisNodeSP rootNode, const QRect &preparedArea)
void mergeMultipleNodes(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, MergeFlags flags)
QUuid uuid() const
virtual KisPaintDeviceSP projection() const =0
virtual qint32 y() const
void mergeNodeProperties(const KoProperties &properties)
const KoProperties & nodeProperties() const
KisImageWSP image
virtual qint32 x() const
int colorLabelIndex() const
QString name() const
quint8 opacity() const
virtual bool visible(bool recursive=false) const
qint32 y() const override
Definition kis_layer.cc:978
qint32 x() const override
Definition kis_layer.cc:973
void setX(qint32 x) override
Definition kis_layer.cc:983
virtual void setChannelFlags(const QBitArray &channelFlags)
Definition kis_layer.cc:342
void setY(qint32 y) override
Definition kis_layer.cc:989
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
KisNodeSP prevSibling() const
Definition kis_node.cpp:402
virtual KisNodeSP clone() const =0
quint32 childCount() const
Definition kis_node.cpp:414
KisNodeWSP parent
Definition kis_node.cpp:86
KisNodeSP lastChild() const
Definition kis_node.cpp:367