kis_mask.cc 8.91 KB
Newer Older
1
2
/*
 *  Copyright (c) 2006 Boudewijn Rempt <boud@valdyas.org>
3
 *            (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
Halla Rempt's avatar
Halla Rempt committed
19

20
21
#include "kis_mask.h"

Halla Rempt's avatar
Halla Rempt committed
22

23
#include <kis_debug.h>
24

25
26
27
// to prevent incomplete class types on "delete selection->flatten();"
#include <kundo2command.h>

28
#include <KoColorSpace.h>
Halla Rempt's avatar
Halla Rempt committed
29
#include <KoColorSpaceRegistry.h>
30
#include <KoCompositeOp.h>
31
32

#include "kis_paint_device.h"
Halla Rempt's avatar
Halla Rempt committed
33
#include "kis_selection.h"
34
#include "kis_pixel_selection.h"
35
#include "kis_painter.h"
36

37
38
39
#include "kis_image.h"
#include "kis_layer.h"

40
41
#include "tiles3/kis_lockless_stack.h"

Halla Rempt's avatar
Halla Rempt committed
42
struct KisMask::Private {
43
    class CachedPaintDevice {
44
    public:
45
46
47
48
49
        KisPaintDeviceSP getDevice(KisPaintDeviceSP prototype) {
            KisPaintDeviceSP device;

            if(!m_stack.pop(device)) {
                device = new KisPaintDevice(prototype->colorSpace());
Halla Rempt's avatar
Halla Rempt committed
50
            }
51

52
            device->prepareClone(prototype);
53
54
            return device;
        }
Dmitry Kazakov's avatar
Dmitry Kazakov committed
55

56
57
58
        void putDevice(KisPaintDeviceSP device) {
            device->clear();
            m_stack.push(device);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
59
        }
60

61
    private:
62
        KisLocklessStack<KisPaintDeviceSP> m_stack;
63
64
    };

65
66
    Private(KisMask *_q) : q(_q) {}

67
    mutable KisSelectionSP selection;
68
    CachedPaintDevice paintDeviceCache;
69
70
71
    KisMask *q;

    void initSelectionImpl(KisSelectionSP copyFrom, KisLayerSP parentLayer, KisPaintDeviceSP copyFromDevice);
72
73
};

Halla Rempt's avatar
Halla Rempt committed
74
75
KisMask::KisMask(const QString & name)
        : KisNode()
76
        , m_d(new Private(this))
77
{
Halla Rempt's avatar
Halla Rempt committed
78
    setName(name);
79
80
81
}

KisMask::KisMask(const KisMask& rhs)
Halla Rempt's avatar
Halla Rempt committed
82
        : KisNode(rhs)
83
        , m_d(new Private(this))
84
{
Halla Rempt's avatar
Halla Rempt committed
85
    setName(rhs.name());
86

87
    if (rhs.m_d->selection) {
88
        m_d->selection = new KisSelection(*rhs.m_d->selection.data());
89
90
        m_d->selection->setParentNode(this);
    }
91
92
93
94
}

KisMask::~KisMask()
{
Halla Rempt's avatar
Halla Rempt committed
95
    delete m_d;
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
const KoColorSpace * KisMask::colorSpace() const
{
    KisNodeSP parentNode = parent();
    return parentNode ? parentNode->colorSpace() : 0;
}

const KoCompositeOp * KisMask::compositeOp() const
{
    /**
     * FIXME: This function duplicates the same function from
     * KisLayer. 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);
}

121
void KisMask::initSelection(KisSelectionSP copyFrom, KisLayerSP parentLayer)
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
{
    m_d->initSelectionImpl(copyFrom, parentLayer, 0);
}

void KisMask::initSelection(KisPaintDeviceSP copyFromDevice, KisLayerSP parentLayer)
{
    m_d->initSelectionImpl(0, parentLayer, copyFromDevice);
}

void KisMask::initSelection(KisLayerSP parentLayer)
{
    m_d->initSelectionImpl(0, parentLayer, 0);
}

void KisMask::Private::initSelectionImpl(KisSelectionSP copyFrom, KisLayerSP parentLayer, KisPaintDeviceSP copyFromDevice)
137
{
138
139
    Q_ASSERT(parentLayer);

140
    KisPaintDeviceSP parentPaintDevice = parentLayer->original();
141

142
    if (copyFrom) {
143
        /**
144
         * We can't use setSelection as we may not have parent() yet
145
         */
