kis_base_node.cpp 12.2 KB
Newer Older
1
/*
2
 *  SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org>
3
 *
Samuel Gaist's avatar
Samuel Gaist committed
4
 *  SPDX-License-Identifier: GPL-2.0-or-later
5
 */
Halla Rempt's avatar
Halla Rempt committed
6

7
#include "kis_base_node.h"
8
#include <klocalizedstring.h>
Halla Rempt's avatar
Halla Rempt committed
9

10
#include <kis_image.h>
11
#include <kis_icon.h>
Halla Rempt's avatar
Halla Rempt committed
12
#include <KoProperties.h>
13
#include <KoColorSpace.h>
14
#include <KoCompositeOpRegistry.h>
15 16
#include "kis_paint_device.h"
#include "kis_layer_properties_icons.h"
17
#include "kis_default_bounds_node_wrapper.h"
18

19
#include "kis_scalar_keyframe_channel.h"
20

21
struct Q_DECL_HIDDEN KisBaseNode::Private
22
{
23
    QString compositeOp;
Halla Rempt's avatar
Halla Rempt committed
24
    KoProperties properties;
25
    KisBaseNode::Property hack_visible; //HACK
26
    QUuid id;
27 28 29
    QMap<QString, KisKeyframeChannel*> keyframeChannels;
    QScopedPointer<KisScalarKeyframeChannel> opacityChannel;

30 31 32 33
    bool collapsed {false};
    bool supportsLodMoves {false};
    bool animated {false};
    bool pinnedToTimeline {false};
34
    KisImageWSP image;
35

36
    Private(KisImageWSP image)
37
        : id(QUuid::createUuid())
38
        , image(image)
39 40
    {
    }
41 42 43 44 45 46 47

    Private(const Private &rhs)
        : compositeOp(rhs.compositeOp),
          id(QUuid::createUuid()),
          collapsed(rhs.collapsed),
          supportsLodMoves(rhs.supportsLodMoves),
          animated(rhs.animated),
48
          pinnedToTimeline(rhs.pinnedToTimeline),
49
          image(rhs.image)
50 51 52 53 54 55 56
    {
        QMapIterator<QString, QVariant> iter = rhs.properties.propertyIterator();
        while (iter.hasNext()) {
            iter.next();
            properties.setProperty(iter.key(), iter.value());
        }
    }
57 58
};

59 60
KisBaseNode::KisBaseNode(KisImageWSP image)
    : m_d(new Private(image))
61
{
62
    /**
63
     * Be cautious! These two calls are vital to warm-up KoProperties.
64
     * We use it and its QMap in a threaded environment. This is not
luz paz's avatar
luz paz committed
65
     * officially supported by Qt, but our environment guarantees, that
66 67 68 69 70
     * there will be the only writer and several readers. Whilst the
     * value of the QMap is boolean and there are no implicit-sharing
     * calls provocated, it is safe to work with it in such an
     * environment.
     */
71
    setVisible(true, true);
Cyrille Berger's avatar
Cyrille Berger committed
72
    setUserLocked(false);
73
    setCollapsed(false);
74
    setSupportsLodMoves(true);
75

76
    m_d->compositeOp = COMPOSITE_OVER;
77 78 79
}


Halla Rempt's avatar
Halla Rempt committed
80
KisBaseNode::KisBaseNode(const KisBaseNode & rhs)
Halla Rempt's avatar
Halla Rempt committed
81
    : QObject()
82
    , KisShared()
83
    , m_d(new Private(*rhs.m_d))
84
{
85 86 87 88 89 90 91 92 93 94 95
    if (rhs.m_d->keyframeChannels.size() > 0) {
        Q_FOREACH(QString key, rhs.m_d->keyframeChannels.keys()) {
            KisKeyframeChannel* channel = rhs.m_d->keyframeChannels.value(key);
            if (!channel) {
                continue;
            }

            if (channel->inherits("KisScalarKeyframeChannel")) {
                KisScalarKeyframeChannel* pchannel = qobject_cast<KisScalarKeyframeChannel*>(channel);
                KIS_ASSERT_RECOVER(pchannel) { continue; }

96
                KisScalarKeyframeChannel* channelNew = new KisScalarKeyframeChannel(*pchannel, nullptr);
97
                KIS_ASSERT(channelNew);
98
                addKeyframeChannel(channelNew);
99 100 101 102 103 104 105

                if (KoID(key) == KisKeyframeChannel::Opacity) {
                    m_d->opacityChannel.reset(channelNew);
                }
            }

        }
106
    }
107 108 109 110 111 112 113
}

