1 /*
2 * Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "kis_base_node.h"
20 #include <klocalizedstring.h>
21
22 #include <kis_image.h>
23 #include <kis_icon.h>
24 #include <KoProperties.h>
25 #include <KoColorSpace.h>
26 #include <KoCompositeOpRegistry.h>
27 #include "kis_paint_device.h"
28 #include "kis_layer_properties_icons.h"
29
30 #include "kis_scalar_keyframe_channel.h"
31
32 struct Q_DECL_HIDDEN KisBaseNode::Private
33 {
34 QString compositeOp;
35 KoProperties properties;
36 KisBaseNode::Property hack_visible; //HACK
37 QUuid id;
38 QMap<QString, KisKeyframeChannel*> keyframeChannels;
39 QScopedPointer<KisScalarKeyframeChannel> opacityChannel;
40
41 bool systemLocked {false};
42 bool collapsed {false};
43 bool supportsLodMoves {false};
44 bool animated {false};
45 bool useInTimeline {true};
46
47 KisImageWSP image;
48
PrivateKisBaseNode::Private49 Private(KisImageWSP image)
50 : id(QUuid::createUuid())
51 , image(image)
52 {
53 }
54
PrivateKisBaseNode::Private55 Private(const Private &rhs)
56 : compositeOp(rhs.compositeOp),
57 id(QUuid::createUuid()),
58 collapsed(rhs.collapsed),
59 supportsLodMoves(rhs.supportsLodMoves),
60 animated(rhs.animated),
61 useInTimeline(rhs.useInTimeline),
62 image(rhs.image)
63 {
64 QMapIterator<QString, QVariant> iter = rhs.properties.propertyIterator();
65 while (iter.hasNext()) {
66 iter.next();
67 properties.setProperty(iter.key(), iter.value());
68 }
69 }
70 };
71
KisBaseNode(KisImageWSP image)72 KisBaseNode::KisBaseNode(KisImageWSP image)
73 : m_d(new Private(image))
74 {
75 /**
76 * Be cautious! These two calls are vital to warm-up KoProperties.
77 * We use it and its QMap in a threaded environment. This is not
78 * officially supported by Qt, but our environment guarantees, that
79 * there will be the only writer and several readers. Whilst the
80 * value of the QMap is boolean and there are no implicit-sharing
81 * calls provocated, it is safe to work with it in such an
82 * environment.
83 */
84 setVisible(true, true);
85 setUserLocked(false);
86 setCollapsed(false);
87 setSupportsLodMoves(true);
88
89 m_d->compositeOp = COMPOSITE_OVER;
90 }
91
92
KisBaseNode(const KisBaseNode & rhs)93 KisBaseNode::KisBaseNode(const KisBaseNode & rhs)
94 : QObject()
95 , KisShared()
96 , m_d(new Private(*rhs.m_d))
97 {
98 if (rhs.m_d->keyframeChannels.size() > 0) {
99 Q_FOREACH(QString key, rhs.m_d->keyframeChannels.keys()) {
100 KisKeyframeChannel* channel = rhs.m_d->keyframeChannels.value(key);
101 if (!channel) {
102 continue;
103 }
104
105 if (channel->inherits("KisScalarKeyframeChannel")) {
106 KisScalarKeyframeChannel* pchannel = qobject_cast<KisScalarKeyframeChannel*>(channel);
107 KIS_ASSERT_RECOVER(pchannel) { continue; }
108
109 KisScalarKeyframeChannel* channelNew = new KisScalarKeyframeChannel(*pchannel, nullptr);
110 KIS_ASSERT(channelNew);
111 m_d->keyframeChannels.insert(channelNew->id(), channelNew);
112
113 if (KoID(key) == KisKeyframeChannel::Opacity) {
114 m_d->opacityChannel.reset(channelNew);
115 }
116 }
117
118 }
119 }
120 }
121
~KisBaseNode()122 KisBaseNode::~KisBaseNode()
123 {
124 delete m_d;
125 }
126
colorPickSourceDevice() const127 KisPaintDeviceSP KisBaseNode::colorPickSourceDevice() const
128 {
129 return projection();
130 }
131
opacity() const132 quint8 KisBaseNode::opacity() const
133 {
134 if (m_d->opacityChannel) {
135 qreal value = m_d->opacityChannel->currentValue();
136
137 if (!qIsNaN(value)) {
138 return value;
139 }
140 }
141
142 return nodeProperties().intProperty("opacity", OPACITY_OPAQUE_U8);
143 }
144
setOpacity(quint8 val)145 void KisBaseNode::setOpacity(quint8 val)
146 {
147 if (m_d->opacityChannel) {
148 KisKeyframeSP activeKeyframe = m_d->opacityChannel->currentlyActiveKeyframe();
149
150 if (activeKeyframe) {
151 m_d->opacityChannel->setScalarValue(activeKeyframe, val);
152 }
153 }
154
155 if (opacity() == val) return;
156
157 setNodeProperty("opacity", val);
158
159 baseNodeInvalidateAllFramesCallback();
160 }
161
percentOpacity() const162 quint8 KisBaseNode::percentOpacity() const
163 {
164 return int(float(opacity() * 100) / 255 + 0.5);
165 }
166
setPercentOpacity(quint8 val)167 void KisBaseNode::setPercentOpacity(quint8 val)
168 {
169 setOpacity(int(float(val * 255) / 100 + 0.5));
170 }
171
compositeOpId() const172 const QString& KisBaseNode::compositeOpId() const
173 {
174 return m_d->compositeOp;
175 }
176
setCompositeOpId(const QString & compositeOp)177 void KisBaseNode::setCompositeOpId(const QString& compositeOp)
178 {
179 if (m_d->compositeOp == compositeOp) return;
180
181 m_d->compositeOp = compositeOp;
182 baseNodeChangedCallback();
183 baseNodeInvalidateAllFramesCallback();
184 }
185
sectionModelProperties() const186 KisBaseNode::PropertyList KisBaseNode::sectionModelProperties() const
187 {
188 KisBaseNode::PropertyList l;
189 l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::visible, visible(), m_d->hack_visible.isInStasis, m_d->hack_visible.stateInStasis);
190 l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::locked, userLocked());
191 return l;
192 }
193
setSectionModelProperties(const KisBaseNode::PropertyList & properties)194 void KisBaseNode::setSectionModelProperties(const KisBaseNode::PropertyList &properties)
195 {
196 setVisible(properties.at(0).state.toBool());
197 m_d->hack_visible = properties.at(0);
198 setUserLocked(properties.at(1).state.toBool());
199 }
200
nodeProperties() const201 const KoProperties & KisBaseNode::nodeProperties() const
202 {
203 return m_d->properties;
204 }
205
setNodeProperty(const QString & name,const QVariant & value)206 void KisBaseNode::setNodeProperty(const QString & name, const QVariant & value)
207 {
208 m_d->properties.setProperty(name, value);
209 baseNodeChangedCallback();
210 }
211
mergeNodeProperties(const KoProperties & properties)212 void KisBaseNode::mergeNodeProperties(const KoProperties & properties)
213 {
214 QMapIterator<QString, QVariant> iter = properties.propertyIterator();
215 while (iter.hasNext()) {
216 iter.next();
217 m_d->properties.setProperty(iter.key(), iter.value());
218 }
219 baseNodeChangedCallback();
220 baseNodeInvalidateAllFramesCallback();
221 }
222
check(const KoProperties & properties) const223 bool KisBaseNode::check(const KoProperties & properties) const
224 {
225 QMapIterator<QString, QVariant> iter = properties.propertyIterator();
226 while (iter.hasNext()) {
227 iter.next();
228 if (m_d->properties.contains(iter.key())) {
229 if (m_d->properties.value(iter.key()) != iter.value())
230 return false;
231 }
232 }
233 return true;
234 }
235
236
createThumbnail(qint32 w,qint32 h,Qt::AspectRatioMode aspectRatioMode)237 QImage KisBaseNode::createThumbnail(qint32 w, qint32 h, Qt::AspectRatioMode aspectRatioMode)
238 {
239 Q_UNUSED(aspectRatioMode);
240
241 try {
242 QImage image(w, h, QImage::Format_ARGB32);
243 image.fill(0);
244 return image;
245 } catch (const std::bad_alloc&) {
246 return QImage();
247 }
248
249 }
250
createThumbnailForFrame(qint32 w,qint32 h,int time,Qt::AspectRatioMode aspectRatioMode)251 QImage KisBaseNode::createThumbnailForFrame(qint32 w, qint32 h, int time, Qt::AspectRatioMode aspectRatioMode)
252 {
253 Q_UNUSED(time)
254 Q_UNUSED(aspectRatioMode);
255 return createThumbnail(w, h);
256 }
257
visible(bool recursive) const258 bool KisBaseNode::visible(bool recursive) const
259 {
260 bool isVisible = m_d->properties.boolProperty(KisLayerPropertiesIcons::visible.id(), true);
261 KisBaseNodeSP parentNode = parentCallback();
262
263 return recursive && isVisible && parentNode ?
264 parentNode->visible(recursive) : isVisible;
265 }
266
setVisible(bool visible,bool loading)267 void KisBaseNode::setVisible(bool visible, bool loading)
268 {
269 const bool isVisible = m_d->properties.boolProperty(KisLayerPropertiesIcons::visible.id(), true);
270 if (!loading && isVisible == visible) return;
271
272 m_d->properties.setProperty(KisLayerPropertiesIcons::visible.id(), visible);
273 notifyParentVisibilityChanged(visible);
274
275 if (!loading) {
276 baseNodeChangedCallback();
277 baseNodeInvalidateAllFramesCallback();
278 }
279 }
280
userLocked() const281 bool KisBaseNode::userLocked() const
282 {
283 return m_d->properties.boolProperty(KisLayerPropertiesIcons::locked.id(), false);
284 }
285
belongsToIsolatedGroup() const286 bool KisBaseNode::belongsToIsolatedGroup() const
287 {
288 if (!m_d->image) {
289 return false;
290 }
291
292 const KisBaseNode* element = this;
293
294 while (element) {
295 if (element->isIsolatedRoot()) {
296 return true;
297 } else {
298 element = element->parentCallback().data();
299 }
300 }
301
302 return false;
303 }
304
isIsolatedRoot() const305 bool KisBaseNode::isIsolatedRoot() const
306 {
307 if (!m_d->image) {
308 return false;
309 }
310
311 const KisBaseNode* isolatedRoot = m_d->image->isolatedModeRoot().data();
312
313 return (this == isolatedRoot);
314 }
315
setUserLocked(bool locked)316 void KisBaseNode::setUserLocked(bool locked)
317 {
318 const bool isLocked = m_d->properties.boolProperty(KisLayerPropertiesIcons::locked.id(), true);
319 if (isLocked == locked) return;
320
321 m_d->properties.setProperty(KisLayerPropertiesIcons::locked.id(), locked);
322 baseNodeChangedCallback();
323 }
324
isEditable(bool checkVisibility) const325 bool KisBaseNode::isEditable(bool checkVisibility) const
326 {
327 bool editable = true;
328 if (checkVisibility) {
329 editable = ((visible(false) || belongsToIsolatedGroup()) && !userLocked());
330 }
331 else {
332 editable = (!userLocked());
333 }
334
335 if (editable) {
336 KisBaseNodeSP parentNode = parentCallback();
337 if (parentNode && parentNode != this) {
338 editable = parentNode->isEditable(checkVisibility);
339 }
340 }
341 return editable;
342 }
343
hasEditablePaintDevice() const344 bool KisBaseNode::hasEditablePaintDevice() const
345 {
346 return paintDevice() && isEditable();
347 }
348
setCollapsed(bool collapsed)349 void KisBaseNode::setCollapsed(bool collapsed)
350 {
351 const bool oldCollapsed = m_d->collapsed;
352
353 m_d->collapsed = collapsed;
354
355 if (oldCollapsed != collapsed) {
356 baseNodeCollapsedChangedCallback();
357 }
358 }
359
collapsed() const360 bool KisBaseNode::collapsed() const
361 {
362 return m_d->collapsed;
363 }
364
setColorLabelIndex(int index)365 void KisBaseNode::setColorLabelIndex(int index)
366 {
367 const int currentLabel = colorLabelIndex();
368
369 if (currentLabel == index) return;
370
371 m_d->properties.setProperty(KisLayerPropertiesIcons::colorLabelIndex.id(), index);
372 baseNodeChangedCallback();
373 }
374
colorLabelIndex() const375 int KisBaseNode::colorLabelIndex() const
376 {
377 return m_d->properties.intProperty(KisLayerPropertiesIcons::colorLabelIndex.id(), 0);
378 }
379
uuid() const380 QUuid KisBaseNode::uuid() const
381 {
382 return m_d->id;
383 }
384
setUuid(const QUuid & id)385 void KisBaseNode::setUuid(const QUuid& id)
386 {
387 m_d->id = id;
388 baseNodeChangedCallback();
389 }
390
supportsLodMoves() const391 bool KisBaseNode::supportsLodMoves() const
392 {
393 return m_d->supportsLodMoves;
394 }
395
supportsLodPainting() const396 bool KisBaseNode::supportsLodPainting() const
397 {
398 return true;
399 }
400
setImage(KisImageWSP image)401 void KisBaseNode::setImage(KisImageWSP image)
402 {
403 m_d->image = image;
404 }
405
image() const406 KisImageWSP KisBaseNode::image() const
407 {
408 return m_d->image;
409 }
410
isFakeNode() const411 bool KisBaseNode::isFakeNode() const
412 {
413 return false;
414 }
415
setSupportsLodMoves(bool value)416 void KisBaseNode::setSupportsLodMoves(bool value)
417 {
418 m_d->supportsLodMoves = value;
419 }
420
421
keyframeChannels() const422 QMap<QString, KisKeyframeChannel*> KisBaseNode::keyframeChannels() const
423 {
424 return m_d->keyframeChannels;
425 }
426
getKeyframeChannel(const QString & id) const427 KisKeyframeChannel * KisBaseNode::getKeyframeChannel(const QString &id) const
428 {
429 QMap<QString, KisKeyframeChannel*>::const_iterator i = m_d->keyframeChannels.constFind(id);
430 if (i == m_d->keyframeChannels.constEnd()) {
431 return 0;
432 }
433 return i.value();
434 }
435
getKeyframeChannel(const QString & id,bool create)436 KisKeyframeChannel * KisBaseNode::getKeyframeChannel(const QString &id, bool create)
437 {
438 KisKeyframeChannel *channel = getKeyframeChannel(id);
439
440 if (!channel && create) {
441 channel = requestKeyframeChannel(id);
442
443 if (channel) {
444 addKeyframeChannel(channel);
445 }
446 }
447
448 return channel;
449 }
450
isAnimated() const451 bool KisBaseNode::isAnimated() const
452 {
453 return m_d->animated;
454 }
455
enableAnimation()456 void KisBaseNode::enableAnimation()
457 {
458 m_d->animated = true;
459 baseNodeChangedCallback();
460 }
461
useInTimeline() const462 bool KisBaseNode::useInTimeline() const
463 {
464 return m_d->useInTimeline;
465 }
466
setUseInTimeline(bool value)467 void KisBaseNode::setUseInTimeline(bool value)
468 {
469 if (value == m_d->useInTimeline) return;
470
471 m_d->useInTimeline = value;
472 baseNodeChangedCallback();
473 }
474
addKeyframeChannel(KisKeyframeChannel * channel)475 void KisBaseNode::addKeyframeChannel(KisKeyframeChannel *channel)
476 {
477 m_d->keyframeChannels.insert(channel->id(), channel);
478 emit keyframeChannelAdded(channel);
479 }
480
requestKeyframeChannel(const QString & id)481 KisKeyframeChannel *KisBaseNode::requestKeyframeChannel(const QString &id)
482 {
483 if (id == KisKeyframeChannel::Opacity.id()) {
484 Q_ASSERT(m_d->opacityChannel.isNull());
485
486 KisPaintDeviceSP device = original();
487
488 if (device) {
489 KisNode* node = dynamic_cast<KisNode*>(this);
490 KisScalarKeyframeChannel * channel = new KisScalarKeyframeChannel(
491 KisKeyframeChannel::Opacity,
492 0, 255,
493 KisNodeWSP( node ),
494 KisKeyframe::Linear
495 );
496
497 m_d->opacityChannel.reset(channel);
498
499 return channel;
500 }
501 }
502
503 return 0;
504 }
505