146
147
        selection = new KisSelection(*copyFrom);
        selection->setDefaultBounds(new KisSelectionDefaultBounds(parentPaintDevice, parentLayer->image()));
148
        if (copyFrom->hasShapeSelection()) {
149
            delete selection->flatten();
150
        }
151
152
153
    } else if (copyFromDevice) {
        selection = new KisSelection(new KisSelectionDefaultBounds(parentPaintDevice, parentLayer->image()));

154
        KisPainter gc(selection->pixelSelection());
155
156
157
158
159
160
        gc.setCompositeOp(COMPOSITE_COPY);
        QRect rc(copyFromDevice->extent());
        gc.bitBlt(rc.topLeft(), copyFromDevice, rc);

    } else {
        selection = new KisSelection(new KisSelectionDefaultBounds(parentPaintDevice, parentLayer->image()));
161
162

        quint8 newDefaultPixel = MAX_SELECTED;
163
        selection->pixelSelection()->setDefaultPixel(&newDefaultPixel);
164
    }
165
166
    selection->setParentNode(q);
    selection->updateProjection();
167
}
168

169
170
KisSelectionSP KisMask::selection() const
{
171
172
173
174
175
176
    /**
     * The mask is created without any selection present.
     * You must always init the selection with initSelection() method.
     */
    Q_ASSERT(m_d->selection);

Halla Rempt's avatar
Halla Rempt committed
177
    return m_d->selection;
178
179
}

180
181
KisPaintDeviceSP KisMask::paintDevice() const
{
182
    return selection()->pixelSelection();
183
184
}

185
186
187
188
189
190
191
192
193
194
KisPaintDeviceSP KisMask::original() const
{
    return paintDevice();
}

KisPaintDeviceSP KisMask::projection() const
{
    return paintDevice();
}

Halla Rempt's avatar
Halla Rempt committed
195
void KisMask::setSelection(KisSelectionSP selection)
196
{
Halla Rempt's avatar
Halla Rempt committed
197
    m_d->selection = selection;
198
199
    if (parent()) {
        const KisLayer *parentLayer = qobject_cast<const KisLayer*>(parent());
200
        m_d->selection->setDefaultBounds(new KisDefaultBounds(parentLayer->image()));
201
    }
202
    m_d->selection->setParentNode(this);
203
}
Halla Rempt's avatar
Halla Rempt committed
204

Halla Rempt's avatar
Halla Rempt committed
205
void KisMask::select(const QRect & rc, quint8 selectedness)
206
{
207
    KisSelectionSP sel = selection();
208
    KisPixelSelectionSP psel = sel->pixelSelection();
Halla Rempt's avatar
Halla Rempt committed
209
    psel->select(rc, selectedness);
210
    sel->updateProjection(rc);
211
212
}

213
214
215
216

QRect KisMask::decorateRect(KisPaintDeviceSP &src,
                            KisPaintDeviceSP &dst,
                            const QRect & rc) const
217
{
218
219
    Q_UNUSED(src);
    Q_UNUSED(dst);
220
    Q_ASSERT_X(0, "KisMask::decorateRect", "Should be overridden by successors");
221
    return rc;
222
223
}

