~smartboyhw/ubuntu/raring/calligra/2.6.0-0ubuntu1

« back to all changes in this revision

Viewing changes to krita/image/kis_selection_manager_p.h

  • Committer: Package Import Robot
  • Author(s): Philip Muškovac
  • Date: 2012-10-23 21:09:16 UTC
  • mfrom: (1.1.13)
  • Revision ID: package-import@ubuntu.com-20121023210916-m82w6zxnxhaxz7va
Tags: 1:2.5.90-0ubuntu1
* New upstream alpha release (LP: #1070436)
  - Add libkactivities-dev and libopenimageio-dev to build-depends
  - Add kubuntu_build_calligraactive.diff to build calligraactive by default
  - Add package for calligraauthor and move files that are shared between
    calligrawords and calligraauthor to calligrawords-common
* Document the patches
* Remove numbers from patches so they follow the same naming scheme as
  the rest of our patches.
* calligra-data breaks replaces krita-data (<< 1:2.5.3) (LP: #1071686)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  Copyright (c) 2005 Michael Thaler
 
3
 *  Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
 
4
 *
 
5
 *  This program is free software; you can redistribute it and/or modify
 
6
 *  it under the terms of the GNU General Public License as published by
 
7
 *  the Free Software Foundation; either version 2 of the License, or
 
8
 *  (at your option) any later version.
 
9
 *
 
10
 *  This program is distributed in the hope that it will be useful,
 
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 *  GNU General Public License for more details.
 
14
 *
 
15
 *  You should have received a copy of the GNU General Public License
 
16
 *  along with this program; if not, write to the Free Software
 
17
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
18
 */
 
19
 
 
20
#define MAX(a, b) ((a) > (b) ? (a) : (b))
 
21
#define MIN(a, b) ((a) < (b) ? (a) : (b))
 
22
#define RINT(x) floor ((x) + 0.5)
 
23
 
 
24
#include "kis_convolution_painter.h"
 
25
#include "kis_convolution_kernel.h"
 
26
 
 
27
class KisSelectionFilter
 
28
{
 
29
public:
 
30
    virtual ~KisSelectionFilter() {}
 
31
 
 
32
    virtual void process(KisPixelSelectionSP pixelSelection,
 
33
                         const QRect &rect) = 0;
 
34
 
 
35
    virtual QString name() {return QString();}
 
36
    virtual QRect changeRect(const QRect &rect) {return rect;}
 
37
 
 
38
protected:
 
39
    void computeBorder(qint32  *circ, qint32  xradius, qint32  yradius) {
 
40
        qint32 i;
 
41
        qint32 diameter = xradius * 2 + 1;
 
42
        double tmp;
 
43
 
 
44
        for (i = 0; i < diameter; i++) {
 
45
            if (i > xradius)
 
46
                tmp = (i - xradius) - 0.5;
 
47
            else if (i < xradius)
 
48
                tmp = (xradius - i) - 0.5;
 
49
            else
 
50
                tmp = 0.0;
 
51
 
 
52
            circ[i] = (qint32) RINT(yradius / (double) xradius * sqrt(xradius * xradius - tmp * tmp));
 
53
        }
 
54
    }
 
55
 
 
56
    void rotatePointers(quint8  **p, quint32 n) {
 
57
        quint32 i;
 
58
        quint8  *p0 = p[0];
 
59
        for (i = 0; i < n - 1; i++) {
 
60
            p[i] = p[i + 1];
 
61
        }
 
62
        p[i] = p0;
 
63
    }
 
64
 
 
65
    void computeTransition(quint8* transition, quint8** buf, qint32 width) {
 
66
        qint32 x = 0;
 
67
 
 
68
        if (width == 1) {
 
69
            if (buf[1][x] > 127 && (buf[0][x] < 128 || buf[2][x] < 128))
 
70
                transition[x] = 255;
 
71
            else
 
72
                transition[x] = 0;
 
73
            return;
 
74
        }
 
75
        if (buf[1][x] > 127) {
 
76
            if (buf[0][x] < 128 || buf[0][x + 1] < 128 ||
 
77
                buf[1][x + 1] < 128 ||
 
78
                buf[2][x] < 128 || buf[2][x + 1] < 128)
 
79
                transition[x] = 255;
 
80
            else
 
81
                transition[x] = 0;
 
82
        } else
 
83
            transition[x] = 0;
 
84
        for (qint32 x = 1; x < width - 1; x++) {
 
85
            if (buf[1][x] >= 128) {
 
86
                if (buf[0][x - 1] < 128 || buf[0][x] < 128 || buf[0][x + 1] < 128 ||
 
87
                    buf[1][x - 1] < 128           ||          buf[1][x + 1] < 128 ||
 
88
                    buf[2][x - 1] < 128 || buf[2][x] < 128 || buf[2][x + 1] < 128)
 
89
                    transition[x] = 255;
 
90
                else
 
91
                    transition[x] = 0;
 
92
            } else
 
93
                transition[x] = 0;
 
94
        }
 
95
        if (buf[1][x] >= 128) {
 
96
            if (buf[0][x - 1] < 128 || buf[0][x] < 128 ||
 
97
                buf[1][x - 1] < 128 ||
 
98
                buf[2][x - 1] < 128 || buf[2][x] < 128)
 
99
                transition[x] = 255;
 
100
            else
 
101
                transition[x] = 0;
 
102
        } else
 
103
            transition[x] = 0;
 
104
    }
 
105
};
 
106
 
 
107
class KisErodeSelectionFilter : public KisSelectionFilter
 
108
{
 
109
public:
 
110
    QString name() {return i18n("Erode Selection");}
 
111
 
 
112
    QRect changeRect(const QRect &rect) {
 
113
        const qint32 radius = 1;
 
114
        return rect.adjusted(-radius, -radius, radius, radius);
 
115
    }
 
116
 
 
117
    void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
 
118
        // Erode (radius 1 pixel) a mask (1bpp)
 
119
        quint8* buf[3];
 
120
 
 
121
        qint32 width = rect.width();
 
122
        qint32 height = rect.height();
 
123
 
 
124
        quint8* out = new quint8[width];
 
125
        for (qint32 i = 0; i < 3; i++)
 
126
            buf[i] = new quint8[width + 2];
 
127
 
 
128
 
 
129
        // load top of image
 
130
        pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1);
 
131
 
 
132
        buf[0][0]         = buf[0][1];
 
133
        buf[0][width + 1] = buf[0][width];
 
134
 
 
135
        memcpy(buf[1], buf[0], width + 2);
 
136
 
 
137
        for (qint32 y = 0; y < height; y++) {
 
138
            if (y + 1 < height) {
 
139
                pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1);
 
140
 
 
141
                buf[2][0]         = buf[2][1];
 
142
                buf[2][width + 1] = buf[2][width];
 
143
            } else {
 
144
                memcpy(buf[2], buf[1], width + 2);
 
145
            }
 
146
 
 
147
            for (qint32 x = 0 ; x < width; x++) {
 
148
                qint32 min = 255;
 
149
 
 
150
                if (buf[0][x+1] < min) min = buf[0][x+1];
 
151
                if (buf[1][x]   < min) min = buf[1][x];
 
152
                if (buf[1][x+1] < min) min = buf[1][x+1];
 
153
                if (buf[1][x+2] < min) min = buf[1][x+2];
 
154
                if (buf[2][x+1] < min) min = buf[2][x+1];
 
155
 
 
156
                out[x] = min;
 
157
            }
 
158
 
 
159
            pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1);
 
160
            rotatePointers(buf, 3);
 
161
        }
 