KisBaseNode::~KisBaseNode()
{
    delete m_d;
}

114
KisPaintDeviceSP KisBaseNode::colorSampleSourceDevice() const
115 116 117 118
{
    return projection();
}

119 120
quint8 KisBaseNode::opacity() const
{
121
    if (m_d->opacityChannel) {
122
        qreal value = m_d->opacityChannel->currentValue();
123

124 125
        if (!qIsNaN(value)) {
            return value;
126 127 128
        }
    }

129
    return nodeProperties().intProperty("opacity", OPACITY_OPAQUE_U8);
130 131 132 133
}

void KisBaseNode::setOpacity(quint8 val)
{
134
    if (m_d->opacityChannel) {
135 136
        int activeKeyframeTime = m_d->opacityChannel->activeKeyframeTime();
        KisScalarKeyframeSP scalarKey = m_d->opacityChannel->keyframeAt<KisScalarKeyframe>(activeKeyframeTime);
137

138 139
        if (scalarKey) {
            scalarKey->setValue(val);
140 141 142
        }
    }

143 144
    if (opacity() == val) return;

145
    setNodeProperty("opacity", val);
146 147

    baseNodeInvalidateAllFramesCallback();
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
}

quint8 KisBaseNode::percentOpacity() const
{
    return int(float(opacity() * 100) / 255 + 0.5);
}

void KisBaseNode::setPercentOpacity(quint8 val)
{
    setOpacity(int(float(val * 255) / 100 + 0.5));
}

const QString& KisBaseNode::compositeOpId() const
{
    return m_d->compositeOp;
}

165
void KisBaseNode::setCompositeOpId(const QString& compositeOp)
166
{
167 168
    if (m_d->compositeOp == compositeOp) return;

169
    m_d->compositeOp = compositeOp;
170
    baseNodeChangedCallback();
171
    baseNodeInvalidateAllFramesCallback();
172
}
173

174
KisBaseNode::PropertyList KisBaseNode::sectionModelProperties() const
175
{
176
    KisBaseNode::PropertyList l;
177 178
    l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::visible, visible(), m_d->hack_visible.isInStasis, m_d->hack_visible.stateInStasis);
    l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::locked, userLocked());
179 180 181
    return l;
}

182
void KisBaseNode::setSectionModelProperties(const KisBaseNode::PropertyList &properties)
183
{
Halla Rempt's avatar
Halla Rempt committed
184
    setVisible(properties.at(0).state.toBool());
185
    m_d->hack_visible = properties.at(0);
Cyrille Berger's avatar
Cyrille Berger committed
186
    setUserLocked(properties.at(1).state.toBool());
187 188
}

189
const KoProperties & KisBaseNode::nodeProperties() const
Halla Rempt's avatar
Halla Rempt committed
190 191 192 193
{
    return m_d->properties;
}

194 195 196 197 198 199
void KisBaseNode::setNodeProperty(const QString & name, const QVariant & value)
{
    m_d->properties.setProperty(name, value);
    baseNodeChangedCallback();
}

Halla Rempt's avatar
Halla Rempt committed
200
void KisBaseNode::mergeNodeProperties(const KoProperties & properties)
Halla Rempt's avatar
Halla Rempt committed
201 202
{
    QMapIterator<QString, QVariant> iter = properties.propertyIterator();
Halla Rempt's avatar
Halla Rempt committed
203
    while (iter.hasNext()) {
Halla Rempt's avatar
Halla Rempt committed
204
        iter.next();
Halla Rempt's avatar
Halla Rempt committed
205
        m_d->properties.setProperty(iter.key(), iter.value());
Halla Rempt's avatar
Halla Rempt committed
206
    }
207
    baseNodeChangedCallback();
208
    baseNodeInvalidateAllFramesCallback();
Halla Rempt's avatar
Halla Rempt committed
209 210
}

Halla Rempt's avatar
Halla Rempt committed
211
bool KisBaseNode::check(const KoProperties & properties) const
Halla Rempt's avatar
Halla Rempt committed
212 213
{
    QMapIterator<QString, QVariant> iter = properties.propertyIterator();
Halla Rempt's avatar
Halla Rempt committed
214
    while (iter.hasNext()) {
Halla Rempt's avatar
Halla Rempt committed
215
        iter.next();
Halla Rempt's avatar
Halla Rempt committed
216 217
        if (m_d->properties.contains(iter.key())) {
            if (m_d->properties.value(iter.key()) != iter.value())
Halla Rempt's avatar
Halla Rempt committed
218 219 220 221 222 223 224
                return false;
        }
    }
    return true;
}