224
void KisMask::apply(KisPaintDeviceSP projection, const QRect & rc) const
225
{
226
    if (selection()) {
227
228
229

        m_d->selection->updateProjection(rc);

Dmitry Kazakov's avatar
Dmitry Kazakov committed
230
        if(!extent().intersects(rc))
231
232
            return;

233
        KisPaintDeviceSP cacheDevice = m_d->paintDeviceCache.getDevice(projection);
234
235
236

        QRect updatedRect = decorateRect(projection, cacheDevice, rc);

237
        KisPainter gc(projection);
238
239
        gc.setCompositeOp(compositeOp());
        gc.setOpacity(opacity());
240
241
        gc.setSelection(m_d->selection);
        gc.bitBlt(updatedRect.topLeft(), cacheDevice, updatedRect);
242
243
244

        m_d->paintDeviceCache.putDevice(cacheDevice);

Halla Rempt's avatar
Halla Rempt committed
245
    } else {
246
        KisPaintDeviceSP cacheDevice = m_d->paintDeviceCache.getDevice(projection);
247
248
        cacheDevice->makeCloneFromRough(projection, rc);
        projection->clear(rc);
249

250
251
        // FIXME: how about opacity and compositeOp?
        decorateRect(cacheDevice, projection, rc);
252
253

        m_d->paintDeviceCache.putDevice(cacheDevice);
254
    }
255
}
256

257
QRect KisMask::needRect(const QRect &rect,  PositionToFilthy pos) const
258
{
259
    Q_UNUSED(pos);
260
    QRect resultRect = rect;
Halla Rempt's avatar
Halla Rempt committed
261
    if (m_d->selection)
262
263
264
        resultRect &= m_d->selection->selectedRect();

    return resultRect;
265
266
}

267
QRect KisMask::changeRect(const QRect &rect, PositionToFilthy pos) const
268
{
269
    Q_UNUSED(pos);
270
    QRect resultRect = rect;
Halla Rempt's avatar
Halla Rempt committed
271
    if (m_d->selection)
272
273
274
        resultRect &= m_d->selection->selectedRect();

    return resultRect;
275
276
}

277
QRect KisMask::extent() const
278
{
279
    return m_d->selection ? m_d->selection->selectedRect() :
Halla Rempt's avatar
Halla Rempt committed
280
           parent() ? parent()->extent() : QRect();
281
282
}

283
QRect KisMask::exactBounds() const
284
{
285
    return m_d->selection ? m_d->selection->selectedExactRect() :
Halla Rempt's avatar
Halla Rempt committed
286
           parent() ? parent()->exactBounds() : QRect();
287
}
288

289
qint32 KisMask::x() const
290
{
291
    return m_d->selection ? m_d->selection->x() :
Halla Rempt's avatar
Halla Rempt committed
292
           parent() ? parent()->x() : 0;
293
294
}

295
296
297
qint32 KisMask::y() const
{
    return m_d->selection ? m_d->selection->y() :
Halla Rempt's avatar
Halla Rempt committed
298
           parent() ? parent()->y() : 0;
299
300
301
302
}

void KisMask::setX(qint32 x)
{
Halla Rempt's avatar
Halla Rempt committed
303
    if (m_d->selection)
304
305
306
307
        m_d->selection->setX(x);
}

void KisMask::setY(qint32 y)
308
{
Halla Rempt's avatar
Halla Rempt committed
309
    if (m_d->selection)
310
        m_d->selection->setY(y);
311
312
}

313
314
QImage KisMask::createThumbnail(qint32 w, qint32 h)
{
315
316
    KisPaintDeviceSP originalDevice =
        selection() ? selection()->projection() : 0;
317
318

    return originalDevice ?
319
320
321
           originalDevice->createThumbnail(w, h,
                                           KoColorConversionTransformation::InternalRenderingIntent,
                                           KoColorConversionTransformation::InternalConversionFlags) : QImage();
322
323
}

Dmitry Kazakov's avatar
Dmitry Kazakov committed
324
325
326
void KisMask::testingInitSelection(const QRect &rect)
{
    m_d->selection = new KisSelection();
327
    m_d->selection->pixelSelection()->select(rect, OPACITY_OPAQUE_U8);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
328
    m_d->selection->updateProjection(rect);
329
    m_d->selection->setParentNode(this);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
330
331
}

Halla Rempt's avatar
Halla Rempt committed
332
#include "kis_mask.moc"