162
 
 
163
        for (qint32 i = 0; i < 3; i++)
 
164
            delete[] buf[i];
 
165
        delete[] out;
 
166
    }
 
167
};
 
168
 
 
169
class KisDilateSelectionFilter : public KisSelectionFilter
 
170
{
 
171
public:
 
172
    QString name() {return i18n("Dilate Selection");}
 
173
 
 
174
    QRect changeRect(const QRect &rect) {
 
175
        const qint32 radius = 1;
 
176
        return rect.adjusted(-radius, -radius, radius, radius);
 
177
    }
 
178
 
 
179
    void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
 
180
        // dilate (radius 1 pixel) a mask (1bpp)
 
181
        quint8* buf[3];
 
182
 
 
183
        qint32 width = rect.width();
 
184
        qint32 height = rect.height();
 
185
 
 
186
        quint8* out = new quint8[width];
 
187
        for (qint32 i = 0; i < 3; i++)
 
188
            buf[i] = new quint8[width + 2];
 
189
 
 
190
 
 
191
        // load top of image
 
192
        pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1);
 
193
 
 
194
        buf[0][0]         = buf[0][1];
 
195
        buf[0][width + 1] = buf[0][width];
 
196
 
 
197
        memcpy(buf[1], buf[0], width + 2);
 
198
 
 
199
        for (qint32 y = 0; y < height; y++) {
 
200
            if (y + 1 < height) {
 
201
                pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1);
 
202
 
 
203
                buf[2][0]         = buf[2][1];
 
204
                buf[2][width + 1] = buf[2][width];
 
205
            } else {
 
206
                memcpy(buf[2], buf[1], width + 2);
 
207
            }
 
208
 
 
209
            for (qint32 x = 0 ; x < width; x++) {
 
210
                qint32 max = 0;
 
211
 
 
212
                if (buf[0][x+1] > max) max = buf[0][x+1];
 
213
                if (buf[1][x]   > max) max = buf[1][x];
 
214
                if (buf[1][x+1] > max) max = buf[1][x+1];
 
215
                if (buf[1][x+2] > max) max = buf[1][x+2];
 
216
                if (buf[2][x+1] > max) max = buf[2][x+1];
 
217
 
 
218
                out[x] = max;
 
219
            }
 
