2
* Copyright (c) 2005 Michael Thaler
3
* Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
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.
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.
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.
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)
24
#include "kis_convolution_painter.h"
25
#include "kis_convolution_kernel.h"
27
class KisSelectionFilter
30
virtual ~KisSelectionFilter() {}
32
virtual void process(KisPixelSelectionSP pixelSelection,
33
const QRect &rect) = 0;
35
virtual QString name() {return QString();}
36
virtual QRect changeRect(const QRect &rect) {return rect;}
39
void computeBorder(qint32 *circ, qint32 xradius, qint32 yradius) {
41
qint32 diameter = xradius * 2 + 1;
44
for (i = 0; i < diameter; i++) {
46
tmp = (i - xradius) - 0.5;
48
tmp = (xradius - i) - 0.5;
52
circ[i] = (qint32) RINT(yradius / (double) xradius * sqrt(xradius * xradius - tmp * tmp));
56
void rotatePointers(quint8 **p, quint32 n) {
59
for (i = 0; i < n - 1; i++) {
65
void computeTransition(quint8* transition, quint8** buf, qint32 width) {
69
if (buf[1][x] > 127 && (buf[0][x] < 128 || buf[2][x] < 128))
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)
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)
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)
107
class KisErodeSelectionFilter : public KisSelectionFilter
110
QString name() {return i18n("Erode Selection");}
112
QRect changeRect(const QRect &rect) {
113
const qint32 radius = 1;
114
return rect.adjusted(-radius, -radius, radius, radius);
117
void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
118
// Erode (radius 1 pixel) a mask (1bpp)
121
qint32 width = rect.width();
122
qint32 height = rect.height();
124
quint8* out = new quint8[width];
125
for (qint32 i = 0; i < 3; i++)
126
buf[i] = new quint8[width + 2];
130
pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1);
132
buf[0][0] = buf[0][1];
133
buf[0][width + 1] = buf[0][width];
135
memcpy(buf[1], buf[0], width + 2);
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);
141
buf[2][0] = buf[2][1];
142
buf[2][width + 1] = buf[2][width];
144
memcpy(buf[2], buf[1], width + 2);
147
for (qint32 x = 0 ; x < width; x++) {
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];
159
pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1);
160
rotatePointers(buf, 3);
163
for (qint32 i = 0; i < 3; i++)
169
class KisDilateSelectionFilter : public KisSelectionFilter
172
QString name() {return i18n("Dilate Selection");}
174
QRect changeRect(const QRect &rect) {
175
const qint32 radius = 1;
176
return rect.adjusted(-radius, -radius, radius, radius);
179
void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
180
// dilate (radius 1 pixel) a mask (1bpp)
183
qint32 width = rect.width();
184
qint32 height = rect.height();
186
quint8* out = new quint8[width];
187
for (qint32 i = 0; i < 3; i++)
188
buf[i] = new quint8[width + 2];
192
pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1);
194
buf[0][0] = buf[0][1];
195
buf[0][width + 1] = buf[0][width];
197
memcpy(buf[1], buf[0], width + 2);
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);
203
buf[2][0] = buf[2][1];
204
buf[2][width + 1] = buf[2][width];
206
memcpy(buf[2], buf[1], width + 2);
209
for (qint32 x = 0 ; x < width; x++) {
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];
221
pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1);
222
rotatePointers(buf, 3);
225
for (qint32 i = 0; i < 3; i++)
231
class KisBorderSelectionFilter : public KisSelectionFilter
234
KisBorderSelectionFilter(qint32 xRadius, qint32 yRadius)
235
: m_xRadius(xRadius),
240
QString name() {return i18n("Border Selection");}
242
QRect changeRect(const QRect &rect) {
243
return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius);
246
void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
247
if (m_xRadius <= 0 || m_yRadius <= 0) return;
253
if (m_xRadius == 1 && m_yRadius == 1) {
254
// optimize this case specifically
257
for (qint32 i = 0; i < 3; i++)
258
source[i] = new quint8[rect.width()];
260
quint8* transition = new quint8[rect.width()];
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);
267
memcpy(source[2], source[1], rect.width());
269
computeTransition(transition, source, rect.width());
270
pixelSelection->writeBytes(transition, rect.x(), rect.y(), rect.width(), 1);
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);
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);
282
for (qint32 i = 0; i < 3; i++)
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;
293
for (qint32 i = 0; i < 3; i++)
294
buf[i] = new quint8[rect.width()];
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;
302
quint8* out = new quint8[rect.width()];
303
density = new quint8*[2 * m_xRadius + 1];
304
density += m_xRadius;
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];
311
for (qint32 x = 0; x < (m_xRadius + 1); x++) { // compute density[][]
312
double tmpx, tmpy, dist;
322
for (qint32 y = 0; y < (m_yRadius + 1); y++) {
329
dist = ((tmpy * tmpy) / (m_yRadius * m_yRadius) +
330
(tmpx * tmpx) / (m_xRadius * m_xRadius));
332
a = (quint8)(255 * (1.0 - sqrt(dist)));
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);
346
memcpy(buf[2], buf[1], rect.width());
347
computeTransition(transition[1], buf, rect.width());
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());
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]) {
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());
369
memcpy(transition[m_yRadius], transition[m_yRadius - 1], rect.width());
371
for (qint32 x = 0; x < rect.width(); x++) { // update max array
373
if (max[x] <= -m_yRadius) {
374
if (transition[m_yRadius][x])
378
} else if (transition[-max[x]][x])
380
else if (transition[-max[x] + 1][x])
381
max[x] = -max[x] + 1;
386
if (max[x] < -m_yRadius - 1)
387
max[x] = -m_yRadius - 1;
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
393
if (last_index >= 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]];
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]];
412
for (i = x + 1; i < rect.width(); i++) {
413
if (max[i] >= -m_yRadius)
416
if (i - x > m_xRadius) {
417
for (; x < i - m_xRadius; x++)
421
last_index = m_xRadius;
424
pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1);
428
for (qint32 i = 0; i < 3; i++)
434
for (qint32 i = 0; i < m_yRadius + 1; i++) {
435
transition[i] -= m_xRadius;
436
delete transition[i];
440
for (qint32 i = 0; i < m_xRadius + 1 ; i++) {
441
density[i] -= m_yRadius;
444
density -= m_xRadius;
453
class KisFeatherSelectionFilter : public KisSelectionFilter
456
KisFeatherSelectionFilter(qint32 radius)
461
QString name() {return i18n("Feather Selection");}
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);
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);
474
const qreal multiplicand = 1 / (2 * M_PI * m_radius * m_radius);
475
const qreal exponentMultiplicand = 1 / (2 * m_radius * m_radius);
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 );
482
KisConvolutionKernelSP kernelHoriz = KisConvolutionKernel::fromMatrix(gaussianMatrix, 0, gaussianMatrix.sum());
483
KisConvolutionKernelSP kernelVertical = KisConvolutionKernel::fromMatrix(gaussianMatrix.transpose(), 0, gaussianMatrix.sum());
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);
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();
501
class KisGrowSelectionFilter : public KisSelectionFilter
504
KisGrowSelectionFilter(qint32 xRadius, qint32 yRadius)
505
: m_xRadius(xRadius),
510
QString name() {return i18n("Grow Selection");}
512
QRect changeRect(const QRect &rect) {
513
return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius);
516
void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
517
if (m_xRadius <= 0 || m_yRadius <= 0) return;
520
* Much code resembles Shrink filter, so please fix bugs
524
quint8 **buf; // caches the region's pixel data
525
quint8 **max; // caches the largest values for each column
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()];
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++) {
536
else if (i < rect.width() + m_xRadius)
537
max[i] = &buffer[(m_yRadius + 1) * (i - m_xRadius)];
539
max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius - 1)];
541
for (qint32 j = 0; j < m_xRadius + 1; j++)
544
/* offset the max pointer by m_xRadius so the range of the array
545
is [-m_xRadius] to [region->w + m_xRadius] */
548
quint8* out = new quint8[ rect.width()]; // holds the new scan line we are computing
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);
553
/* offset the circ pointer by m_xRadius so the range of the array
554
is [-m_xRadius] to [m_xRadius] */
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);
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]);
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);
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]);
580
max[x][0] = buf[0][x];
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 */
586
if (last_index >= 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]];
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]];
609
pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1);
611
/* undo the offsets to the pointers so we can free the malloced memmory */
618
for (qint32 i = 0; i < m_yRadius + 1; i++)
629
class KisShrinkSelectionFilter : public KisSelectionFilter
632
KisShrinkSelectionFilter(qint32 xRadius, qint32 yRadius, bool edgeLock)
633
: m_xRadius(xRadius),
639
QString name() {return i18n("Shrink Selection");}
641
QRect changeRect(const QRect &rect) {
642
return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius);
645
void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
646
if (m_xRadius <= 0 || m_yRadius <= 0) return;
649
pretty much the same as fatten_region only different
650
blame all bugs in this function on jaycox@gimp.org
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
656
quint8 **buf; // caches the region's pixels
657
quint8 **max; // caches the smallest values for each column
658
qint32 last_max, last_index;
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()];
666
qint32 buffer_size = (rect.width() + 2 * m_xRadius + 1) * (m_yRadius + 1);
667
quint8* buffer = new quint8[buffer_size];
670
memset(buffer, 255, buffer_size);
672
memset(buffer, 0, buffer_size);
674
for (qint32 i = 0; i < rect.width() + 2 * m_xRadius; i++) {
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)];
683
max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius - 1)];
685
max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius)];
688
for (qint32 j = 0 ; j < m_xRadius + 1; j++) max[0][j] = 0;
690
// offset the max pointer by m_xRadius so the range of the array is [-m_xRadius] to [region->w + m_xRadius]
693
quint8* out = new quint8[rect.width()]; // holds the new scan line we are computing
695
qint32* circ = new qint32[2 * m_xRadius + 1]; // holds the y coords of the filter's mask
697
computeBorder(circ, m_xRadius, m_yRadius);
699
// offset the circ pointer by m_xRadius so the range of the array is [-m_xRadius] to [m_xRadius]
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);
706
memcpy(buf[0], buf[1], rect.width());
708
memset(buf[0], 0, rect.width());
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]);
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);
722
memcpy(buf[m_yRadius], buf[m_yRadius - 1], rect.width());
724
memset(buf[m_yRadius], 0, rect.width());
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]);
730
max[x][0] = buf[0][x];
732
last_max = max[0][circ[-1]];
735
for (qint32 x = 0 ; x < rect.width(); x++) { // render scan line
737
if (last_index >= 0) {
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]];
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]];
760
pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1);
763
// undo the offsets to the pointers so we can free the malloced memmory
770
for (qint32 i = 0; i < m_yRadius + 1; i++)
782
class KisSmoothSelectionFilter : public KisSelectionFilter
785
QString name() {return i18n("Smooth Selection");}
787
QRect changeRect(const QRect &rect) {
788
const qint32 radius = 1;
789
return rect.adjusted(-radius, -radius, radius, radius);
792
void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
793
// Simple convolution filter to smooth a mask (1bpp)
796
qint32 width = rect.width();
797
qint32 height = rect.height();
800
quint8* out = new quint8[width];
801
for (qint32 i = 0; i < 3; i++)
802
buf[i] = new quint8[width + 2];
806
pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1);
808
buf[0][0] = buf[0][1];
809
buf[0][width + 1] = buf[0][width];
811
memcpy(buf[1], buf[0], width + 2);
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);
817
buf[2][0] = buf[2][1];
818
buf[2][width + 1] = buf[2][width];
820
memcpy(buf[2], buf[1], width + 2);
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]);
831
pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1);
832
rotatePointers(buf, 3);
835
for (qint32 i = 0; i < 3; i++)
841
class KisInvertSelectionFilter : public KisSelectionFilter
843
QString name() {return i18n("Invert Selection");}
845
QRect changeRect(const QRect &rect) {
849
void process(KisPixelSelectionSP pixelSelection, const QRect &rect) {
851
pixelSelection->invert();