kis_layer.cc 28 KB
Newer Older
1
/*
2
3
4
 *  SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
 *  SPDX-FileCopyrightText: 2005 C. Boemann <cbo@boemann.dk>
 *  SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com>
5
 *
Samuel Gaist's avatar
Samuel Gaist committed
6
 *  SPDX-License-Identifier: GPL-2.0-or-later
7
 */
8

Halla Rempt's avatar
Halla Rempt committed
9
10
11
#include "kis_layer.h"


12
#include <klocalizedstring.h>
13
#include <QImage>
14
#include <QBitArray>
15
#include <QStack>
Sebastian Sauer's avatar
Sebastian Sauer committed
16
17
#include <QMutex>
#include <QMutexLocker>
18
19
20
#include <QReadWriteLock>
#include <QReadLocker>
#include <QWriteLocker>
Halla Rempt's avatar
Halla Rempt committed
21

22
#include <KoIcon.h>
23
#include <kis_icon.h>
Halla Rempt's avatar
Halla Rempt committed
24
#include <KoProperties.h>
25
#include <KoCompositeOpRegistry.h>
Halla Rempt's avatar
Halla Rempt committed
26
#include <KoColorSpace.h>
27

28
#include "kis_debug.h"
Patrick Julien's avatar
Patrick Julien committed
29
#include "kis_image.h"
Halla Rempt's avatar
Halla Rempt committed
30

31
#include "kis_painter.h"
32
33
#include "kis_mask.h"
#include "kis_effect_mask.h"
Halla Rempt's avatar
Halla Rempt committed
34
#include "kis_selection_mask.h"
35
#include "kis_meta_data_store.h"
36
#include "kis_selection.h"
37
#include "kis_paint_layer.h"
38
#include "kis_raster_keyframe_channel.h"
39

Dmitry Kazakov's avatar
Dmitry Kazakov committed
40
41
#include "kis_clone_layer.h"

42
#include "kis_psd_layer_style.h"
43
#include "kis_layer_projection_plane.h"
44
#include "layerstyles/kis_layer_style_projection_plane.h"
45

46
#include "krita_utils.h"
47
#include "kis_layer_properties_icons.h"
48
#include "kis_layer_utils.h"
49
#include "kis_projection_leaf.h"
50
#include "KisSafeNodeProjectionStore.h"
51

Dmitry Kazakov's avatar
Dmitry Kazakov committed
52
53
54

class KisCloneLayersList {
public:
55
    void addClone(KisCloneLayerWSP cloneLayer) {
Dmitry Kazakov's avatar
Dmitry Kazakov committed
56
57
58
        m_clonesList.append(cloneLayer);
    }

59
    void removeClone(KisCloneLayerWSP cloneLayer) {
Dmitry Kazakov's avatar
Dmitry Kazakov committed
60
61
62
63
        m_clonesList.removeOne(cloneLayer);
    }

    void setDirty(const QRect &rect) {
64
65
66
67
        Q_FOREACH (KisCloneLayerSP clone, m_clonesList) {
            if (clone) {
                clone->setDirtyOriginal(rect);
            }
Dmitry Kazakov's avatar
Dmitry Kazakov committed
68
69
70
        }
    }

71
72
73
74
    const QList<KisCloneLayerWSP> registeredClones() const {
        return m_clonesList;
    }

Dmitry Kazakov's avatar
Dmitry Kazakov committed
75
76
77
78
    bool hasClones() const {
        return !m_clonesList.isEmpty();
    }

Dmitry Kazakov's avatar
Dmitry Kazakov committed
79
private:
80
    QList<KisCloneLayerWSP> m_clonesList;
Dmitry Kazakov's avatar
Dmitry Kazakov committed
81
82
};

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
class KisLayerMasksCache {
public:
    KisLayerMasksCache(KisLayer *parent)
        : m_parent(parent)
    {
    }

    KisSelectionMaskSP selectionMask() {
        QReadLocker readLock(&m_lock);

        if (!m_isSelectionMaskValid) {
            readLock.unlock();

            QWriteLocker writeLock(&m_lock);
            if (!m_isSelectionMaskValid) {
                KoProperties properties;
                properties.setProperty("active", true);
                properties.setProperty("visible", true);
                QList<KisNodeSP> masks = m_parent->childNodes(QStringList("KisSelectionMask"), properties);

                // return the first visible mask
                Q_FOREACH (KisNodeSP mask, masks) {
                    if (mask) {
                        m_selectionMask = dynamic_cast<KisSelectionMask*>(mask.data());
                        break;
                    }
                }
                m_isSelectionMaskValid = true;
            }

            // return under write lock
            return m_selectionMask;
        }

        // return under read lock
        return m_selectionMask;
    }