220
 
 
221
            pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1);
 
222
            rotatePointers(buf, 3);
 
223
        }
 
224
 
 
225
        for (qint32 i = 0; i < 3; i++)
 
226
            delete[] buf[i];
 
227
        delete[] out;
 
228
    }
 
229
};
 
230
 
 
231
class KisBorderSelectionFilter : public KisSelectionFilter
 
232
{
 
233
public:
 
234
    KisBorderSelectionFilter(qint32 xRadius, qint32 yRadius)
 
235
        : m_xRadius(xRadius),
 
236
          m_yRadius(yRadius)
 
237
    {
 
238
    }
 
239
 
 
240
    QString name() {return i18n("Border Selection");}
 
241
 
 
242
    QRect changeRect(const QRect &rect) {
 
243
        return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius);
 
244
    }
 
245
 
 
246
    void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
 
247
        if (m_xRadius <= 0 || m_yRadius <= 0) return;
 
248
 
 
249
        quint8  *buf[3];
 
250
        quint8 **density;
 
251
        quint8 **transition;
 
252
 
 
253
        if (m_xRadius == 1 && m_yRadius == 1) {
 
254
            // optimize this case specifically
 
255
            quint8* source[3];
 
256
 
 
257
            for (qint32 i = 0; i < 3; i++)
 
258
                source[i] = new quint8[rect.width()];
 
259
 
 
260
            quint8* transition = new quint8[rect.width()];
 
261
 
 
262
            pixelSelection->readBytes(source[0], rect.x(), rect.y(), rect.width(), 1);
 
263
            memcpy(source[1], source[0], rect.width());
 
264
            if (rect.height() > 1)
 
265
                pixelSelection->readBytes(source[2], rect.x(), rect.y() + 1, rect.width(), 1);
 
266
            else
 
267
                memcpy(source[2], source[1], rect.width());
 
268
 
 
269
            computeTransition(transition, source, rect.width());
 
270
            pixelSelection->writeBytes(transition, rect.x(), rect.y(), rect.width(), 1);
 
271
 
 
272
            for (qint32 y = 1; y < rect.height(); y++) {
 
273
                rotatePointers(source, 3);
 
274
                if (y + 1 < rect.height())
 
275
                    pixelSelection->readBytes(source[2], rect.x(), rect.y() + y + 1, rect.width(), 1);
 
276
                else
 
277
                    memcpy(source[2], source[1], rect.width());
 
278
                computeTransition(transition, source, rect.width());
 
279
                pixelSelection->writeBytes(transition, rect.x(), rect.y() + y, rect.width(), 1);
 
280
            }
 
281
 
 
282
            for (qint32 i = 0; i < 3; i++)
 
283
                delete[] source[i];
 
284
            delete[] transition;
 
285
            return;
 
286
        }
 
287
 
 
288
        qint32* max = new qint32[rect.width() + 2 * m_xRadius];
 
289
        for (qint32 i = 0; i < (rect.width() + 2 * m_xRadius); i++)
 
290
            max[i] = m_yRadius + 2;
 
291
        max += m_xRadius;
 
292
 
 
293
        for (qint32 i = 0; i < 3; i++)
 
294
            buf[i] = new quint8[rect.width()];
 
295
 
 
296
        transition = new quint8*[m_yRadius + 1];
 
297
        for (qint32 i = 0; i < m_yRadius + 1; i++) {
 
298
            transition[i] = new quint8[rect.width() + 2 * m_xRadius];
 
299
            memset(transition[i], 0, rect.width() + 2 * m_xRadius);
 
300
            transition[i] += m_xRadius;
 
301
        }
 
302
        quint8* out = new quint8[rect.width()];
 
303
        density = new quint8*[2 * m_xRadius + 1];
 
304
        density += m_xRadius;
 
305
 
 
306
        for (qint32 x = 0; x < (m_xRadius + 1); x++) { // allocate density[][]
 
307
            density[ x]  = new quint8[2 * m_yRadius + 1];
 
308
            density[ x] += m_yRadius;
 
309
            density[-x]  = density[x];
 
310
        }
 
311
        for (qint32 x = 0; x < (m_xRadius + 1); x++) { // compute density[][]
 
312
            double tmpx, tmpy, dist;
 
313
            quint8 a;
 
314
 
 
315
            if (x > 0)
 
316
                tmpx = x - 0.5;
 
317
            else if (x < 0)
 
318
                tmpx = x + 0.5;
 
319
            else
 
320
                tmpx = 0.0;
 
321
 
 
322
            for (qint32 y = 0; y < (m_yRadius + 1); y++) {
 
323
                if (y > 0)
 
324
                    tmpy = y - 0.5;
 
325
                else if (y < 0)
 
326
                    tmpy = y + 0.5;
 
327
                else
 
328
                    tmpy = 0.0;
 
329
                dist = ((tmpy * tmpy) / (m_yRadius * m_yRadius) +
 
330
                        (tmpx * tmpx) / (m_xRadius * m_xRadius));
 
331
                if (dist < 1.0)
 
332
                    a = (quint8)(255 * (1.0 - sqrt(dist)));
 
333
                else
 
334
                    a = 0;
 
335
                density[ x][ y] = a;
 
336
                density[ x][-y] = a;
 
337
                density[-x][ y] = a;
 
338
                density[-x][-y] = a;
 
339
            }
 
340
        }
 