225
QImage KisBaseNode::createThumbnail(qint32 w, qint32 h, Qt::AspectRatioMode aspectRatioMode)
226
{
227 228
    Q_UNUSED(aspectRatioMode);

229
    try {
Halla Rempt's avatar
Halla Rempt committed
230 231 232
        QImage image(w, h, QImage::Format_ARGB32);
        image.fill(0);
        return image;
233
    } catch (const std::bad_alloc&) {
234 235 236 237 238
        return QImage();
    }

}

239
QImage KisBaseNode::createThumbnailForFrame(qint32 w, qint32 h, int time, Qt::AspectRatioMode aspectRatioMode)
240
{
241
    Q_UNUSED(time);
242
    Q_UNUSED(aspectRatioMode);
243 244 245
    return createThumbnail(w, h);
}

246
bool KisBaseNode::visible(bool recursive) const
247
{
248
    bool isVisible = m_d->properties.boolProperty(KisLayerPropertiesIcons::visible.id(), true);
249 250 251
    KisBaseNodeSP parentNode = parentCallback();

    return recursive && isVisible && parentNode ?
252
        parentNode->visible(recursive) : isVisible;
253 254
}

Halla Rempt's avatar
Halla Rempt committed
255
void KisBaseNode::setVisible(bool visible, bool loading)
256
{
257
    const bool isVisible = m_d->properties.boolProperty(KisLayerPropertiesIcons::visible.id(), true);
258 259
    if (!loading && isVisible == visible) return;

260
    m_d->properties.setProperty(KisLayerPropertiesIcons::visible.id(), visible);
261 262
    notifyParentVisibilityChanged(visible);

Halla Rempt's avatar
Halla Rempt committed
263 264
    if (!loading) {
        baseNodeChangedCallback();
265
        baseNodeInvalidateAllFramesCallback();
Halla Rempt's avatar
Halla Rempt committed
266
    }
267 268
}

Cyrille Berger's avatar
Cyrille Berger committed
269
bool KisBaseNode::userLocked() const
270
{
271
    return m_d->properties.boolProperty(KisLayerPropertiesIcons::locked.id(), false);
272 273
}

274 275 276 277 278 279 280 281 282
bool KisBaseNode::belongsToIsolatedGroup() const
{
    if (!m_d->image) {
        return false;
    }

    const KisBaseNode* element = this;

    while (element) {
283
        if (element->isIsolatedRoot()) {
284 285 286 287 288 289 290 291 292
            return true;
        } else {
            element = element->parentCallback().data();
        }
    }

    return false;
}

293 294 295 296 297 298
bool KisBaseNode::isIsolatedRoot() const
{
    if (!m_d->image) {
        return false;
    }

299
    const KisBaseNode* isolatedRoot = m_d->image->isolationRootNode().data();
300 301 302 303

    return (this == isolatedRoot);
}

Cyrille Berger's avatar
Cyrille Berger committed
304
void KisBaseNode::setUserLocked(bool locked)
305
{
306
    const bool isLocked = m_d->properties.boolProperty(KisLayerPropertiesIcons::locked.id(), true);
307 308
    if (isLocked == locked) return;

309
    m_d->properties.setProperty(KisLayerPropertiesIcons::locked.id(), locked);
310
    baseNodeChangedCallback();
Cyrille Berger's avatar
Cyrille Berger committed
311 312
}

313
bool KisBaseNode::isEditable(bool checkVisibility) const
314
{
315 316
    bool editable = true;
    if (checkVisibility) {
317
        editable = ((visible(false) || belongsToIsolatedGroup()) && !userLocked());
318 319
    }
    else {
320
        editable = (!userLocked());
321
    }
Halla Rempt's avatar
Halla Rempt committed
322 323 324 325

    if (editable) {
        KisBaseNodeSP parentNode = parentCallback();
        if (parentNode && parentNode != this) {
326
            editable = parentNode->isEditable(checkVisibility);
Halla Rempt's avatar
Halla Rempt committed
327 328 329
        }
    }
    return editable;
330 331
}

332 333 334 335 336
bool KisBaseNode::hasEditablePaintDevice() const
{
    return paintDevice() && isEditable();
}

337 338
void KisBaseNode::setCollapsed(bool collapsed)
{
339 340
    const bool oldCollapsed = m_d->collapsed;

341
    m_d->collapsed = collapsed;
342 343 344 345

    if (oldCollapsed != collapsed) {
        baseNodeCollapsedChangedCallback();
    }
346 347 348 349 350 351 352
}