    QList<KisEffectMaskSP> effectMasks() {
        QReadLocker readLock(&m_lock);

        if (!m_isEffectMasksValid) {
            readLock.unlock();

            QWriteLocker writeLock(&m_lock);
            if (!m_isEffectMasksValid) {
                m_effectMasks = m_parent->searchEffectMasks(0);
                m_isEffectMasksValid = true;
            }

            // return under write lock
            return m_effectMasks;
        }

        // return under read lock
        return m_effectMasks;
    }

    void setDirty()
    {
        QWriteLocker l(&m_lock);
        m_isSelectionMaskValid = false;
        m_isEffectMasksValid = false;
        m_selectionMask = 0;
        m_effectMasks.clear();
    }

private:
    KisLayer *m_parent;

    QReadWriteLock m_lock;

    bool m_isSelectionMaskValid = false;
    bool m_isEffectMasksValid = false;
    KisSelectionMaskSP m_selectionMask;
    QList<KisEffectMaskSP> m_effectMasks;
};

161
struct Q_DECL_HIDDEN KisLayer::Private
Halla Rempt's avatar
Halla Rempt committed
162
{
163
164
165
166
    Private(KisLayer *q)
        : masksCache(q)
    {
    }
167

168
    QBitArray channelFlags;
169
    KisMetaData::Store* metaDataStore;
Dmitry Kazakov's avatar
Dmitry Kazakov committed
170
    KisCloneLayersList clonesList;
171

172
    KisPSDLayerStyleSP layerStyle;
173
    KisLayerStyleProjectionPlaneSP layerStyleProjectionPlane;
174

175
    KisLayerProjectionPlaneSP projectionPlane;
176
    KisSafeNodeProjectionStoreSP safeProjection;
177

178
    KisLayerMasksCache masksCache;
179
180
181
};


Halla Rempt's avatar
Halla Rempt committed
182
KisLayer::KisLayer(KisImageWSP image, const QString &name, quint8 opacity)
183
        : KisNode(image)
184
        , m_d(new Private(this))
185
{
Halla Rempt's avatar
Halla Rempt committed
186
187
    setName(name);
    setOpacity(opacity);
188
    m_d->metaDataStore = new KisMetaData::Store();
189
    m_d->projectionPlane = toQShared(new KisLayerProjectionPlane(this));
190
    m_d->safeProjection = new KisSafeNodeProjectionStore();
191
    m_d->safeProjection->setImage(image);
192
193
}

194
KisLayer::KisLayer(const KisLayer& rhs)
Halla Rempt's avatar
Halla Rempt committed
195
        : KisNode(rhs)
196
        , m_d(new Private(this))
197
{
198
    if (this != &rhs) {
199
        m_d->metaDataStore = new KisMetaData::Store(*rhs.m_d->metaDataStore);
200
        m_d->channelFlags = rhs.m_d->channelFlags;
201

202
203
        setName(rhs.name());
        m_d->projectionPlane = toQShared(new KisLayerProjectionPlane(this));
204
        m_d->safeProjection = new KisSafeNodeProjectionStore(*rhs.m_d->safeProjection);
205
        m_d->safeProjection->setImage(image());
206

207
        if (rhs.m_d->layerStyle) {
208
            m_d->layerStyle = rhs.m_d->layerStyle->clone().dynamicCast<KisPSDLayerStyle>();
209
210
211
212
213
214
215

            if (rhs.m_d->layerStyleProjectionPlane) {
                m_d->layerStyleProjectionPlane = toQShared(
                    new KisLayerStyleProjectionPlane(*rhs.m_d->layerStyleProjectionPlane,
                                                     this,
                                                     m_d->layerStyle));
            }
216
        }
217
    }
218
219
}

Patrick Julien's avatar
Patrick Julien committed
220
221
KisLayer::~KisLayer()
{
222
    delete m_d->metaDataStore;
Cyrille Berger's avatar
Cyrille Berger committed
223
    delete m_d;
224
225
}

Cyrille Berger's avatar
+ const    
Cyrille Berger committed
226
const KoColorSpace * KisLayer::colorSpace() const
227
{
228
    KisImageSP image = this->image();
229
230
231
232
    if (!image) {
        return nullptr;
    }
    return image->colorSpace();
233
234
}

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
const KoCompositeOp * KisLayer::compositeOp() const
{
    /**
     * FIXME: This function duplicates the same function from
     * KisMask. We can't move it to KisBaseNode as it doesn't
     * know anything about parent() method of KisNode
     * Please think it over...
     */

    KisNodeSP parentNode = parent();
    if (!parentNode) return 0;

    if (!parentNode->colorSpace()) return 0;
    const KoCompositeOp* op = parentNode->colorSpace()->compositeOp(compositeOpId());
    return op ? op : parentNode->colorSpace()->compositeOp(COMPOSITE_OVER);
}