341
        pixelSelection->readBytes(buf[0], rect.x(), rect.y(), rect.width(), 1);
 
342
        memcpy(buf[1], buf[0], rect.width());
 
343
        if (rect.height() > 1)
 
344
            pixelSelection->readBytes(buf[2], rect.x(), rect.y() + 1, rect.width(), 1);
 
345
        else
 
346
            memcpy(buf[2], buf[1], rect.width());
 
347
        computeTransition(transition[1], buf, rect.width());
 
348
 
 
349
        for (qint32 y = 1; y < m_yRadius && y + 1 < rect.height(); y++) { // set up top of image
 
350
            rotatePointers(buf, 3);
 
351
            pixelSelection->readBytes(buf[2], rect.x(), rect.y() + y + 1, rect.width(), 1);
 
352
            computeTransition(transition[y + 1], buf, rect.width());
 
353
        }
 
354
        for (qint32 x = 0; x < rect.width(); x++) { // set up max[] for top of image
 
355
            max[x] = -(m_yRadius + 7);
 
356
            for (qint32 j = 1; j < m_yRadius + 1; j++)
 
357
                if (transition[j][x]) {
 
358
                    max[x] = j;
 
359
                    break;
 
360
                }
 
361
        }
 
362
        for (qint32 y = 0; y < rect.height(); y++) { // main calculation loop
 
363
            rotatePointers(buf, 3);
 
364
            rotatePointers(transition, m_yRadius + 1);
 
365
            if (y < rect.height() - (m_yRadius + 1)) {
 
366
                pixelSelection->readBytes(buf[2], rect.x(), rect.y() + y + m_yRadius + 1, rect.width(), 1);
 
367
                computeTransition(transition[m_yRadius], buf, rect.width());
 
368
            } else
 
369
                memcpy(transition[m_yRadius], transition[m_yRadius - 1], rect.width());
 
370
 
 
371
            for (qint32 x = 0; x < rect.width(); x++) { // update max array
 
372
                if (max[x] < 1) {
 
373
                    if (max[x] <= -m_yRadius) {
 
374
                        if (transition[m_yRadius][x])
 
375
                            max[x] = m_yRadius;
 
376
                        else
 
377
                            max[x]--;
 
378
                    } else if (transition[-max[x]][x])
 
379
                        max[x] = -max[x];
 
380
                    else if (transition[-max[x] + 1][x])
 
381
                        max[x] = -max[x] + 1;
 
382
                    else
 
383
                        max[x]--;
 
384
                } else
 
385
                    max[x]--;
 
386
                if (max[x] < -m_yRadius - 1)
 
387
                    max[x] = -m_yRadius - 1;
 
388
            }
 
389
            quint8 last_max =  max[0][density[-1]];
 
390
            qint32 last_index = 1;
 
391
            for (qint32 x = 0 ; x < rect.width(); x++) { // render scan line
 
392
                last_index--;
 
393
                if (last_index >= 0) {
 
394
                    last_max = 0;
 
395
                    for (qint32 i = m_xRadius; i >= 0; i--)
 
396
                        if (max[x + i] <= m_yRadius && max[x + i] >= -m_yRadius && density[i][max[x+i]] > last_max) {
 
397
                            last_max = density[i][max[x + i]];
 
398
                            last_index = i;
 
399
                        }
 
400
                    out[x] = last_max;
 
401
                } else {
 
402
                    last_max = 0;
 
403
                    for (qint32 i = m_xRadius; i >= -m_xRadius; i--)
 
404
                        if (max[x + i] <= m_yRadius && max[x + i] >= -m_yRadius && density[i][max[x + i]] > last_max) {
 
405
                            last_max = density[i][max[x + i]];
 
406
                            last_index = i;
 
407
                        }
 
408
                    out[x] = last_max;
 
409
                }
 
410
                if (last_max == 0) {
 
411
                    qint32 i;
 
412
                    for (i = x + 1; i < rect.width(); i++) {
 
413
                        if (max[i] >= -m_yRadius)
 
414
                            break;
 
415
                    }
 
416
                    if (i - x > m_xRadius) {
 
417
                        for (; x < i - m_xRadius; x++)
 
418
                            out[x] = 0;
 
419
                        x--;
 
420
                    }
 
421
                    last_index = m_xRadius;
 
422
                }
 
423
            }
 
424
            pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1);
 
425
        }
 
