kis_convolution_painter.cc 14.9 KB
Newer Older
1
/*
2
 *  Copyright (c) 2005 Cyrille Berger <cberger@cberger.net>
3
4
5
6
7
8
9
10
11
12
13
14
15
 *
 *  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
16
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18
19
20
21
22
23
24
25
26
27
28
 */

#include <stdlib.h>
#include <string.h>
#include <cfloat>

#include "qbrush.h"
#include "qcolor.h"
#include "qfontinfo.h"
#include "qfontmetrics.h"
#include "qpen.h"
#include "qregion.h"
Laurent Montel's avatar
qt3to4    
Laurent Montel committed
29
#include "qmatrix.h"
30
#include <QImage>
31
#include <QMap>
32
33
34
#include <QPainter>
#include <QPixmap>
#include <QRect>
35
#include <QString>
Casper Boemann's avatar
Casper Boemann committed
36
#include <QVector>
37
38
39
40

#include <kdebug.h>
#include <klocale.h>

41
#include <QColor>
42
43
44
45
46
47

#include "kis_brush.h"
#include "kis_global.h"
#include "kis_image.h"
#include "kis_iterators_pixel.h"
#include "kis_layer.h"
48
#include "kis_paint_device.h"
49
50
51
#include "kis_painter.h"
#include "kis_pattern.h"
#include "kis_rect.h"
52
#include "KoColorSpace.h"
53
54
55
56
57
58
#include "kis_types.h"
#include "kis_vec.h"
#include "kis_selection.h"
#include "kis_convolution_painter.h"


59
KisKernelSP KisKernel::fromQImage(const QImage& img)
60
{
61
    KisKernelSP k = KisKernelSP(new KisKernel);
62
63
64
65
    k->width = img.width();
    k->height = img.height();
    k->offset = 0;
    uint count = k->width * k->height;
Laurent Montel's avatar
Laurent Montel committed
66
67
    k->data = new qint32[count];
    qint32* itData = k->data;
68
    const quint8* itImg = img.bits();
69
    k->factor = 0;
70
    for(uint i = 0; i < count; ++i , ++itData, itImg+=4)
71
    {
72
        *itData = 255 - ( *itImg + *(itImg+1) + *(itImg+2) ) / 3;
73
74
75
76
77
        k->factor += *itData;
    }
    return k;
}

Halla Rempt's avatar
Halla Rempt committed
78

Halla Rempt's avatar
Halla Rempt committed
79
KisConvolutionPainter::KisConvolutionPainter()
80
    : super()
Halla Rempt's avatar
Halla Rempt committed
81
{
82
83
}

84
KisConvolutionPainter::KisConvolutionPainter(KisPaintDeviceSP device) : super(device)
85
86
87
{
}

Laurent Montel's avatar
Laurent Montel committed
88
void KisConvolutionPainter::applyMatrix(KisKernelSP kernel, qint32 x, qint32 y, qint32 w, qint32 h,
89
                    KisConvolutionBorderOp borderOp,
90
                    KoChannelInfo::enumChannelFlags  channelFlags )