252
KisPSDLayerStyleSP KisLayer::layerStyle() const
253
{
254
    return m_d->layerStyle;
255
256
}

257
void KisLayer::setLayerStyle(KisPSDLayerStyleSP layerStyle)
258
{
259
    if (layerStyle) {
260
        KIS_SAFE_ASSERT_RECOVER_NOOP(layerStyle->hasLocalResourcesSnapshot());
261

262
        m_d->layerStyle = layerStyle;
263

264
265
266
        KisLayerStyleProjectionPlaneSP plane = !layerStyle->isEmpty() ?
            KisLayerStyleProjectionPlaneSP(new KisLayerStyleProjectionPlane(this)) :
            KisLayerStyleProjectionPlaneSP(0);
267
268

        m_d->layerStyleProjectionPlane = plane;
269
    } else {
270
        m_d->layerStyleProjectionPlane.clear();
271
        m_d->layerStyle.clear();
272
    }
273
274
}

275
KisBaseNode::PropertyList KisLayer::sectionModelProperties() const
Gábor Lehel's avatar
Gábor Lehel committed
276
{
277
    KisBaseNode::PropertyList l = KisBaseNode::sectionModelProperties();
278
    l << KisBaseNode::Property(KoID("opacity", i18n("Opacity")), i18n("%1%", percentOpacity()));
279

280
281
282
    const KoCompositeOp * compositeOp = this->compositeOp();

    if (compositeOp) {
283
        l << KisBaseNode::Property(KoID("compositeop", i18n("Blending Mode")), compositeOp->description());
284
285
    }

286
    if (m_d->layerStyle && !m_d->layerStyle->isEmpty()) {
287
        l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::layerStyle, m_d->layerStyle->isEnabled());
288
289
    }

290
    l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::inheritAlpha, alphaChannelDisabled());
291

Gábor Lehel's avatar
Gábor Lehel committed
292
293
294
    return l;
}

295
void KisLayer::setSectionModelProperties(const KisBaseNode::PropertyList &properties)
296
{
297
    KisBaseNode::setSectionModelProperties(properties);
298

299
    Q_FOREACH (const KisBaseNode::Property &property, properties) {
300
        if (property.id == KisLayerPropertiesIcons::inheritAlpha.id()) {
301
302
            disableAlphaChannel(property.state.toBool());
        }
303

304
        if (property.id == KisLayerPropertiesIcons::layerStyle.id()) {
305
306
307
            if (m_d->layerStyle &&
                m_d->layerStyle->isEnabled() != property.state.toBool()) {

308
                m_d->layerStyle->setEnabled(property.state.toBool());
309
310
311

                baseNodeChangedCallback();
                baseNodeInvalidateAllFramesCallback();
312
313
            }
        }
314
    }
315
316
}

317
318
void KisLayer::disableAlphaChannel(bool disable)
{
319
320
321
322
    QBitArray newChannelFlags = m_d->channelFlags;

    if(newChannelFlags.isEmpty())
        newChannelFlags = colorSpace()->channelFlags(true, true);
323

324
    if(disable)
325
        newChannelFlags &= colorSpace()->channelFlags(true, false);
326
    else
327
328
329
        newChannelFlags |= colorSpace()->channelFlags(false, true);

    setChannelFlags(newChannelFlags);
330
331
332
333
}

bool KisLayer::alphaChannelDisabled() const
{
Halla Rempt's avatar
Halla Rempt committed
334
    QBitArray flags = colorSpace()->channelFlags(false, true) & m_d->channelFlags;
335
336
337
338
    return flags.count(true) == 0 && !m_d->channelFlags.isEmpty();
}


Halla Rempt's avatar
Halla Rempt committed
339
void KisLayer::setChannelFlags(const QBitArray & channelFlags)
340
{
341
    Q_ASSERT(channelFlags.isEmpty() ||((quint32)channelFlags.count() == colorSpace()->channelCount()));
342

343
344
345
346
347
    if (KritaUtils::compareChannelFlags(channelFlags,
                                        this->channelFlags())) {
        return;
    }

348
349
350
351
352
353
354
    if (!channelFlags.isEmpty() &&
        channelFlags == QBitArray(channelFlags.size(), true)) {

        m_d->channelFlags.clear();
    } else {
        m_d->channelFlags = channelFlags;
    }
355
356
357

    baseNodeChangedCallback();
    baseNodeInvalidateAllFramesCallback();
358
359
}

360
QBitArray & KisLayer::channelFlags() const
361
{
362
    return m_d->channelFlags;
363
364
}