426
        delete [] out;
 
427
 
 
428
        for (qint32 i = 0; i < 3; i++)
 
429
            delete buf[i];
 
430
 
 
431
        max -= m_xRadius;
 
432
        delete[] max;
 
433
 
 
434
        for (qint32 i = 0; i < m_yRadius + 1; i++) {
 
435
            transition[i] -= m_xRadius;
 
436
            delete transition[i];
 
437
        }
 
438
        delete[] transition;
 
439
 
 
440
        for (qint32 i = 0; i < m_xRadius + 1 ; i++) {
 
441
            density[i] -= m_yRadius;
 
442
            delete density[i];
 
443
        }
 
444
        density -= m_xRadius;
 
445
        delete[] density;
 
446
    }
 
447
 
 
448
private:
 
449
    qint32 m_xRadius;
 
450
    qint32 m_yRadius;
 
451
};
 
452
 
 
453
class KisFeatherSelectionFilter : public KisSelectionFilter
 
454
{
 
455
public:
 
456
    KisFeatherSelectionFilter(qint32 radius)
 
457
        : m_radius(radius)
 
458
    {
 
459
    }
 
460
 
 
461
    QString name() {return i18n("Feather Selection");}
 
462
 
 
463
    QRect changeRect(const QRect &rect) {
 
464
        // multiply by 2 due to BORDER_AVOID flag
 
465
        return rect.adjusted(-2 * m_radius, -2 * m_radius,
 
466
                             2 * m_radius, 2 * m_radius);
 
467
    }
 
468
 
 
469
    void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
 
470
        // compute horizontal kernel
 
471
        const uint kernelSize = m_radius * 2 + 1;
 
472
        Matrix<qreal, Dynamic, Dynamic> gaussianMatrix(1, kernelSize);
 
473
 
 
474
        const qreal multiplicand = 1 / (2 * M_PI * m_radius * m_radius);
 
475
        const qreal exponentMultiplicand = 1 / (2 * m_radius * m_radius);
 
476
 
 
477
        for (uint x = 0; x < kernelSize; x++) {
 
478
            uint xDistance = qAbs((int)m_radius - (int)x);
 
479
            gaussianMatrix(0, x) = multiplicand * exp( -(qreal)((xDistance * xDistance) + (m_radius * m_radius)) * exponentMultiplicand );
 
480
        }
 
481
 
 
482
        KisConvolutionKernelSP kernelHoriz = KisConvolutionKernel::fromMatrix(gaussianMatrix, 0, gaussianMatrix.sum());
 
483
        KisConvolutionKernelSP kernelVertical = KisConvolutionKernel::fromMatrix(gaussianMatrix.transpose(), 0, gaussianMatrix.sum());
 
484
 
 
485
        KisPaintDeviceSP interm = new KisPaintDevice(pixelSelection->colorSpace());
 
486
        KisConvolutionPainter horizPainter(interm);
 
487
        horizPainter.setChannelFlags(interm->colorSpace()->channelFlags(false, true));
 
488
        horizPainter.applyMatrix(kernelHoriz, pixelSelection, rect.topLeft(), rect.topLeft(), rect.size(), BORDER_AVOID);
 
489
        horizPainter.end();
 
490
 
 
491
        KisConvolutionPainter verticalPainter(pixelSelection);
 
492
        verticalPainter.setChannelFlags(pixelSelection->colorSpace()->channelFlags(false, true));
 
493
        verticalPainter.applyMatrix(kernelVertical, interm, rect.topLeft(), rect.topLeft(), rect.size(), BORDER_AVOID);
 
494
        verticalPainter.end();
 
495
    }
 
496
 
 
497
private:
 
498
    qint32 m_radius;
 
499
};
 
500
 
 
501
class KisGrowSelectionFilter : public KisSelectionFilter
 
502
{
 
503
public:
 
504
    KisGrowSelectionFilter(qint32 xRadius, qint32 yRadius)
 
505
        : m_xRadius(xRadius),
 
506
          m_yRadius(yRadius)
 
507
    {
 
508
    }
 
509
 
 
510
    QString name() {return i18n("Grow Selection");}
 
511
 
 
512
    QRect changeRect(const QRect &rect) {
 
513
        return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius);
 
514
    }
 
515
 
 
516
    void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
 
517
        if (m_xRadius <= 0 || m_yRadius <= 0) return;
 
518
 
 
519
        /**
 
520
         * Much code resembles Shrink filter, so please fix bugs
 
521
         * in both filters
 
522
         */
 
523
 
 
524
        quint8  **buf;  // caches the region's pixel data
 
525
        quint8  **max;  // caches the largest values for each column
 
526
 
 
527
        max = new quint8* [rect.width() + 2 * m_xRadius];
 
528
        buf = new quint8* [m_yRadius + 1];
 
529
        for (qint32 i = 0; i < m_yRadius + 1; i++) {
 
530
            buf[i] = new quint8[rect.width()];
 
531
        }
 