91
{
92
    // Make the area we cover as small as possible
93
    if (m_device->hasSelection()) {
94

Halla Rempt's avatar
Halla Rempt committed
95
        QRect r = m_device->selection()->selectedRect().intersect(QRect(x, y, w, h));
96
97
98
99
100
101
102
        x = r.x();
        y = r.y();
        w = r.width();
        h = r.height();

    }

Halla Rempt's avatar
Halla Rempt committed
103
    if ( w == 0 && h == 0 ) return;
104
    // Determine the kernel's extent from the center pixel
Laurent Montel's avatar
Laurent Montel committed
105
    qint32 kw, kh, khalfWidth, khalfHeight, xLastMinuskhw, yLastMinuskhh;
106
107
    kw = kernel->width;
    kh = kernel->height;
108
109
    khalfWidth = (kw - 1) / 2;
    khalfHeight = (kh - 1) / 2;
Halla Rempt's avatar
Halla Rempt committed
110

111
112
    xLastMinuskhw = x + (w - khalfWidth);
    yLastMinuskhh = y + (h - khalfHeight);
113

114
    // Don't try to convolve on an area smaller than the kernel, or with a kernel that is not square or has no center pixel.
115
    if (w < kw || h < kh || (kw&1) == 0 || (kh&1) == 0 || kernel->factor == 0 ) return;
Halla Rempt's avatar
Halla Rempt committed
116

117
118
    m_cancelRequested = false;
    int lastProgressPercent = 0;
119
    emit notifyProgress(0);
120

121
    KoColorSpace * cs = m_device->colorSpace();
Halla Rempt's avatar
Halla Rempt committed
122

123
124
125
126
127
    // Determine whether we convolve border pixels, or not.
    switch (borderOp) {
        case BORDER_DEFAULT_FILL :
            break;
        case BORDER_REPEAT:
128
129
130
            applyMatrixRepeat(kernel, x, y, w, h, channelFlags);
            return;
        case BORDER_WRAP:
131
132
        case BORDER_AVOID:
        default :
133
134
            x += khalfWidth;
            y += khalfHeight;
135
136
137
            w -= kw - 1;
            h -= kh - 1;
    }
Halla Rempt's avatar
Halla Rempt committed
138

139
140
    // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them in the colorstrategy.

141
142
143
144
145
    int cacheSize = kw * kh;
    int cdepth = cs -> pixelSize();
    quint8** pixelPtrCache = new quint8*[cacheSize];
    for (int i = 0; i < cacheSize; i++)
        pixelPtrCache[i] = new quint8[cdepth];
146
//     pixelPtrCache.fill(0);
147
148

    // row == the y position of the pixel we want to change in the paint device
149
    int row = y;
150

151
    for (; row < y + h; ++row) {
152
153

        // col = the x position of the pixel we want to change
Halla Rempt's avatar
Halla Rempt committed
154
155
        int col = x;

156
        KisHLineIteratorPixel hit = m_device->createHLineIterator(x, row, w);
157
        bool needFull = true;
158
        while (!hit.isDone()) {
Halla Rempt's avatar
Halla Rempt committed
159

Cyrille Berger's avatar
Cyrille Berger committed
160
161
162
163
164
165
            // Iterate over all contributing pixels that are covered by the kernel
            // krow = the y position in the kernel matrix
            if(needFull)
            {
                qint32 i = 0;
                for (qint32 krow = 0; krow <  kh; ++krow) {
166

Cyrille Berger's avatar
Cyrille Berger committed
167
168
169
                    // col - khalfWidth = the left starting point of the kernel as centered on our pixel
                    // krow - khalfHeight = the offset for the top of the kernel as centered on our pixel
                    // kw = the width of the kernel
170

Cyrille Berger's avatar
Cyrille Berger committed
171
                    // Fill the cache with pointers to the pixels under the kernel
172
                    KisHLineConstIteratorPixel kit = m_device->createHLineIterator(col - khalfWidth, (row - khalfHeight) + krow, kw);
173
                    while (!kit.isDone()) {
174
                        memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
175
                        ++kit;
Cyrille Berger's avatar
Cyrille Berger committed
176
                        ++i;
177
178
                    }
                }
Cyrille Berger's avatar
Cyrille Berger committed
179
180
181
182
                needFull = false;
                Q_ASSERT (i==kw*kh);
            } else {
                for (qint32 krow = 0; krow <  kh; ++krow) { // shift the cache to the left
183
184
185
186
187
                    quint8** d = pixelPtrCache + krow * kw;
                    //memmove( d, d + 1, (kw-1)*sizeof(quint8*));
                    for (int i = 0; i < (kw-1); i++) {
                        memcpy(d[i], d[i+1], cdepth);
                    }
Cyrille Berger's avatar
Cyrille Berger committed
188
189
                }
                qint32 i = kw - 1;
190
                KisVLineConstIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, row - khalfHeight, kh);
Cyrille Berger's avatar
Cyrille Berger committed
191
                while (!kit.isDone()) {
192
                    memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
Cyrille Berger's avatar
Cyrille Berger committed
193
194
195
196
197
                    ++kit;
                    i += kw;
                }
            }
            if (hit.isSelected()) {
198
                cs->convolveColors(pixelPtrCache, kernel->data, channelFlags, hit.rawData(), kernel->factor, kernel->offset, kw * kh);
199
//                 pixelPtrCache.fill(0);
200
201
202
203
204
205
            }
            ++col;
            ++hit;
        }

        int progressPercent = 100 - ((((y + h) - row) * 100) / h);
Halla Rempt's avatar
Halla Rempt committed
206

207
        if (progressPercent > lastProgressPercent) {
208
            emit notifyProgress(progressPercent);
209
210
211
            lastProgressPercent = progressPercent;

            if (m_cancelRequested) {
212
213
214
215
                for (int i = 0; i < cacheSize; i++)
                    delete[] pixelPtrCache[i];
                delete[] pixelPtrCache;

216
217
                return;
            }
Halla Rempt's avatar
Halla Rempt committed
218
219
        }

220
    }
221
222
223

    addDirtyRect(QRect(x, y, w, h));