365
366
bool KisLayer::temporary() const
{
Halla Rempt's avatar
Halla Rempt committed
367
    return nodeProperties().boolProperty("temporary", false);
368
369
370
371
}

void KisLayer::setTemporary(bool t)
{
372
    setNodeProperty("temporary", t);
373
374
}

375
void KisLayer::setImage(KisImageWSP image)
376
{
377
378
379
380
381
    // we own the projection device, so we should take care about it
    KisPaintDeviceSP projection = this->projection();
    if (projection && projection != original()) {
        projection->setDefaultBounds(new KisDefaultBounds(image));
    }
382
    m_d->safeProjection->setImage(image);
383

384
    KisNode::setImage(image);
385
386
}

387
388
389
390
391
392
bool KisLayer::canMergeAndKeepBlendOptions(KisLayerSP otherLayer)
{
    return
        this->compositeOpId() == otherLayer->compositeOpId() &&
        this->opacity() == otherLayer->opacity() &&
        this->channelFlags() == otherLayer->channelFlags() &&
393
394
395
        !this->layerStyle() && !otherLayer->layerStyle() &&
        (this->colorSpace() == otherLayer->colorSpace() ||
         *this->colorSpace() == *otherLayer->colorSpace());
396
397
}

398
KisLayerSP KisLayer::createMergedLayerTemplate(KisLayerSP prevLayer)
399
{
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
    const bool keepBlendingOptions = canMergeAndKeepBlendOptions(prevLayer);

    KisLayerSP newLayer = new KisPaintLayer(image(), prevLayer->name(), OPACITY_OPAQUE_U8);

    if (keepBlendingOptions) {
        newLayer->setCompositeOpId(compositeOpId());
        newLayer->setOpacity(opacity());
        newLayer->setChannelFlags(channelFlags());
    }

    return newLayer;
}

void KisLayer::fillMergedLayerTemplate(KisLayerSP dstLayer, KisLayerSP prevLayer)
{
    const bool keepBlendingOptions = canMergeAndKeepBlendOptions(prevLayer);
416
417
418
419
420
421

    QRect layerProjectionExtent = this->projection()->extent();
    QRect prevLayerProjectionExtent = prevLayer->projection()->extent();
    bool alphaDisabled = this->alphaChannelDisabled();
    bool prevAlphaDisabled = prevLayer->alphaChannelDisabled();

422
    KisPaintDeviceSP mergedDevice = dstLayer->paintDevice();
423
424

    if (!keepBlendingOptions) {
425
426
        KisPainter gc(mergedDevice);

427
428
429
430
431
        KisImageSP imageSP = image().toStrongRef();
        if (!imageSP) {
            return;
        }

432
433
434
        //Copy the pixels of previous layer with their actual alpha value
        prevLayer->disableAlphaChannel(false);

435
        prevLayer->projectionPlane()->apply(&gc, prevLayerProjectionExtent | imageSP->bounds());
436
437
438
439
440
441
442
443
444

        //Restore the previous prevLayer disableAlpha status for correct undo/redo
        prevLayer->disableAlphaChannel(prevAlphaDisabled);

        //Paint the pixels of the current layer, using their actual alpha value
        if (alphaDisabled == prevAlphaDisabled) {
            this->disableAlphaChannel(false);
        }

445
        this->projectionPlane()->apply(&gc, layerProjectionExtent | imageSP->bounds());
446
447
448
449
450
451

        //Restore the layer disableAlpha status for correct undo/redo
        this->disableAlphaChannel(alphaDisabled);
    }
    else {
        //Copy prevLayer
452
453
        KisPaintDeviceSP srcDev = prevLayer->projection();
        mergedDevice->makeCloneFrom(srcDev, srcDev->extent());
454
455
456
457

        //Paint layer on the copy
        KisPainter gc(mergedDevice);
        gc.bitBlt(layerProjectionExtent.topLeft(), this->projection(), layerProjectionExtent);
458
    }
459
460
}

461
void KisLayer::registerClone(KisCloneLayerWSP clone)
Dmitry Kazakov's avatar
Dmitry Kazakov committed
462
463
464
465
{
    m_d->clonesList.addClone(clone);
}

466
void KisLayer::unregisterClone(KisCloneLayerWSP clone)
Dmitry Kazakov's avatar
Dmitry Kazakov committed
467
468
{
    m_d->clonesList.removeClone(clone);
469
470
}

471
472
473
474
475
const QList<KisCloneLayerWSP> KisLayer::registeredClones() const
{
    return m_d->clonesList.registeredClones();
}

Dmitry Kazakov's avatar
Dmitry Kazakov committed
476
477
478
479
480
481
482
483
484
485
bool KisLayer::hasClones() const
{
    return m_d->clonesList.hasClones();
}