532
        quint8* buffer = new quint8[(rect.width() + 2 * m_xRadius) *(m_yRadius + 1)];
 
533
        for (qint32 i = 0; i < rect.width() + 2 * m_xRadius; i++) {
 
534
            if (i < m_xRadius)
 
535
                max[i] = buffer;
 
536
            else if (i < rect.width() + m_xRadius)
 
537
                max[i] = &buffer[(m_yRadius + 1) * (i - m_xRadius)];
 
538
            else
 
539
                max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius - 1)];
 
540
 
 
541
            for (qint32 j = 0; j < m_xRadius + 1; j++)
 
542
                max[i][j] = 0;
 
543
        }
 
544
        /* offset the max pointer by m_xRadius so the range of the array
 
545
           is [-m_xRadius] to [region->w + m_xRadius] */
 
546
        max += m_xRadius;
 
547
 
 
548
        quint8* out = new quint8[ rect.width()];  // holds the new scan line we are computing
 
549
 
 
550
        qint32* circ = new qint32[ 2 * m_xRadius + 1 ]; // holds the y coords of the filter's mask
 
551
        computeBorder(circ, m_xRadius, m_yRadius);
 
552
 
 
553
        /* offset the circ pointer by m_xRadius so the range of the array
 
554
           is [-m_xRadius] to [m_xRadius] */
 
555
        circ += m_xRadius;
 
556
 
 
557
        memset(buf[0], 0, rect.width());
 
558
        for (qint32 i = 0; i < m_yRadius && i < rect.height(); i++) { // load top of image
 
559
            pixelSelection->readBytes(buf[i + 1], rect.x(), rect.y() + i, rect.width(), 1);
 
560
        }
 
561
 
 
562
        for (qint32 x = 0; x < rect.width() ; x++) { // set up max for top of image
 
563
            max[x][0] = 0;         // buf[0][x] is always 0
 
564
            max[x][1] = buf[1][x]; // MAX (buf[1][x], max[x][0]) always = buf[1][x]
 
565
            for (qint32 j = 2; j < m_yRadius + 1; j++) {
 
566
                max[x][j] = MAX(buf[j][x], max[x][j-1]);
 
567
            }
 
568
        }
 
569
 
 
570
        for (qint32 y = 0; y < rect.height(); y++) {
 
571
            rotatePointers(buf, m_yRadius + 1);
 
572
            if (y < rect.height() - (m_yRadius))
 
573
                pixelSelection->readBytes(buf[m_yRadius], rect.x(), rect.y() + y + m_yRadius, rect.width(), 1);
 
574
            else
 
575
                memset(buf[m_yRadius], 0, rect.width());
 
576
            for (qint32 x = 0; x < rect.width(); x++) { /* update max array */
 
577
                for (qint32 i = m_yRadius; i > 0; i--) {
 
578
                    max[x][i] = MAX(MAX(max[x][i - 1], buf[i - 1][x]), buf[i][x]);
 
579
                }
 
580
                max[x][0] = buf[0][x];
 
581
            }
 
582
            qint32 last_max = max[0][circ[-1]];
 
583
            qint32 last_index = 1;
 
584
            for (qint32 x = 0; x < rect.width(); x++) { /* render scan line */
 
585
                last_index--;
 
586
                if (last_index >= 0) {
 
587
                    if (last_max == 255)
 
588
                        out[x] = 255;
 
589
                    else {
 
590
                        last_max = 0;
 
591
                        for (qint32 i = m_xRadius; i >= 0; i--)
 
592
                            if (last_max < max[x + i][circ[i]]) {
 
593
                                last_max = max[x + i][circ[i]];
 
594
                                last_index = i;
 
595
                            }
 
596
                        out[x] = last_max;
 
597
                    }
 
598
                } else {
 
599
                    last_index = m_xRadius;
 
600
                    last_max = max[x + m_xRadius][circ[m_xRadius]];
 
601
                    for (qint32 i = m_xRadius - 1; i >= -m_xRadius; i--)
 
602
                        if (last_max < max[x + i][circ[i]]) {
 
603
                            last_max = max[x + i][circ[i]];
 
604
                            last_index = i;
 
605
                        }
 
606
                    out[x] = last_max;
 
607
                }
 
608
            }
 
609
            pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1);
 
610
        }
 
611
        /* undo the offsets to the pointers so we can free the malloced memmory */
 
612
        circ -= m_xRadius;
 
613
        max -= m_xRadius;
 
614
 
 
615
        delete[] circ;
 
616
        delete[] buffer;
 
617
        delete[] max;
 
618
        for (qint32 i = 0; i < m_yRadius + 1; i++)
 
619
            delete[] buf[i];
 
620
        delete[] buf;
 
621
        delete[] out;
 
622
    }
 
623
 
 
624
private:
 
625
    qint32 m_xRadius;
 
626
    qint32 m_yRadius;
 