bool KisBaseNode::collapsed() const
{
    return m_d->collapsed;
}

353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
void KisBaseNode::setColorLabelIndex(int index)
{
    const int currentLabel = colorLabelIndex();

    if (currentLabel == index) return;

    m_d->properties.setProperty(KisLayerPropertiesIcons::colorLabelIndex.id(), index);
    baseNodeChangedCallback();
}

int KisBaseNode::colorLabelIndex() const
{
    return m_d->properties.intProperty(KisLayerPropertiesIcons::colorLabelIndex.id(), 0);
}

368 369 370 371 372 373 374 375
QUuid KisBaseNode::uuid() const
{
    return m_d->id;
}

void KisBaseNode::setUuid(const QUuid& id)
{
    m_d->id = id;
376
    baseNodeChangedCallback();
377 378
}

379 380 381 382 383
bool KisBaseNode::supportsLodMoves() const
{
    return m_d->supportsLodMoves;
}

384 385 386 387 388
bool KisBaseNode::supportsLodPainting() const
{
    return true;
}

389 390
void KisBaseNode::setImage(KisImageWSP image)
{
391 392 393 394 395 396
    m_d->image = image;
}

KisImageWSP KisBaseNode::image() const
{
    return m_d->image;
397 398
}

399 400 401 402 403
bool KisBaseNode::isFakeNode() const
{
    return false;
}

404 405 406 407
void KisBaseNode::setSupportsLodMoves(bool value)
{
    m_d->supportsLodMoves = value;
}
408

409

410
QMap<QString, KisKeyframeChannel*> KisBaseNode::keyframeChannels() const
411
{
412
    return m_d->keyframeChannels;
413 414 415 416
}

KisKeyframeChannel * KisBaseNode::getKeyframeChannel(const QString &id) const
{
417 418 419 420
    QMap<QString, KisKeyframeChannel*>::const_iterator i = m_d->keyframeChannels.constFind(id);
    if (i == m_d->keyframeChannels.constEnd()) {
        return 0;
    }
421 422 423
    return i.value();
}

424 425 426 427 428 429 430 431 432 433 434 435 436
bool KisBaseNode::isPinnedToTimeline() const
{
    return m_d->pinnedToTimeline;
}

void KisBaseNode::setPinnedToTimeline(bool pinned)
{
   if (pinned == m_d->pinnedToTimeline) return;

   m_d->pinnedToTimeline = pinned;
   baseNodeChangedCallback();
}

437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
KisKeyframeChannel * KisBaseNode::getKeyframeChannel(const QString &id, bool create)
{
    KisKeyframeChannel *channel = getKeyframeChannel(id);

    if (!channel && create) {
        channel = requestKeyframeChannel(id);

        if (channel) {
            addKeyframeChannel(channel);
        }
    }

    return channel;
}

452 453 454 455 456 457 458 459 460 461 462 463 464 465
bool KisBaseNode::isAnimated() const
{
    return m_d->animated;
}

void KisBaseNode::enableAnimation()
{
    m_d->animated = true;
    baseNodeChangedCallback();
}

void KisBaseNode::addKeyframeChannel(KisKeyframeChannel *channel)
{
    m_d->keyframeChannels.insert(channel->id(), channel);
466
    emit keyframeChannelAdded(channel);
467
}
468 469 470

KisKeyframeChannel *KisBaseNode::requestKeyframeChannel(const QString &id)
{
471 472 473 474 475 476
    if (id == KisKeyframeChannel::Opacity.id()) {
        Q_ASSERT(m_d->opacityChannel.isNull());

        KisPaintDeviceSP device = original();

        if (device) {
477
            KisNode* node = dynamic_cast<KisNode*>(this);
478 479
            KisScalarKeyframeChannel * channel = new KisScalarKeyframeChannel(
                KisKeyframeChannel::Opacity,
480
                node
481 482
            );

483 484 485 486
            channel->setLimits(0, 255);
            channel->setDefaultInterpolationMode(KisScalarKeyframe::Linear);
            channel->setDefaultValue(255);

487 488 489 490 491 492
            m_d->opacityChannel.reset(channel);

            return channel;
        }
    }

493 494
    return 0;
}
495 496 497 498 499 500 501 502 503

bool KisBaseNode::supportsKeyframeChannel(const QString &id)
{
    if (id == KisKeyframeChannel::Opacity.id() && original()) {
        return true;
    }

    return false;
}