void KisLayer::updateClones(const QRect &rect)
{
    m_d->clonesList.setDirty(rect);
}

486
void KisLayer::notifyChildMaskChanged()
487
{
488
    m_d->masksCache.setDirty();
489
490
}

Halla Rempt's avatar
Halla Rempt committed
491
KisSelectionMaskSP KisLayer::selectionMask() const
492
{
493
    return m_d->masksCache.selectionMask();
494
495
}

496
497
KisSelectionSP KisLayer::selection() const
{
498
499
500
501
    KisSelectionMaskSP mask = selectionMask();

    if (mask) {
        return mask->selection();
502
    }
503

504
    KisImageSP image = this->image();
505
506
    if (image) {
        return image->globalSelection();
507
    }
508
    return KisSelectionSP();
509
510
}

511
512
513
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////

514
QList<KisEffectMaskSP> KisLayer::effectMasks() const
515
{
516
    return m_d->masksCache.effectMasks();
517
518
}

519
QList<KisEffectMaskSP> KisLayer::effectMasks(KisNodeSP lastNode) const
520
521
522
523
524
525
526
527
528
529
{
    if (lastNode.isNull()) {
        return effectMasks();
    } else {
        // happens rarely.
        return searchEffectMasks(lastNode);
    }
}

QList<KisEffectMaskSP> KisLayer::searchEffectMasks(KisNodeSP lastNode) const
530
531
532
{
    QList<KisEffectMaskSP> masks;

533
534
535
536
537
    KIS_SAFE_ASSERT_RECOVER_NOOP(projectionLeaf());

    KisProjectionLeafSP child = projectionLeaf()->firstChild();
    while (child) {
        if (child->node() == lastNode) break;
538

539
540
        KIS_SAFE_ASSERT_RECOVER_NOOP(child);
        KIS_SAFE_ASSERT_RECOVER_NOOP(child->node());
541

542
543
544
        if (child->visible()) {
            KisEffectMaskSP mask = dynamic_cast<KisEffectMask*>(const_cast<KisNode*>(child->node().data()));
            if (mask) {
545
                masks.append(mask);
546
            }
547
        }
548
549

        child = child->nextSibling();
550
    }
551

552
553
554
    return masks;
}

555
bool KisLayer::hasEffectMasks() const
556
{
557
    return  !m_d->masksCache.effectMasks().isEmpty();
558
559
}

560
561
562
QRect KisLayer::masksChangeRect(const QList<KisEffectMaskSP> &masks,
                                const QRect &requestedRect,
                                bool &rectVariesFlag) const
563
{
564
    rectVariesFlag = false;
565

566
    QRect prevChangeRect = requestedRect;
567
568
569
570
571
572

    /**
     * We set default value of the change rect for the case
     * when there is no mask at all
     */
    QRect changeRect = requestedRect;
573

574
    Q_FOREACH (const KisEffectMaskSP& mask, masks) {
575
        changeRect = mask->changeRect(prevChangeRect);
576

Halla Rempt's avatar
Halla Rempt committed
577
        if (changeRect != prevChangeRect)
578
            rectVariesFlag = true;
579

580
581
        prevChangeRect = changeRect;
    }
582

583
584
585
586
587
588
589
590
591
592
593
594
    return changeRect;
}

QRect KisLayer::masksNeedRect(const QList<KisEffectMaskSP> &masks,
                              const QRect &changeRect,
                              QStack<QRect> &applyRects,
                              bool &rectVariesFlag) const
{
    rectVariesFlag = false;

    QRect prevNeedRect = changeRect;
    QRect needRect;
595
    
Halla Rempt's avatar
Halla Rempt committed
596
    for (qint32 i = masks.size() - 1; i >= 0; i--) {
597
598
599
600
        applyRects.push(prevNeedRect);

        needRect = masks[i]->needRect(prevNeedRect);

Halla Rempt's avatar
Halla Rempt committed
601
        if (prevNeedRect != needRect)
602
603
604
            rectVariesFlag = true;

        prevNeedRect = needRect;
605
    }
606

607
608
    return needRect;
}
609

610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
KisNode::PositionToFilthy calculatePositionToFilthy(KisNodeSP nodeInQuestion,
                                           KisNodeSP filthy,
                                           KisNodeSP parent)
{
    if (parent == filthy || parent != filthy->parent()) {
        return KisNode::N_ABOVE_FILTHY;
    }

    if (nodeInQuestion == filthy) {
        return KisNode::N_FILTHY;
    }

    KisNodeSP node = nodeInQuestion->prevSibling();
    while (node) {
        if (node == filthy) {
            return KisNode::N_ABOVE_FILTHY;
        }
        node = node->prevSibling();
    }

    return KisNode::N_BELOW_FILTHY;
}