627
};
 
628
 
 
629
class KisShrinkSelectionFilter : public KisSelectionFilter
 
630
{
 
631
public:
 
632
    KisShrinkSelectionFilter(qint32 xRadius, qint32 yRadius, bool edgeLock)
 
633
        : m_xRadius(xRadius),
 
634
          m_yRadius(yRadius),
 
635
          m_edgeLock(edgeLock)
 
636
    {
 
637
    }
 
638
 
 
639
    QString name() {return i18n("Shrink Selection");}
 
640
 
 
641
    QRect changeRect(const QRect &rect) {
 
642
        return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius);
 
643
    }
 
644
 
 
645
    void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
 
646
        if (m_xRadius <= 0 || m_yRadius <= 0) return;
 
647
 
 
648
        /*
 
649
          pretty much the same as fatten_region only different
 
650
          blame all bugs in this function on jaycox@gimp.org
 
651
        */
 
652
        /* If edge_lock is true  we assume that pixels outside the region
 
653
           we are passed are identical to the edge pixels.
 
654
           If edge_lock is false, we assume that pixels outside the region are 0
 
655
        */
 
656
        quint8  **buf;  // caches the region's pixels
 
657
        quint8  **max;  // caches the smallest values for each column
 
658
        qint32    last_max, last_index;
 
659
 
 
660
        max = new quint8* [rect.width() + 2 * m_xRadius];
 
661
        buf = new quint8* [m_yRadius + 1];
 
662
        for (qint32 i = 0; i < m_yRadius + 1; i++) {
 
663
            buf[i] = new quint8[rect.width()];
 
664
        }
 
665
 
 
666
        qint32 buffer_size = (rect.width() + 2 * m_xRadius + 1) * (m_yRadius + 1);
 
667
        quint8* buffer = new quint8[buffer_size];
 
668
 
 
669
        if (m_edgeLock)
 
670
            memset(buffer, 255, buffer_size);
 
671
        else
 
672
            memset(buffer, 0, buffer_size);
 
673
 
 
674
        for (qint32 i = 0; i < rect.width() + 2 * m_xRadius; i++) {
 
675
            if (i < m_xRadius)
 
676
                if (m_edgeLock)
 
677
                    max[i] = buffer;
 
678
                else
 
679
                    max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius)];
 
680
            else if (i < rect.width() + m_xRadius)
 
681
                max[i] = &buffer[(m_yRadius + 1) * (i - m_xRadius)];
 
682
            else if (m_edgeLock)
 
683
                max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius - 1)];
 
684
            else
 
685
                max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius)];
 
686
        }
 
687
        if (!m_edgeLock)
 
688
            for (qint32 j = 0 ; j < m_xRadius + 1; j++) max[0][j] = 0;
 
689
 
 
690
        // offset the max pointer by m_xRadius so the range of the array is [-m_xRadius] to [region->w + m_xRadius]
 
691
        max += m_xRadius;
 
692
 
 
693
        quint8* out = new quint8[rect.width()]; // holds the new scan line we are computing
 
694
 
 
695
        qint32* circ = new qint32[2 * m_xRadius + 1]; // holds the y coords of the filter's mask
 
696
 
 
697
        computeBorder(circ, m_xRadius, m_yRadius);
 
698
 
 
699
        // offset the circ pointer by m_xRadius so the range of the array is [-m_xRadius] to [m_xRadius]
 
700
        circ += m_xRadius;
 
701
 
 
702
        for (qint32 i = 0; i < m_yRadius && i < rect.height(); i++) // load top of image
 
703
            pixelSelection->readBytes(buf[i + 1], rect.x(), rect.y() + i, rect.width(), 1);
 
704
 
 
705
        if (m_edgeLock)
 
706
            memcpy(buf[0], buf[1], rect.width());
 
707
        else
 
708
            memset(buf[0], 0, rect.width());
 
709
 
 
710
 
 
711
        for (qint32 x = 0; x < rect.width(); x++) { // set up max for top of image
 
712
            max[x][0] = buf[0][x];
 
713
            for (qint32 j = 1; j < m_yRadius + 1; j++)
 
714
                max[x][j] = MIN(buf[j][x], max[x][j-1]);
 
715
        }
 