224
    emit notifyProgressDone();
225
226
227
228

    for (int i = 0; i < cacheSize; i++)
        delete[] pixelPtrCache[i];
    delete[] pixelPtrCache;
229
}
230

Laurent Montel's avatar
Laurent Montel committed
231
void KisConvolutionPainter::applyMatrixRepeat(KisKernelSP kernel, qint32 x, qint32 y, qint32 w, qint32 h,
232
                           KoChannelInfo::enumChannelFlags channelFlags)
233
234
235
{
    int lastProgressPercent = 0;
    // Determine the kernel's extent from the center pixel
Laurent Montel's avatar
Laurent Montel committed
236
    qint32 kw, kh, khalfWidth, khalfHeight, xLastMinuskhw, yLastMinuskhh;
237
238
239
240
241
    kw = kernel->width;
    kh = kernel->height;
    khalfWidth = (kw - 1) / 2;
    khalfHeight = (kh - 1) / 2;

242
243
    xLastMinuskhw = x + (w - khalfWidth);
    yLastMinuskhh = y + (h - khalfHeight);
244

245
    KoColorSpace * cs = m_device->colorSpace();
246
247
248

    // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them in the colorstrategy.

249
250
251
252
253
    int cacheSize = kw * kh;
    int cdepth = cs -> pixelSize();
    quint8** pixelPtrCache = new quint8*[cacheSize];
    for (int i = 0; i < cacheSize; i++)
        pixelPtrCache[i] = new quint8[cdepth];
254

255
256
    // row == the y position of the pixel we want to change in the paint device
    int row = y;
257

258
259
260
261
262
    for (; row < y + h; ++row) {

        // col = the x position of the pixel we want to change
        int col = x;

263
        KisHLineIteratorPixel hit = m_device->createHLineIterator(x, row, w);
264
        bool needFull = true;
265
        
Laurent Montel's avatar
Laurent Montel committed
266
267
        qint32 itStart = row - khalfHeight;
        qint32 itH = kh;
268
269
270
271
        if(itStart < 0)
        {
            itH += itStart;
            itStart = 0;
272
        } else if(itStart + kh > yLastMinuskhh)
273
        {
274
            itH -= itStart + kh - yLastMinuskhh;
275
        }
276
        KisVLineConstIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, itStart, itH);
277
278
        while (!hit.isDone()) {

Cyrille Berger's avatar
Cyrille Berger committed
279
280
281
282
283
284
285
            // Iterate over all contributing pixels that are covered by the kernel
            // krow = the y position in the kernel matrix
            if(needFull) // The cache has not been fill, so we need to fill it
            {
                qint32 i = 0;
                qint32 krow = 0;
                if( row < khalfHeight )
286
                {
Cyrille Berger's avatar
Cyrille Berger committed
287
288
289
290
291
                    // We are just outside the layer, all the row in the cache will be identical
                    // so we need to create them only once, and then to copy them
                    if( x < khalfWidth)
                    { // the left pixels are outside of the layer, in the corner
                        qint32 kcol = 0;
292
                        KisHLineConstIteratorPixel kit = m_device->createHLineIterator(0, 0, kw);
Cyrille Berger's avatar
Cyrille Berger committed
293
294
                        for(; kcol < (khalfWidth - x) + 1; ++kcol)
                        { // First copy the address of the topleft pixel
295
                            memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth);
296
                        }
Cyrille Berger's avatar
Cyrille Berger committed
297
298
299
                        for(; kcol < kw; ++kcol)
                        { // Then copy the address of the rest of the line
                            ++kit;
300
                            memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth);
Cyrille Berger's avatar
Cyrille Berger committed
301
302
303
                        }
                    } else {
                        uint kcol = 0;
304
                        KisHLineConstIteratorPixel kit = m_device->createHLineIterator(col - khalfWidth, 0, kw);
Cyrille Berger's avatar
Cyrille Berger committed
305
                        while (!kit.isDone()) {
306
                            memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth);
Cyrille Berger's avatar
Cyrille Berger committed
307
308
                            ++kit;
                            ++kcol;
309
310
                        }
                    }
311
                    krow = 1; // we have already done the first krow
Cyrille Berger's avatar
Cyrille Berger committed
312
                    for(;krow < (khalfHeight - row); ++krow)
313
                    {
314
315
316
                        //    Copy the first line in the current line
                        for (int i = 0; i < kw; i++)
                            memcpy(pixelPtrCache[krow * kw + i], pixelPtrCache[i], cdepth);
317
                    }
Cyrille Berger's avatar
Cyrille Berger committed
318
319
320
321
322
323
324
325
                    i = krow * kw;
                }
                qint32 itH = kh;
                if(row + khalfHeight > yLastMinuskhh)
                {
                    itH += yLastMinuskhh - row - khalfHeight;
                }
                for (; krow <  itH; ++krow) {
326

Cyrille Berger's avatar
Cyrille Berger committed
327
328
329
                    // col - khalfWidth = the left starting point of the kernel as centered on our pixel
                    // krow - khalfHeight = the offset for the top of the kernel as centered on our pixel
                    // kw = the width of the kernel
330

Cyrille Berger's avatar
Cyrille Berger committed
331
332
333
334
335
336
337
338
                    // Fill the cache with pointers to the pixels under the kernel
                    qint32 itHStart = col - khalfWidth;
                    qint32 itW = kw;
                    if(itHStart < 0)
                    {
                        itW += itHStart;
                        itHStart = 0;
                    }
339
                    KisHLineConstIteratorPixel kit = m_device->createHLineIterator(itHStart, (row - khalfHeight) + krow, itW);
Cyrille Berger's avatar
Cyrille Berger committed
340
341
342
                    if( col < khalfWidth )
                    {
                        for(; i <  krow * kw + ( kw - itW ); i+= 1)
343
                        {
344
                            memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
345
346
                        }
                    }
Cyrille Berger's avatar
Cyrille Berger committed
347
                    while (!kit.isDone()) {
348
                        memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
Cyrille Berger's avatar
Cyrille Berger committed
349
350
                        ++kit;
                        ++i;
351
                    }
Cyrille Berger's avatar
Cyrille Berger committed
352
353
354
                }
                qint32 lastvalid = i - kw;
                for(; krow < kh; ++krow) {
355
356
357
358
                    // Copy the last valid line in the current line
                    for (int i = 0; i < kw; i++)
                        memcpy(pixelPtrCache[krow * kw + i], pixelPtrCache[lastvalid + i],
                               cdepth);
Cyrille Berger's avatar
Cyrille Berger committed
359
360
361
362
                }
                needFull = false;
            } else {
                for (qint32 krow = 0; krow <  kh; ++krow) { // shift the cache to the left
363
364
365
366
367
                    quint8** d = pixelPtrCache + krow * kw;
                    //memmove( d, d + 1, (kw-1)*sizeof(quint8*));
                    for (int i = 0; i < (kw-1); i++) {
                        memcpy(d[i], d[i+1], cdepth);
                    }
Cyrille Berger's avatar
Cyrille Berger committed
368
369
370
371
                }
                if(col < xLastMinuskhw)
                {
                    qint32 i = kw - 1;
372
//                         KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, itStart, itH, false);
Cyrille Berger's avatar
Cyrille Berger committed
373
374
375
376
                    kit.nextCol();
                    if( row < khalfHeight )
                    {
                        for(; i < (khalfHeight- row ) * kw; i+=kw)
377
                        {
378
                            memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
379
380
                        }
                    }
Cyrille Berger's avatar
Cyrille Berger committed
381
                    while (!kit.isDone()) {
382
                        memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
Cyrille Berger's avatar
Cyrille Berger committed
383
384
385
386
387
388
                        ++kit;
                        i += kw;
                    }
                    qint32 lastvalid = i - kw;
                    for(;i < kw*kh; i+=kw)
                    {
389
                        memcpy(pixelPtrCache[i], pixelPtrCache[lastvalid], cdepth);
Cyrille Berger's avatar
Cyrille Berger committed
390
                    }
391
                }
Cyrille Berger's avatar
Cyrille Berger committed
392
393
            }
            if (hit.isSelected()) {
394
                cs->convolveColors(pixelPtrCache, kernel->data, channelFlags, hit.rawData(), kernel->factor, kernel->offset, kw * kh);
395
396
397
398
399
400
401
402
403
404
405
406
            }
            ++col;
            ++hit;
        }

        int progressPercent = 100 - ((((y + h) - row) * 100) / h);

        if (progressPercent > lastProgressPercent) {
            emit notifyProgress(progressPercent);
            lastProgressPercent = progressPercent;

            if (m_cancelRequested) {
407
408
409
410
                for (int i = 0; i < cacheSize; i++)
                    delete[] pixelPtrCache[i];
                delete[] pixelPtrCache;

411
412
413
414
415
416
417
418
419
                return;
            }
        }

    }

    addDirtyRect(QRect(x, y, w, h));

    emit notifyProgressDone();
420
421
422
423

    for (int i = 0; i < cacheSize; i++)
        delete[] pixelPtrCache[i];
    delete[] pixelPtrCache;
424
}