633
QRect KisLayer::applyMasks(const KisPaintDeviceSP source,
634
                           KisPaintDeviceSP destination,
635
                           const QRect &requestedRect,
636
                           KisNodeSP filthyNode,
637
                           KisNodeSP lastNode) const
638
639
640
641
{
    Q_ASSERT(source);
    Q_ASSERT(destination);

642
    QList<KisEffectMaskSP> masks = effectMasks(lastNode);
643
644
    QRect changeRect;
    QRect needRect;
645

Halla Rempt's avatar
Halla Rempt committed
646
    if (masks.isEmpty()) {
647
        changeRect = requestedRect;
Halla Rempt's avatar
Halla Rempt committed
648
        if (source != destination) {
649
650
            copyOriginalToProjection(source, destination, requestedRect);
        }
Halla Rempt's avatar
Halla Rempt committed
651
    } else {
652
653
654
655
        QStack<QRect> applyRects;
        bool changeRectVaries;
        bool needRectVaries;

656
657
658
659
660
661
662
663
        /**
         * FIXME: Assume that varying of the changeRect has already
         * been taken into account while preparing walkers
         */
        changeRectVaries = false;
        changeRect = requestedRect;
        //changeRect = masksChangeRect(masks, requestedRect,
        //                             changeRectVaries);
664
665
666
667

        needRect = masksNeedRect(masks, changeRect,
                                 applyRects, needRectVaries);

Halla Rempt's avatar
Halla Rempt committed
668
        if (!changeRectVaries && !needRectVaries) {
669
670
671
672
673
674
675
676
            /**
             * A bit of optimization:
             * All filters will read/write exactly from/to the requested
             * rect so we needn't create temporary paint device,
             * just apply it onto destination
             */
            Q_ASSERT(needRect == requestedRect);

Halla Rempt's avatar
Halla Rempt committed
677
            if (source != destination) {
678
679
680
                copyOriginalToProjection(source, destination, needRect);
            }

681
            Q_FOREACH (const KisEffectMaskSP& mask, masks) {
682
683
684
                const QRect maskApplyRect = applyRects.pop();
                const QRect maskNeedRect =
                    applyRects.isEmpty() ? needRect : applyRects.top();
685
                    
686
687
                PositionToFilthy maskPosition = calculatePositionToFilthy(mask, filthyNode, const_cast<KisLayer*>(this));
                mask->apply(destination, maskApplyRect, maskNeedRect, maskPosition);
688
            }
689
            Q_ASSERT(applyRects.isEmpty());
Halla Rempt's avatar
Halla Rempt committed
690
        } else {
691
692
693
694
695
696
697
            /**
             * We can't eliminate additional copy-op
             * as filters' behaviour may be quite insane here,
             * so let them work on their own paintDevice =)
             */

            KisPaintDeviceSP tempDevice = new KisPaintDevice(colorSpace());
698
            tempDevice->prepareClone(source);
699
700
            copyOriginalToProjection(source, tempDevice, needRect);

701
702
703
            QRect maskApplyRect = applyRects.pop();
            QRect maskNeedRect = needRect;

704
            Q_FOREACH (const KisEffectMaskSP& mask, masks) {
705
706
                PositionToFilthy maskPosition = calculatePositionToFilthy(mask, filthyNode, const_cast<KisLayer*>(this));
                mask->apply(tempDevice, maskApplyRect, maskNeedRect, maskPosition);
707
708
709
710
711

                if (!applyRects.isEmpty()) {
                    maskNeedRect = maskApplyRect;
                    maskApplyRect = applyRects.pop();
                }
712
713
714
            }
            Q_ASSERT(applyRects.isEmpty());

715
            KisPainter::copyAreaOptimized(changeRect.topLeft(), tempDevice, destination, changeRect);
716
717
718
719
720
721
        }
    }

    return changeRect;
}

722
QRect KisLayer::updateProjection(const QRect& rect, KisNodeSP filthyNode)
723
724
725
726
{
    QRect updatedRect = rect;
    KisPaintDeviceSP originalDevice = original();
    if (!rect.isValid() ||
727
        (!visible() && !isIsolatedRoot() && !hasClones()) ||
728
        !originalDevice) return QRect();
729

Halla Rempt's avatar
Halla Rempt committed
730
    if (!needProjection() && !hasEffectMasks()) {
731
        m_d->safeProjection->releaseDevice();
Halla Rempt's avatar
Halla Rempt committed
732
    } else {
733

Halla Rempt's avatar
Halla Rempt committed
734
        if (!updatedRect.isEmpty()) {
735
            KisPaintDeviceSP projection = m_d->safeProjection->getDeviceLazy(originalDevice);
736
            updatedRect = applyMasks(originalDevice, projection,
737
                                     updatedRect, filthyNode, 0);
738
        }
739
    }
740
741

    return updatedRect;
742
743
}

