Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_base_node.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include "kis_base_node.h"
8#include <klocalizedstring.h>
9
10#include <kis_image.h>
11#include <kis_icon.h>
12#include <KoProperties.h>
14#include <KoColorSpace.h>
16
17#include "kis_pointer_utils.h"
18
19#include "kis_paint_device.h"
22
24
25struct Q_DECL_HIDDEN KisBaseNode::Private
26{
27 QString compositeOp;
30 QUuid id;
31 QMap<QString, KisKeyframeChannel*> keyframeChannels;
33
34 bool collapsed {false};
35 bool supportsLodMoves {false};
36 bool animated {false};
37 bool pinnedToTimeline {false};
39
41 : id(QUuid::createUuid())
42 , opacityProperty(new KisDefaultBounds(p_image), &properties, OPACITY_OPAQUE_U8)
43 , image(p_image)
44 {
45 }
46
47 Private(const Private &rhs)
48 : compositeOp(rhs.compositeOp),
49 id(QUuid::createUuid()),
50 opacityProperty(new KisDefaultBounds(rhs.image), &properties, OPACITY_OPAQUE_U8),
51 collapsed(rhs.collapsed),
52 supportsLodMoves(rhs.supportsLodMoves),
53 animated(rhs.animated),
54 pinnedToTimeline(rhs.pinnedToTimeline),
55 image(rhs.image)
56 {
57 QMapIterator<QString, QVariant> iter = rhs.properties.propertyIterator();
58 while (iter.hasNext()) {
59 iter.next();
60 properties.setProperty(iter.key(), iter.value());
61 }
62 }
63};
64
66 : m_d(new Private(image))
67{
77 setVisible(true, true);
78 setUserLocked(false);
79 setCollapsed(false);
81
82 m_d->compositeOp = COMPOSITE_OVER;
83
84 connect(&m_d->opacityProperty, SIGNAL(changed(quint8)), this, SIGNAL(opacityChanged(quint8)));
85}
86
87
89 : QObject()
90 , KisShared()
91 , m_d(new Private(*rhs.m_d))
92{
93 if (rhs.m_d->opacityProperty.hasChannel()) {
94 m_d->opacityProperty.transferKeyframeData(rhs.m_d->opacityProperty);
95 m_d->keyframeChannels.insert(m_d->opacityProperty.channel()->id(), m_d->opacityProperty.channel());
96 }
97
98 connect(&m_d->opacityProperty, SIGNAL(changed(quint8)), this, SIGNAL(opacityChanged(quint8)));
99}
100
102{
103 delete m_d;
104}
105
110
112{
113 return m_d->opacityProperty.get();
114}
115
117{
118 m_d->opacityProperty.set(val);
120}
121
123{
124 return int(float(opacity() * 100) / 255 + 0.5);
125}
126
128{
129 setOpacity(int(float(val * 255) / 100 + 0.5));
130}
131
132const QString& KisBaseNode::compositeOpId() const
133{
134 return m_d->compositeOp;
135}
136
137void KisBaseNode::setCompositeOpId(const QString& compositeOp)
138{
139 if (m_d->compositeOp == compositeOp) return;
140
141 m_d->compositeOp = compositeOp;
144}
145
153
155{
156 setVisible(properties.at(0).state.toBool());
157 m_d->hack_visible = properties.at(0);
158 setUserLocked(properties.at(1).state.toBool());
159}
160
162{
163 return m_d->properties;
164}
165
166void KisBaseNode::setNodeProperty(const QString & name, const QVariant & value)
167{
168 m_d->properties.setProperty(name, value);
170}
171
173{
174 QMapIterator<QString, QVariant> iter = properties.propertyIterator();
175 while (iter.hasNext()) {
176 iter.next();
177 m_d->properties.setProperty(iter.key(), iter.value());
178 }
181}
182
183bool KisBaseNode::check(const KoProperties & properties) const
184{
185 QMapIterator<QString, QVariant> iter = properties.propertyIterator();
186 while (iter.hasNext()) {
187 iter.next();
188 if (m_d->properties.contains(iter.key())) {
189 if (m_d->properties.value(iter.key()) != iter.value())
190 return false;
191 }
192 }
193 return true;
194}
195
196
197QImage KisBaseNode::createThumbnail(qint32 w, qint32 h, Qt::AspectRatioMode aspectRatioMode)
198{
199 Q_UNUSED(aspectRatioMode);
200
201 try {
202 QImage image(w, h, QImage::Format_ARGB32);
203 image.fill(0);
204 return image;
205 } catch (const std::bad_alloc&) {
206 return QImage();
207 }
208
209}
210
212{
213 return -1;
214}
215
216QImage KisBaseNode::createThumbnailForFrame(qint32 w, qint32 h, int time, Qt::AspectRatioMode aspectRatioMode)
217{
218 Q_UNUSED(time);
219 Q_UNUSED(aspectRatioMode);
220 return createThumbnail(w, h);
221}
222
223bool KisBaseNode::visible(bool recursive) const
224{
225 bool isVisible = m_d->properties.boolProperty(KisLayerPropertiesIcons::visible.id(), true);
226 KisBaseNodeSP parentNode = parentCallback();
227
228 return recursive && isVisible && parentNode ?
229 parentNode->visible(recursive) : isVisible;
230}
231
232void KisBaseNode::setVisible(bool visible, bool loading)
233{
234 const bool isVisible = m_d->properties.boolProperty(KisLayerPropertiesIcons::visible.id(), true);
235 if (!loading && isVisible == visible) return;
236
237 m_d->properties.setProperty(KisLayerPropertiesIcons::visible.id(), visible);
239
240 if (!loading) {
243 }
244}
245
247{
248 return m_d->properties.boolProperty(KisLayerPropertiesIcons::locked.id(), false);
249}
250
252{
253 if (!m_d->image) {
254 return false;
255 }
256
257 const KisBaseNode* element = this;
258
259 while (element) {
260 if (element->isIsolatedRoot()) {
261 return true;
262 } else {
263 element = element->parentCallback().data();
264 }
265 }
266
267 return false;
268}
269
271{
272 if (!m_d->image) {
273 return false;
274 }
275
276 const KisBaseNode* isolatedRoot = m_d->image->isolationRootNode().data();
277
278 return (this == isolatedRoot);
279}
280
282{
283 const bool isLocked = m_d->properties.boolProperty(KisLayerPropertiesIcons::locked.id(), true);
284 if (isLocked == locked) return;
285
286 m_d->properties.setProperty(KisLayerPropertiesIcons::locked.id(), locked);
288}
289
290bool KisBaseNode::isEditable(bool checkVisibility) const
291{
292 bool editable = true;
293 if (checkVisibility) {
294 editable = ((visible(false) || belongsToIsolatedGroup()) && !userLocked());
295 }
296 else {
297 editable = (!userLocked());
298 }
299
300 if (editable) {
301 KisBaseNodeSP parentNode = parentCallback();
302 if (parentNode && parentNode != this) {
303 editable = parentNode->isEditable(checkVisibility);
304 }
305 }
306 return editable;
307}
308
310{
311 return paintDevice() && isEditable();
312}
313
314void KisBaseNode::setCollapsed(bool collapsed)
315{
316 const bool oldCollapsed = m_d->collapsed;
317
318 m_d->collapsed = collapsed;
319
320 if (oldCollapsed != collapsed) {
322 }
323}
324
325bool KisBaseNode::collapsed() const
326{
327 return m_d->collapsed;
328}
329
331{
332 const int currentLabel = colorLabelIndex();
333
334 if (currentLabel == index) return;
335
336 m_d->properties.setProperty(KisLayerPropertiesIcons::colorLabelIndex.id(), index);
338}
339
341{
342 return m_d->properties.intProperty(KisLayerPropertiesIcons::colorLabelIndex.id(), 0);
343}
344
345QUuid KisBaseNode::uuid() const
346{
347 return m_d->id;
348}
349
350void KisBaseNode::setUuid(const QUuid& id)
351{
352 m_d->id = id;
354}
355
357{
358 return m_d->supportsLodMoves;
359}
360
362{
363 return true;
364}
365
367{
368 m_d->image = image;
369 m_d->opacityProperty.updateDefaultBounds(new KisDefaultBounds(image));
370}
371
373{
374 return m_d->image;
375}
376
378{
379 return false;
380}
381
383{
384 m_d->supportsLodMoves = value;
385}
386
387
388QMap<QString, KisKeyframeChannel*> KisBaseNode::keyframeChannels() const
389{
390 return m_d->keyframeChannels;
391}
392
394{
395 QMap<QString, KisKeyframeChannel*>::const_iterator i = m_d->keyframeChannels.constFind(id);
396 if (i == m_d->keyframeChannels.constEnd()) {
397 return 0;
398 }
399 return i.value();
400}
401
403{
404 return m_d->pinnedToTimeline;
405}
406
408{
409 if (pinned == m_d->pinnedToTimeline) return;
410
411 m_d->pinnedToTimeline = pinned;
413}
414
416{
418
419 if (!channel && create) {
420 channel = requestKeyframeChannel(id);
421
422 if (channel) {
423 addKeyframeChannel(channel);
424 }
425 }
426
427 return channel;
428}
429
431{
432 return m_d->animated;
433}
434
436{
437 m_d->animated = true;
439}
440
442{
443 m_d->keyframeChannels.insert(channel->id(), channel);
444 Q_EMIT keyframeChannelAdded(channel);
445}
446
448{
449 if (id == KisKeyframeChannel::Opacity.id()) {
450 Q_ASSERT(!m_d->opacityProperty.hasChannel());
451
452 KisPaintDeviceSP device = original();
453 KisNode* node = dynamic_cast<KisNode*>(this);
454
455 if (device && node) {
456 m_d->opacityProperty.makeAnimated(node);
457 return m_d->opacityProperty.channel();
458 }
459 }
460
461 return 0;
462}
463
465{
466 if (id == KisKeyframeChannel::Opacity.id() && original()) {
467 return true;
468 }
469
470 return false;
471}
472
473QDebug operator<<(QDebug dbg, const KisBaseNode::Property &prop)
474{
475 dbg.nospace() << "Property(" << prop.id << ", " << prop.state;
476
477 if (prop.isInStasis) {
478 dbg.nospace() << ", in-stasis";
479 }
480
481 dbg.nospace() << ")";
482
483 return dbg.space();
484}
float value(const T *src, size_t ch)
const quint8 OPACITY_OPAQUE_U8
const QString COMPOSITE_OVER
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
KisKeyframeChannel stores and manages KisKeyframes. Maps units of time to virtual keyframe values....
static const KoID Opacity
static KisBaseNode::Property getProperty(const KoID &id, bool state)
QMapIterator< QString, QVariant > propertyIterator() const
void setProperty(const QString &name, const QVariant &value)
QDebug operator<<(QDebug dbg, const KisBaseNode::Property &prop)
void setPinnedToTimeline(bool pinned)
void setNodeProperty(const QString &name, const QVariant &value)
void setSupportsLodMoves(bool value)
virtual bool supportsLodPainting() const
QMap< QString, KisKeyframeChannel * > keyframeChannels
quint8 percentOpacity() const
virtual KisPaintDeviceSP colorSampleSourceDevice() const
virtual void setUserLocked(bool l)
bool isIsolatedRoot() const
QUuid uuid() const
virtual void baseNodeInvalidateAllFramesCallback()
virtual KisPaintDeviceSP projection() const =0
const QString & compositeOpId() const
virtual void baseNodeCollapsedChangedCallback()
bool isEditable(bool checkVisibility=true) const
virtual void baseNodeChangedCallback()
KisBaseNode(KisImageWSP image)
void setOpacity(quint8 val)
virtual void setVisible(bool visible, bool loading=false)
bool isPinnedToTimeline() const
KisKeyframeChannel * getKeyframeChannel(const QString &id, bool create)
void mergeNodeProperties(const KoProperties &properties)
virtual void setImage(KisImageWSP image)
virtual QImage createThumbnail(qint32 w, qint32 h, Qt::AspectRatioMode aspectRatioMode=Qt::IgnoreAspectRatio)
virtual KisKeyframeChannel * requestKeyframeChannel(const QString &id)
bool belongsToIsolatedGroup() const
const KoProperties & nodeProperties() const
virtual KisPaintDeviceSP original() const =0
KisAnimatedOpacityProperty opacityProperty
virtual void notifyParentVisibilityChanged(bool value)
void setUuid(const QUuid &id)
KoProperties properties
void setColorLabelIndex(int index)
KisBaseNode::Property hack_visible
~KisBaseNode() override
Private(KisImageWSP p_image)
Private *const m_d
void setCollapsed(bool collapsed)
virtual int thumbnailSeqNo() const
void keyframeChannelAdded(KisKeyframeChannel *channel)
QString compositeOp
KisImageWSP image
int colorLabelIndex() const
void opacityChanged(quint8 value)
bool userLocked() const
bool isAnimated() const
void setPercentOpacity(quint8 val)
virtual KisPaintDeviceSP paintDevice() const =0
virtual PropertyList sectionModelProperties() const
QString name() const
void setCompositeOpId(const QString &compositeOpId)
Private(const Private &rhs)
virtual void addKeyframeChannel(KisKeyframeChannel *channel)
virtual bool isFakeNode() const
quint8 opacity() const
bool check(const KoProperties &properties) const
virtual bool visible(bool recursive=false) const
virtual KisBaseNodeSP parentCallback() const
virtual QImage createThumbnailForFrame(qint32 w, qint32 h, int time, Qt::AspectRatioMode aspectRatioMode=Qt::IgnoreAspectRatio)
bool hasEditablePaintDevice() const
void enableAnimation()
virtual bool supportsKeyframeChannel(const QString &id)
virtual void setSectionModelProperties(const PropertyList &properties)