716
 
 
717
        for (qint32 y = 0; y < rect.height(); y++) {
 
718
            rotatePointers(buf, m_yRadius + 1);
 
719
            if (y < rect.height() - m_yRadius)
 
720
                pixelSelection->readBytes(buf[m_yRadius], rect.x(), rect.y() + y + m_yRadius, rect.width(), 1);
 
721
            else if (m_edgeLock)
 
722
                memcpy(buf[m_yRadius], buf[m_yRadius - 1], rect.width());
 
723
            else
 
724
                memset(buf[m_yRadius], 0, rect.width());
 
725
 
 
726
            for (qint32 x = 0 ; x < rect.width(); x++) { // update max array
 
727
                for (qint32 i = m_yRadius; i > 0; i--) {
 
728
                    max[x][i] = MIN(MIN(max[x][i - 1], buf[i - 1][x]), buf[i][x]);
 
729
                }
 
730
                max[x][0] = buf[0][x];
 
731
            }
 
732
            last_max =  max[0][circ[-1]];
 
733
            last_index = 0;
 
734
 
 
735
            for (qint32 x = 0 ; x < rect.width(); x++) { // render scan line
 
736
                last_index--;
 
737
                if (last_index >= 0) {
 
738
                    if (last_max == 0)
 
739
                        out[x] = 0;
 
740
                    else {
 
741
                        last_max = 255;
 
742
                        for (qint32 i = m_xRadius; i >= 0; i--)
 
743
                            if (last_max > max[x + i][circ[i]]) {
 
744
                                last_max = max[x + i][circ[i]];
 
745
                                last_index = i;
 
746
                            }
 
747
                        out[x] = last_max;
 
748
                    }
 
749
                } else {
 
750
                    last_index = m_xRadius;
 
751
                    last_max = max[x + m_xRadius][circ[m_xRadius]];
 
752
                    for (qint32 i = m_xRadius - 1; i >= -m_xRadius; i--)
 
753
                        if (last_max > max[x + i][circ[i]]) {
 
754
                            last_max = max[x + i][circ[i]];
 
755
                            last_index = i;
 
756
                        }
 
757
                    out[x] = last_max;
 
758
                }
 
759
            }
 
760
            pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1);
 
761
        }
 
762
 
 
763
        // undo the offsets to the pointers so we can free the malloced memmory
 
764
        circ -= m_xRadius;
 
765
        max -= m_xRadius;
 
766
 
 
767
        delete[] circ;
 
768
        delete[] buffer;
 
769
        delete[] max;
 
770
        for (qint32 i = 0; i < m_yRadius + 1; i++)
 
771
            delete buf[i];
 
772
        delete[] buf;
 
773
        delete[] out;
 
774
    }
 
775
 
 
776
private:
 
777
    qint32 m_xRadius;
 
778
    qint32 m_yRadius;
 
779
    qint32 m_edgeLock;
 
780
};
 
781
 
 
782
class KisSmoothSelectionFilter : public KisSelectionFilter
 
783
{
 
784
public:
 
785
    QString name() {return i18n("Smooth Selection");}
 
786
 
 
787
    QRect changeRect(const QRect &rect) {
 
788
        const qint32 radius = 1;
 
789
        return rect.adjusted(-radius, -radius, radius, radius);
 
790
    }
 
791
 
 
792
    void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
 
793
        // Simple convolution filter to smooth a mask (1bpp)
 
794
        quint8      *buf[3];
 
795
 
 
796
        qint32 width = rect.width();
 
797
        qint32 height = rect.height();
 
798
 
 
799
 
 
800
        quint8* out = new quint8[width];
 
801
        for (qint32 i = 0; i < 3; i++)
 
802
            buf[i] = new quint8[width + 2];
 
803
 
 
804
 
 
805
        // load top of image
 
806
        pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1);
 
807
 
 
808
        buf[0][0]         = buf[0][1];
 
809
        buf[0][width + 1] = buf[0][width];
 
810
 
 
811
        memcpy(buf[1], buf[0], width + 2);
 
812
 
 
813
        for (qint32 y = 0; y < height; y++) {
 
814
            if (y + 1 < height) {
 
815
                pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1);
 
816
 
 
817
                buf[2][0]         = buf[2][1];
 
818
                buf[2][width + 1] = buf[2][width];
 
819
            } else {
 
820
                memcpy(buf[2], buf[1], width + 2);
 
821
            }
 
822
 
 
823
            for (qint32 x = 0 ; x < width; x++) {
 
824
                qint32 value = (buf[0][x] + buf[0][x+1] + buf[0][x+2] +
 
825
                                buf[1][x] + buf[2][x+1] + buf[1][x+2] +
 
826
                                buf[2][x] + buf[1][x+1] + buf[2][x+2]);
 
827
 
 
828
                out[x] = value / 9;
 
829
            }
 
830
 
 
831
            pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1);
 
832
            rotatePointers(buf, 3);
 
833
        }
 
834
 
 
835
        for (qint32 i = 0; i < 3; i++)
 
836
            delete[] buf[i];
 
837
        delete[] out;
 
838
    }
 
839
};
 
840
 
 
841
class KisInvertSelectionFilter : public KisSelectionFilter
 
842
{
 
843
    QString name() {return i18n("Invert Selection");}
 
844
 
 
845
    QRect changeRect(const QRect &rect) {
 
846
        Q_UNUSED(rect);
 
847
        return QRect();
 
848
    }
 
849
    void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
 
850
        Q_UNUSED(rect);
 
851
        pixelSelection->invert();
 
852
    }
 
853
};
 
854