744
QRect KisLayer::partialChangeRect(KisNodeSP lastNode, const QRect& rect)
745
746
{
    bool changeRectVaries = false;
747
748
749
    QRect changeRect = outgoingChangeRect(rect);
    changeRect = masksChangeRect(effectMasks(lastNode), changeRect,
                                 changeRectVaries);
750

751
752
753
754
755
756
757
758
759
760
    return changeRect;
}

/**
 * \p rect is a dirty rect in layer's original() coordinates!
 */
void KisLayer::buildProjectionUpToNode(KisPaintDeviceSP projection, KisNodeSP lastNode, const QRect& rect)
{
    QRect changeRect = partialChangeRect(lastNode, rect);

761
762
    KisPaintDeviceSP originalDevice = original();

763
    KIS_SAFE_ASSERT_RECOVER_RETURN(needProjection() || hasEffectMasks());
764
765
766

    if (!changeRect.isEmpty()) {
        applyMasks(originalDevice, projection,
767
                   changeRect, this, lastNode);
768
769
770
    }
}

771
772
773
774
bool KisLayer::needProjection() const
{
    return false;
}
775

776
777
778
void KisLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
                                        KisPaintDeviceSP projection,
                                        const QRect& rect) const
779
{
780
    KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect);
781
782
}

783
KisAbstractProjectionPlaneSP KisLayer::projectionPlane() const
784
{
785
    return m_d->layerStyleProjectionPlane ?
786
787
        KisAbstractProjectionPlaneSP(m_d->layerStyleProjectionPlane) :
        KisAbstractProjectionPlaneSP(m_d->projectionPlane);
788
}
789

790
KisLayerProjectionPlaneSP KisLayer::internalProjectionPlane() const
791
{
792
    return m_d->projectionPlane;
793
}
794

795
796
KisPaintDeviceSP KisLayer::projection() const
{
797
    KisPaintDeviceSP originalDevice = original();
798

799
    return needProjection() || hasEffectMasks() ?
800
        m_d->safeProjection->getDeviceLazy(originalDevice) : originalDevice;
801
802
}

803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
QRect KisLayer::tightUserVisibleBounds() const
{
    QRect changeRect = exactBounds();

    /// we do not use incomingChangeRect() here, because
    /// exactBounds() already takes it into account (it
    /// was used while preparing original())

    bool changeRectVaries;
    changeRect = outgoingChangeRect(changeRect);
    changeRect = masksChangeRect(effectMasks(), changeRect, changeRectVaries);

    return changeRect;
}

818
819
820
821
822
QRect KisLayer::amortizedProjectionRectForCleanupInChangePass() const
{
    return projection()->exactBoundsAmortized();
}

823
QRect KisLayer::changeRect(const QRect &rect, PositionToFilthy pos) const
824
{
825
    QRect changeRect = rect;
826
    changeRect = incomingChangeRect(changeRect);
827

828
    if(pos == KisNode::N_FILTHY) {
829
        QRect projectionToBeUpdated = amortizedProjectionRectForCleanupInChangePass() & changeRect;
Dmitry Kazakov's avatar
Dmitry Kazakov committed
830

831
        bool changeRectVaries;
832
833
        changeRect = outgoingChangeRect(changeRect);
        changeRect = masksChangeRect(effectMasks(), changeRect, changeRectVaries);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
834
835
836
837
838
839
840
841
842
843
844
845
846

        /**
         * If the projection contains some dirty areas we should also
         * add them to the change rect, because they might have
         * changed. E.g. when a visibility of the mask has chnaged
         * while the parent layer was invinisble.
         */

        if (!projectionToBeUpdated.isEmpty() &&
            !changeRect.contains(projectionToBeUpdated)) {

            changeRect |= projectionToBeUpdated;
        }
847
848
    }

849
    // TODO: string comparison: optimize!
850
851
852
853
    if (pos != KisNode::N_FILTHY &&
        pos != KisNode::N_FILTHY_PROJECTION &&
        compositeOpId() != COMPOSITE_COPY) {

854
        changeRect |= rect;
855
    }
856

857
    return changeRect;
858
859
}

860
861
862
void KisLayer::childNodeChanged(KisNodeSP changedChildNode)
{
    if (dynamic_cast<KisMask*>(changedChildNode.data())) {
863
        notifyChildMaskChanged();
864
865
866
    }
}

867
868
869
870
871
872
873
874
875
876
QRect KisLayer::incomingChangeRect(const QRect &rect) const
{
    return rect;
}

QRect KisLayer::outgoingChangeRect(const QRect &rect) const
{
    return rect;
}

877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
QRect KisLayer::needRectForOriginal(const QRect &rect) const
{
    QRect needRect = rect;

    const QList<KisEffectMaskSP> masks = effectMasks();

    if (!masks.isEmpty()) {
        QStack<QRect> applyRects;
        bool needRectVaries;

        needRect = masksNeedRect(masks, rect,
                                 applyRects, needRectVaries);
    }

    return needRect;
}

894
QImage KisLayer::createThumbnail(qint32 w, qint32 h, Qt::AspectRatioMode aspectRatioMode)
895
{
896
897
898
899
    if (w == 0 || h == 0) {
        return QImage();
    }

900
901
902
    KisPaintDeviceSP originalDevice = original();

    return originalDevice ?
903
           originalDevice->createThumbnail(w, h, aspectRatioMode, 1,
904
905
                                           KoColorConversionTransformation::internalRenderingIntent(),
                                           KoColorConversionTransformation::internalConversionFlags()) : QImage();
906
907
}

908
QImage KisLayer::createThumbnailForFrame(qint32 w, qint32 h, int time, Qt::AspectRatioMode aspectRatioMode)
909
910
911
912
913
914
{
    if (w == 0 || h == 0) {
        return QImage();
    }

    KisPaintDeviceSP originalDevice = original();
915
    if (originalDevice ) {
916
        KisRasterKeyframeChannel *channel = originalDevice->keyframeChannel();
917
918
919

        if (channel) {
            KisPaintDeviceSP targetDevice = new KisPaintDevice(colorSpace());
920
921
            KisRasterKeyframeSP keyframe = channel->activeKeyframeAt<KisRasterKeyframe>(time);
            keyframe->writeFrameToDevice(targetDevice);
922
            return targetDevice->createThumbnail(w, h, aspectRatioMode, 1,
923
924
925
                                                 KoColorConversionTransformation::internalRenderingIntent(),
                                                 KoColorConversionTransformation::internalConversionFlags());
        }
926
    }
927
928

    return createThumbnail(w, h);
929
930
}

931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
qint32 KisLayer::x() const
{
    KisPaintDeviceSP originalDevice = original();
    return originalDevice ? originalDevice->x() : 0;
}
qint32 KisLayer::y() const
{
    KisPaintDeviceSP originalDevice = original();
    return originalDevice ? originalDevice->y() : 0;
}
void KisLayer::setX(qint32 x)
{
    KisPaintDeviceSP originalDevice = original();
    if (originalDevice)
        originalDevice->setX(x);
}
void KisLayer::setY(qint32 y)
{
    KisPaintDeviceSP originalDevice = original();
    if (originalDevice)
        originalDevice->setY(y);
}

954
QRect KisLayer::layerExtentImpl(bool needExactBounds) const
955
{
956
957
958
959
960
961
962
    QRect additionalMaskExtent = QRect();
    QList<KisEffectMaskSP> effectMasks = this->effectMasks();

    Q_FOREACH(KisEffectMaskSP mask, effectMasks) {
        additionalMaskExtent |= mask->nonDependentExtent();
    }

963
    KisPaintDeviceSP originalDevice = original();
964
    QRect layerExtent;
965

966
967
968
969
970
    if (originalDevice) {
        layerExtent = needExactBounds ?
            originalDevice->exactBounds() :
            originalDevice->extent();
    }
971

972
    QRect additionalCompositeOpExtent;
973
974
    if (compositeOpId() == COMPOSITE_COPY ||
        compositeOpId() == COMPOSITE_DESTINATION_IN ||
975
        compositeOpId() == COMPOSITE_DESTINATION_ATOP) {
976

977
        additionalCompositeOpExtent = originalDevice->defaultBounds()->bounds();
978
979
    }

980
981
982
983
984
985
986
    return layerExtent | additionalMaskExtent | additionalCompositeOpExtent;
}

QRect KisLayer::extent() const
{
    return layerExtentImpl(false);
}
987

988
989
990
QRect KisLayer::exactBounds() const
{
    return layerExtentImpl(true);
991
992
}

Halla Rempt's avatar
Halla Rempt committed
993
994
KisLayerSP KisLayer::parentLayer() const
{
995
    return qobject_cast<KisLayer*>(parent().data());
Halla Rempt's avatar
Halla Rempt committed
996
997
}

998
999
1000
KisMetaData::Store* KisLayer::metaData()
{
    return m_d->metaDataStore;
For faster browsing, not all history is shown. View entire blame