File indexing completed on 2024-05-19 04:26:34

0001 /*
0002  *  SPDX-FileCopyrightText: 2005 Michael Thaler
0003  *  SPDX-FileCopyrightText: 2011 Dmitry Kazakov <dimula73@gmail.com>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "kis_selection_filters.h"
0009 
0010 #include <algorithm>
0011 
0012 #include <klocalizedstring.h>
0013 
0014 #include <KoColorSpace.h>
0015 #include "kis_convolution_painter.h"
0016 #include "kis_convolution_kernel.h"
0017 #include "kis_pixel_selection.h"
0018 #include <kis_sequential_iterator.h>
0019 
0020 #define MAX(a, b) ((a) > (b) ? (a) : (b))
0021 #define MIN(a, b) ((a) < (b) ? (a) : (b))
0022 #define RINT(x) floor ((x) + 0.5)
0023 
0024 KisSelectionFilter::~KisSelectionFilter()
0025 {
0026 }
0027 
0028 KUndo2MagicString KisSelectionFilter::name()
0029 {
0030     return KUndo2MagicString();
0031 }
0032 
0033 QRect KisSelectionFilter::changeRect(const QRect &rect, KisDefaultBoundsBaseSP defaultBounds)
0034 {
0035     Q_UNUSED(defaultBounds);
0036     return rect;
0037 }
0038 
0039 void KisSelectionFilter::computeBorder(qint32* circ, qint32 xradius, qint32 yradius)
0040 {
0041     qint32 i;
0042     qint32 diameter = xradius * 2 + 1;
0043     double tmp;
0044 
0045     for (i = 0; i < diameter; i++) {
0046         if (i > xradius)
0047             tmp = (i - xradius) - 0.5;
0048         else if (i < xradius)
0049             tmp = (xradius - i) - 0.5;
0050         else
0051             tmp = 0.0;
0052 
0053         double divisor = (double) xradius;
0054         if (divisor == 0.0) {
0055             divisor = 1.0;
0056         }
0057         circ[i] = (qint32) RINT(yradius * sqrt(xradius * xradius - tmp * tmp) / divisor);
0058     }
0059 }
0060 
0061 void KisSelectionFilter::rotatePointers(quint8** p, quint32 n)
0062 {
0063     quint32 i;
0064     quint8  *p0 = p[0];
0065     for (i = 0; i < n - 1; i++) {
0066         p[i] = p[i + 1];
0067     }
0068     p[i] = p0;
0069 }
0070 
0071 void KisSelectionFilter::computeTransition(quint8* transition, quint8** buf, qint32 width)
0072 {
0073     qint32 x = 0;
0074 
0075     if (width == 1) {
0076         if (buf[1][x] > 127 && (buf[0][x] < 128 || buf[2][x] < 128))
0077             transition[x] = 255;
0078         else
0079             transition[x] = 0;
0080         return;
0081     }
0082     if (buf[1][x] > 127) {
0083         if (buf[0][x] < 128 || buf[0][x + 1] < 128 ||
0084             buf[1][x + 1] < 128 ||
0085             buf[2][x] < 128 || buf[2][x + 1] < 128)
0086             transition[x] = 255;
0087         else
0088             transition[x] = 0;
0089     } else
0090         transition[x] = 0;
0091     for (qint32 x = 1; x < width - 1; x++) {
0092         if (buf[1][x] >= 128) {
0093             if (buf[0][x - 1] < 128 || buf[0][x] < 128 || buf[0][x + 1] < 128 ||
0094                 buf[1][x - 1] < 128           ||          buf[1][x + 1] < 128 ||
0095                 buf[2][x - 1] < 128 || buf[2][x] < 128 || buf[2][x + 1] < 128)
0096                 transition[x] = 255;
0097             else
0098                 transition[x] = 0;
0099         } else
0100             transition[x] = 0;
0101     }
0102     if (buf[1][x] >= 128) {
0103         if (buf[0][x - 1] < 128 || buf[0][x] < 128 ||
0104             buf[1][x - 1] < 128 ||
0105             buf[2][x - 1] < 128 || buf[2][x] < 128)
0106             transition[x] = 255;
0107         else
0108             transition[x] = 0;
0109     } else
0110         transition[x] = 0;
0111 }
0112 
0113 
0114 KUndo2MagicString KisErodeSelectionFilter::name()
0115 {
0116     return kundo2_i18n("Erode Selection");
0117 }
0118 
0119 QRect KisErodeSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds)
0120 {
0121     Q_UNUSED(defaultBounds);
0122 
0123     const qint32 radius = 1;
0124     return rect.adjusted(-radius, -radius, radius, radius);
0125 }
0126 
0127 void KisErodeSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0128 {
0129     // Erode (radius 1 pixel) a mask (1bpp)
0130     quint8* buf[3];
0131 
0132     qint32 width = rect.width();
0133     qint32 height = rect.height();
0134 
0135     quint8* out = new quint8[width];
0136     for (qint32 i = 0; i < 3; i++)
0137         buf[i] = new quint8[width + 2];
0138 
0139 
0140     // load top of image
0141     pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1);
0142 
0143     buf[0][0]         = buf[0][1];
0144     buf[0][width + 1] = buf[0][width];
0145 
0146     memcpy(buf[1], buf[0], width + 2);
0147 
0148     for (qint32 y = 0; y < height; y++) {
0149         if (y + 1 < height) {
0150             pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1);
0151 
0152             buf[2][0]         = buf[2][1];
0153             buf[2][width + 1] = buf[2][width];
0154         } else {
0155             memcpy(buf[2], buf[1], width + 2);
0156         }
0157 
0158         for (qint32 x = 0 ; x < width; x++) {
0159             qint32 min = 255;
0160 
0161             if (buf[0][x+1] < min) min = buf[0][x+1];
0162             if (buf[1][x]   < min) min = buf[1][x];
0163             if (buf[1][x+1] < min) min = buf[1][x+1];
0164             if (buf[1][x+2] < min) min = buf[1][x+2];
0165             if (buf[2][x+1] < min) min = buf[2][x+1];
0166 
0167             out[x] = min;
0168         }
0169 
0170         pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1);
0171         rotatePointers(buf, 3);
0172     }
0173 
0174     for (qint32 i = 0; i < 3; i++)
0175         delete[] buf[i];
0176     delete[] out;
0177 }
0178 
0179 
0180 KUndo2MagicString KisDilateSelectionFilter::name()
0181 {
0182     return kundo2_i18n("Dilate Selection");
0183 }
0184 
0185 QRect KisDilateSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds)
0186 {
0187     Q_UNUSED(defaultBounds);
0188 
0189     const qint32 radius = 1;
0190     return rect.adjusted(-radius, -radius, radius, radius);
0191 }
0192 
0193 void KisDilateSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0194  {
0195     // dilate (radius 1 pixel) a mask (1bpp)
0196     quint8* buf[3];
0197 
0198     qint32 width = rect.width();
0199     qint32 height = rect.height();
0200 
0201     quint8* out = new quint8[width];
0202     for (qint32 i = 0; i < 3; i++)
0203         buf[i] = new quint8[width + 2];
0204 
0205 
0206     // load top of image
0207     pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1);
0208 
0209     buf[0][0]         = buf[0][1];
0210     buf[0][width + 1] = buf[0][width];
0211 
0212     memcpy(buf[1], buf[0], width + 2);
0213 
0214     for (qint32 y = 0; y < height; y++) {
0215         if (y + 1 < height) {
0216             pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1);
0217 
0218             buf[2][0]         = buf[2][1];
0219             buf[2][width + 1] = buf[2][width];
0220         } else {
0221             memcpy(buf[2], buf[1], width + 2);
0222         }
0223 
0224         for (qint32 x = 0 ; x < width; x++) {
0225             qint32 max = 0;
0226 
0227             if (buf[0][x+1] > max) max = buf[0][x+1];
0228             if (buf[1][x]   > max) max = buf[1][x];
0229             if (buf[1][x+1] > max) max = buf[1][x+1];
0230             if (buf[1][x+2] > max) max = buf[1][x+2];
0231             if (buf[2][x+1] > max) max = buf[2][x+1];
0232 
0233             out[x] = max;
0234         }
0235 
0236         pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1);
0237         rotatePointers(buf, 3);
0238     }
0239 
0240     for (qint32 i = 0; i < 3; i++)
0241         delete[] buf[i];
0242     delete[] out;
0243 }
0244 
0245 
0246 KisBorderSelectionFilter::KisBorderSelectionFilter(qint32 xRadius, qint32 yRadius, bool antialiasing)
0247   : m_xRadius(xRadius),
0248     m_yRadius(yRadius),
0249     m_antialiasing(antialiasing)
0250 {
0251 }
0252 
0253 KUndo2MagicString KisBorderSelectionFilter::name()
0254 {
0255     return kundo2_i18n("Border Selection");
0256 }
0257 
0258 QRect KisBorderSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds)
0259 {
0260     Q_UNUSED(defaultBounds);
0261 
0262     return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius);
0263 }
0264 
0265 void KisBorderSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0266 {
0267     if (m_xRadius <= 0 || m_yRadius <= 0) return;
0268 
0269     quint8  *buf[3];
0270     quint8 **density;
0271     quint8 **transition;
0272 
0273     if (m_xRadius == 1 && m_yRadius == 1) {
0274         // optimize this case specifically
0275         quint8* source[3];
0276 
0277         for (qint32 i = 0; i < 3; i++)
0278             source[i] = new quint8[rect.width()];
0279 
0280         quint8* transition = new quint8[rect.width()];
0281 
0282         pixelSelection->readBytes(source[0], rect.x(), rect.y(), rect.width(), 1);
0283         memcpy(source[1], source[0], rect.width());
0284         if (rect.height() > 1)
0285             pixelSelection->readBytes(source[2], rect.x(), rect.y() + 1, rect.width(), 1);
0286         else
0287             memcpy(source[2], source[1], rect.width());
0288 
0289         computeTransition(transition, source, rect.width());
0290         pixelSelection->writeBytes(transition, rect.x(), rect.y(), rect.width(), 1);
0291 
0292         for (qint32 y = 1; y < rect.height(); y++) {
0293             rotatePointers(source, 3);
0294             if (y + 1 < rect.height())
0295                 pixelSelection->readBytes(source[2], rect.x(), rect.y() + y + 1, rect.width(), 1);
0296             else
0297                 memcpy(source[2], source[1], rect.width());
0298             computeTransition(transition, source, rect.width());
0299             pixelSelection->writeBytes(transition, rect.x(), rect.y() + y, rect.width(), 1);
0300         }
0301 
0302         for (qint32 i = 0; i < 3; i++)
0303             delete[] source[i];
0304         delete[] transition;
0305         return;
0306     }
0307 
0308     qint32* max = new qint32[rect.width() + 2 * m_xRadius];
0309     for (qint32 i = 0; i < (rect.width() + 2 * m_xRadius); i++)
0310         max[i] = m_yRadius + 2;
0311     max += m_xRadius;
0312 
0313     for (qint32 i = 0; i < 3; i++)
0314         buf[i] = new quint8[rect.width()];
0315 
0316     transition = new quint8*[m_yRadius + 1];
0317     for (qint32 i = 0; i < m_yRadius + 1; i++) {
0318         transition[i] = new quint8[rect.width() + 2 * m_xRadius];
0319         memset(transition[i], 0, rect.width() + 2 * m_xRadius);
0320         transition[i] += m_xRadius;
0321     }
0322     quint8* out = new quint8[rect.width()];
0323     density = new quint8*[2 * m_xRadius + 1];
0324     density += m_xRadius;
0325 
0326     for (qint32 x = 0; x < (m_xRadius + 1); x++) { // allocate density[][]
0327         density[ x]  = new quint8[2 * m_yRadius + 1];
0328         density[ x] += m_yRadius;
0329         density[-x]  = density[x];
0330     }
0331 
0332     // compute density[][]
0333     if (m_antialiasing) {
0334         KIS_SAFE_ASSERT_RECOVER_NOOP(m_xRadius == m_yRadius && "anisotropic fading is not implemented");
0335         const qreal maxRadius = 0.5 * (m_xRadius + m_yRadius);
0336         const qreal minRadius = maxRadius - 1.0;
0337 
0338         for (qint32 x = 0; x < (m_xRadius + 1); x++) {
0339             double dist;
0340             quint8 a;
0341 
0342             for (qint32 y = 0; y < (m_yRadius + 1); y++) {
0343 
0344                 dist = sqrt(pow2(x) + pow2(y));
0345 
0346                 if (dist > maxRadius) {
0347                     a = 0;
0348                 } else if (dist > minRadius) {
0349                     a = qRound((1.0 - dist + minRadius) * 255.0);
0350                 } else {
0351                     a = 255;
0352                 }
0353 
0354                 density[ x][ y] = a;
0355                 density[ x][-y] = a;
0356                 density[-x][ y] = a;
0357                 density[-x][-y] = a;
0358             }
0359         }
0360 
0361     } else {
0362         for (qint32 x = 0; x < (m_xRadius + 1); x++) {
0363             double tmpx, tmpy, dist;
0364             quint8 a;
0365 
0366             tmpx = x > 0.0 ? x - 0.5 : 0.0;
0367 
0368             for (qint32 y = 0; y < (m_yRadius + 1); y++) {
0369                 tmpy = y > 0.0 ? y - 0.5 : 0.0;
0370 
0371                 dist = (pow2(tmpy) / pow2(m_yRadius) +
0372                         pow2(tmpx) / pow2(m_xRadius));
0373 
0374                 a = dist <= 1.0 ? 255 : 0;
0375 
0376                 density[ x][ y] = a;
0377                 density[ x][-y] = a;
0378                 density[-x][ y] = a;
0379                 density[-x][-y] = a;
0380             }
0381         }
0382     }
0383 
0384     pixelSelection->readBytes(buf[0], rect.x(), rect.y(), rect.width(), 1);
0385     memcpy(buf[1], buf[0], rect.width());
0386     if (rect.height() > 1)
0387         pixelSelection->readBytes(buf[2], rect.x(), rect.y() + 1, rect.width(), 1);
0388     else
0389         memcpy(buf[2], buf[1], rect.width());
0390     computeTransition(transition[1], buf, rect.width());
0391 
0392     for (qint32 y = 1; y < m_yRadius && y + 1 < rect.height(); y++) { // set up top of image
0393         rotatePointers(buf, 3);
0394         pixelSelection->readBytes(buf[2], rect.x(), rect.y() + y + 1, rect.width(), 1);
0395         computeTransition(transition[y + 1], buf, rect.width());
0396     }
0397     for (qint32 x = 0; x < rect.width(); x++) { // set up max[] for top of image
0398         max[x] = -(m_yRadius + 7);
0399         for (qint32 j = 1; j < m_yRadius + 1; j++)
0400             if (transition[j][x]) {
0401                 max[x] = j;
0402                 break;
0403             }
0404     }
0405     for (qint32 y = 0; y < rect.height(); y++) { // main calculation loop
0406         rotatePointers(buf, 3);
0407         rotatePointers(transition, m_yRadius + 1);
0408         if (y < rect.height() - (m_yRadius + 1)) {
0409             pixelSelection->readBytes(buf[2], rect.x(), rect.y() + y + m_yRadius + 1, rect.width(), 1);
0410             computeTransition(transition[m_yRadius], buf, rect.width());
0411         } else
0412             memcpy(transition[m_yRadius], transition[m_yRadius - 1], rect.width());
0413 
0414         for (qint32 x = 0; x < rect.width(); x++) { // update max array
0415             if (max[x] < 1) {
0416                 if (max[x] <= -m_yRadius) {
0417                     if (transition[m_yRadius][x])
0418                         max[x] = m_yRadius;
0419                     else
0420                         max[x]--;
0421                 } else if (transition[-max[x]][x])
0422                     max[x] = -max[x];
0423                 else if (transition[-max[x] + 1][x])
0424                     max[x] = -max[x] + 1;
0425                 else
0426                     max[x]--;
0427             } else
0428                 max[x]--;
0429             if (max[x] < -m_yRadius - 1)
0430                 max[x] = -m_yRadius - 1;
0431         }
0432         quint8 last_max =  max[0][density[-1]];
0433         qint32 last_index = 1;
0434         for (qint32 x = 0 ; x < rect.width(); x++) { // render scan line
0435             last_index--;
0436             if (last_index >= 0) {
0437                 last_max = 0;
0438                 for (qint32 i = m_xRadius; i >= 0; i--)
0439                     if (max[x + i] <= m_yRadius && max[x + i] >= -m_yRadius && density[i][max[x+i]] > last_max) {
0440                         last_max = density[i][max[x + i]];
0441                         last_index = i;
0442                     }
0443                 out[x] = last_max;
0444             } else {
0445                 last_max = 0;
0446                 for (qint32 i = m_xRadius; i >= -m_xRadius; i--)
0447                     if (max[x + i] <= m_yRadius && max[x + i] >= -m_yRadius && density[i][max[x + i]] > last_max) {
0448                         last_max = density[i][max[x + i]];
0449                         last_index = i;
0450                     }
0451                 out[x] = last_max;
0452             }
0453             if (last_max == 0) {
0454                 qint32 i;
0455                 for (i = x + 1; i < rect.width(); i++) {
0456                     if (max[i] >= -m_yRadius)
0457                         break;
0458                 }
0459                 if (i - x > m_xRadius) {
0460                     for (; x < i - m_xRadius; x++)
0461                         out[x] = 0;
0462                     x--;
0463                 }
0464                 last_index = m_xRadius;
0465             }
0466         }
0467         pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1);
0468     }
0469     delete [] out;
0470 
0471     for (qint32 i = 0; i < 3; i++)
0472         delete[] buf[i];
0473 
0474     max -= m_xRadius;
0475     delete[] max;
0476 
0477     for (qint32 i = 0; i < m_yRadius + 1; i++) {
0478         transition[i] -= m_xRadius;
0479         delete transition[i];
0480     }
0481     delete[] transition;
0482 
0483     for (qint32 i = 0; i < m_xRadius + 1 ; i++) {
0484         density[i] -= m_yRadius;
0485         delete density[i];
0486     }
0487     density -= m_xRadius;
0488     delete[] density;
0489 }
0490 
0491 
0492 KisFeatherSelectionFilter::KisFeatherSelectionFilter(qint32 radius)
0493     : m_radius(radius)
0494 {
0495 }
0496 
0497 KUndo2MagicString KisFeatherSelectionFilter::name()
0498 {
0499     return kundo2_i18n("Feather Selection");
0500 }
0501 
0502 QRect KisFeatherSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds)
0503 {
0504     Q_UNUSED(defaultBounds);
0505 
0506     return rect.adjusted(-m_radius, -m_radius,
0507                          m_radius, m_radius);
0508 }
0509 
0510 void KisFeatherSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0511 {
0512     // compute horizontal kernel
0513     const uint kernelSize = m_radius * 2 + 1;
0514     Eigen::Matrix<qreal, Eigen::Dynamic, Eigen::Dynamic> gaussianMatrix(1, kernelSize);
0515 
0516     const qreal multiplicand = 1.0 / (2.0 * M_PI * m_radius * m_radius);
0517     const qreal exponentMultiplicand = 1.0 / (2.0 * m_radius * m_radius);
0518 
0519     for (uint x = 0; x < kernelSize; x++) {
0520         uint xDistance = qAbs((int)m_radius - (int)x);
0521         gaussianMatrix(0, x) = multiplicand * exp( -(qreal)((xDistance * xDistance) + (m_radius * m_radius)) * exponentMultiplicand );
0522     }
0523 
0524     KisConvolutionKernelSP kernelHoriz = KisConvolutionKernel::fromMatrix(gaussianMatrix, 0, gaussianMatrix.sum());
0525     KisConvolutionKernelSP kernelVertical = KisConvolutionKernel::fromMatrix(gaussianMatrix.transpose(), 0, gaussianMatrix.sum());
0526 
0527     KisPaintDeviceSP interm = new KisPaintDevice(pixelSelection->colorSpace());
0528     interm->prepareClone(pixelSelection);
0529 
0530     KisConvolutionPainter horizPainter(interm);
0531     horizPainter.setChannelFlags(interm->colorSpace()->channelFlags(false, true));
0532     horizPainter.applyMatrix(kernelHoriz, pixelSelection, rect.topLeft(), rect.topLeft(), rect.size(), BORDER_REPEAT);
0533     horizPainter.end();
0534 
0535     KisConvolutionPainter verticalPainter(pixelSelection);
0536     verticalPainter.setChannelFlags(pixelSelection->colorSpace()->channelFlags(false, true));
0537     verticalPainter.applyMatrix(kernelVertical, interm, rect.topLeft(), rect.topLeft(), rect.size(), BORDER_REPEAT);
0538     verticalPainter.end();
0539 }
0540 
0541 
0542 KisGrowSelectionFilter::KisGrowSelectionFilter(qint32 xRadius, qint32 yRadius)
0543     : m_xRadius(xRadius)
0544     , m_yRadius(yRadius)
0545 {
0546 }
0547 
0548 KUndo2MagicString KisGrowSelectionFilter::name()
0549 {
0550     return kundo2_i18n("Grow Selection");
0551 }
0552 
0553 QRect KisGrowSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds)
0554 {
0555     Q_UNUSED(defaultBounds);
0556 
0557     return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius);
0558 }
0559 
0560 void KisGrowSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0561 {
0562     if (m_xRadius <= 0 || m_yRadius <= 0) return;
0563 
0564     /**
0565         * Much code resembles Shrink filter, so please fix bugs
0566         * in both filters
0567         */
0568 
0569     quint8  **buf;  // caches the region's pixel data
0570     quint8  **max;  // caches the largest values for each column
0571 
0572     max = new quint8* [rect.width() + 2 * m_xRadius];
0573     buf = new quint8* [m_yRadius + 1];
0574     for (qint32 i = 0; i < m_yRadius + 1; i++) {
0575         buf[i] = new quint8[rect.width()];
0576     }
0577     quint8* buffer = new quint8[(rect.width() + 2 * m_xRadius) *(m_yRadius + 1)];
0578     for (qint32 i = 0; i < rect.width() + 2 * m_xRadius; i++) {
0579         if (i < m_xRadius)
0580             max[i] = buffer;
0581         else if (i < rect.width() + m_xRadius)
0582             max[i] = &buffer[(m_yRadius + 1) * (i - m_xRadius)];
0583         else
0584             max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius - 1)];
0585 
0586         for (qint32 j = 0; j < m_xRadius + 1; j++)
0587             max[i][j] = 0;
0588     }
0589     /* offset the max pointer by m_xRadius so the range of the array
0590         is [-m_xRadius] to [region->w + m_xRadius] */
0591     max += m_xRadius;
0592 
0593     quint8* out = new quint8[ rect.width()];  // holds the new scan line we are computing
0594 
0595     qint32* circ = new qint32[ 2 * m_xRadius + 1 ]; // holds the y coords of the filter's mask
0596     computeBorder(circ, m_xRadius, m_yRadius);
0597 
0598     /* offset the circ pointer by m_xRadius so the range of the array
0599         is [-m_xRadius] to [m_xRadius] */
0600     circ += m_xRadius;
0601 
0602     memset(buf[0], 0, rect.width());
0603     for (qint32 i = 0; i < m_yRadius && i < rect.height(); i++) { // load top of image
0604         pixelSelection->readBytes(buf[i + 1], rect.x(), rect.y() + i, rect.width(), 1);
0605     }
0606 
0607     for (qint32 x = 0; x < rect.width() ; x++) { // set up max for top of image
0608         max[x][0] = 0;         // buf[0][x] is always 0
0609         max[x][1] = buf[1][x]; // MAX (buf[1][x], max[x][0]) always = buf[1][x]
0610         for (qint32 j = 2; j < m_yRadius + 1; j++) {
0611             max[x][j] = MAX(buf[j][x], max[x][j-1]);
0612         }
0613     }
0614 
0615     for (qint32 y = 0; y < rect.height(); y++) {
0616         rotatePointers(buf, m_yRadius + 1);
0617         if (y < rect.height() - (m_yRadius))
0618             pixelSelection->readBytes(buf[m_yRadius], rect.x(), rect.y() + y + m_yRadius, rect.width(), 1);
0619         else
0620             memset(buf[m_yRadius], 0, rect.width());
0621         for (qint32 x = 0; x < rect.width(); x++) { /* update max array */
0622             for (qint32 i = m_yRadius; i > 0; i--) {
0623                 max[x][i] = MAX(MAX(max[x][i - 1], buf[i - 1][x]), buf[i][x]);
0624             }
0625             max[x][0] = buf[0][x];
0626         }
0627         qint32 last_max = max[0][circ[-1]];
0628         qint32 last_index = 1;
0629         for (qint32 x = 0; x < rect.width(); x++) { /* render scan line */
0630             last_index--;
0631             if (last_index >= 0) {
0632                 if (last_max == 255)
0633                     out[x] = 255;
0634                 else {
0635                     last_max = 0;
0636                     for (qint32 i = m_xRadius; i >= 0; i--)
0637                         if (last_max < max[x + i][circ[i]]) {
0638                             last_max = max[x + i][circ[i]];
0639                             last_index = i;
0640                         }
0641                     out[x] = last_max;
0642                 }
0643             } else {
0644                 last_index = m_xRadius;
0645                 last_max = max[x + m_xRadius][circ[m_xRadius]];
0646                 for (qint32 i = m_xRadius - 1; i >= -m_xRadius; i--)
0647                     if (last_max < max[x + i][circ[i]]) {
0648                         last_max = max[x + i][circ[i]];
0649                         last_index = i;
0650                     }
0651                 out[x] = last_max;
0652             }
0653         }
0654         pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1);
0655     }
0656     /* undo the offsets to the pointers so we can free the malloced memory */
0657     circ -= m_xRadius;
0658     max -= m_xRadius;
0659 
0660     delete[] circ;
0661     delete[] buffer;
0662     delete[] max;
0663     for (qint32 i = 0; i < m_yRadius + 1; i++)
0664         delete[] buf[i];
0665     delete[] buf;
0666     delete[] out;
0667 }
0668 
0669 
0670 KisShrinkSelectionFilter::KisShrinkSelectionFilter(qint32 xRadius, qint32 yRadius, bool edgeLock)
0671     : m_xRadius(xRadius)
0672     , m_yRadius(yRadius)
0673     , m_edgeLock(edgeLock)
0674 {
0675 }
0676 
0677 KUndo2MagicString KisShrinkSelectionFilter::name()
0678 {
0679     return kundo2_i18n("Shrink Selection");
0680 }
0681 
0682 QRect KisShrinkSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds)
0683 {
0684     return m_edgeLock ? defaultBounds->imageBorderRect() : rect;
0685 }
0686 
0687 void KisShrinkSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0688 {
0689     if (m_xRadius <= 0 || m_yRadius <= 0) return;
0690 
0691     /*
0692         pretty much the same as fatten_region only different
0693         blame all bugs in this function on jaycox@gimp.org
0694     */
0695     /* If edge_lock is true  we assume that pixels outside the region
0696         we are passed are identical to the edge pixels.
0697         If edge_lock is false, we assume that pixels outside the region are 0
0698     */
0699     quint8  **buf;  // caches the region's pixels
0700     quint8  **max;  // caches the smallest values for each column
0701     qint32    last_max, last_index;
0702 
0703     max = new quint8* [rect.width() + 2 * m_xRadius];
0704     buf = new quint8* [m_yRadius + 1];
0705     for (qint32 i = 0; i < m_yRadius + 1; i++) {
0706         buf[i] = new quint8[rect.width()];
0707     }
0708 
0709     qint32 buffer_size = (rect.width() + 2 * m_xRadius + 1) * (m_yRadius + 1);
0710     quint8* buffer = new quint8[buffer_size];
0711 
0712     if (m_edgeLock)
0713         memset(buffer, 255, buffer_size);
0714     else
0715         memset(buffer, 0, buffer_size);
0716 
0717     for (qint32 i = 0; i < rect.width() + 2 * m_xRadius; i++) {
0718         if (i < m_xRadius)
0719             if (m_edgeLock)
0720                 max[i] = buffer;
0721             else
0722                 max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius)];
0723         else if (i < rect.width() + m_xRadius)
0724             max[i] = &buffer[(m_yRadius + 1) * (i - m_xRadius)];
0725         else if (m_edgeLock)
0726             max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius - 1)];
0727         else
0728             max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius)];
0729     }
0730     if (!m_edgeLock)
0731         for (qint32 j = 0 ; j < m_xRadius + 1; j++) max[0][j] = 0;
0732 
0733     // offset the max pointer by m_xRadius so the range of the array is [-m_xRadius] to [region->w + m_xRadius]
0734     max += m_xRadius;
0735 
0736     quint8* out = new quint8[rect.width()]; // holds the new scan line we are computing
0737 
0738     qint32* circ = new qint32[2 * m_xRadius + 1]; // holds the y coords of the filter's mask
0739 
0740     computeBorder(circ, m_xRadius, m_yRadius);
0741 
0742     // offset the circ pointer by m_xRadius so the range of the array is [-m_xRadius] to [m_xRadius]
0743     circ += m_xRadius;
0744 
0745     for (qint32 i = 0; i < m_yRadius && i < rect.height(); i++) // load top of image
0746         pixelSelection->readBytes(buf[i + 1], rect.x(), rect.y() + i, rect.width(), 1);
0747 
0748     if (m_edgeLock)
0749         memcpy(buf[0], buf[1], rect.width());
0750     else
0751         memset(buf[0], 0, rect.width());
0752 
0753 
0754     for (qint32 x = 0; x < rect.width(); x++) { // set up max for top of image
0755         max[x][0] = buf[0][x];
0756         for (qint32 j = 1; j < m_yRadius + 1; j++)
0757             max[x][j] = MIN(buf[j][x], max[x][j-1]);
0758     }
0759 
0760     for (qint32 y = 0; y < rect.height(); y++) {
0761         rotatePointers(buf, m_yRadius + 1);
0762         if (y < rect.height() - m_yRadius)
0763             pixelSelection->readBytes(buf[m_yRadius], rect.x(), rect.y() + y + m_yRadius, rect.width(), 1);
0764         else if (m_edgeLock)
0765             memcpy(buf[m_yRadius], buf[m_yRadius - 1], rect.width());
0766         else
0767             memset(buf[m_yRadius], 0, rect.width());
0768 
0769         for (qint32 x = 0 ; x < rect.width(); x++) { // update max array
0770             for (qint32 i = m_yRadius; i > 0; i--) {
0771                 max[x][i] = MIN(MIN(max[x][i - 1], buf[i - 1][x]), buf[i][x]);
0772             }
0773             max[x][0] = buf[0][x];
0774         }
0775         last_max =  max[0][circ[-1]];
0776         last_index = 0;
0777 
0778         for (qint32 x = 0 ; x < rect.width(); x++) { // render scan line
0779             last_index--;
0780             if (last_index >= 0) {
0781                 if (last_max == 0)
0782                     out[x] = 0;
0783                 else {
0784                     last_max = 255;
0785                     for (qint32 i = m_xRadius; i >= 0; i--)
0786                         if (last_max > max[x + i][circ[i]]) {
0787                             last_max = max[x + i][circ[i]];
0788                             last_index = i;
0789                         }
0790                     out[x] = last_max;
0791                 }
0792             } else {
0793                 last_index = m_xRadius;
0794                 last_max = max[x + m_xRadius][circ[m_xRadius]];
0795                 for (qint32 i = m_xRadius - 1; i >= -m_xRadius; i--)
0796                     if (last_max > max[x + i][circ[i]]) {
0797                         last_max = max[x + i][circ[i]];
0798                         last_index = i;
0799                     }
0800                 out[x] = last_max;
0801             }
0802         }
0803         pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1);
0804     }
0805 
0806     // undo the offsets to the pointers so we can free the malloced memory
0807     circ -= m_xRadius;
0808     max -= m_xRadius;
0809 
0810     delete[] circ;
0811     delete[] buffer;
0812     delete[] max;
0813     for (qint32 i = 0; i < m_yRadius + 1; i++)
0814         delete[] buf[i];
0815     delete[] buf;
0816     delete[] out;
0817 }
0818 
0819 
0820 KUndo2MagicString KisSmoothSelectionFilter::name()
0821 {
0822     return kundo2_i18n("Smooth Selection");
0823 }
0824 
0825 QRect KisSmoothSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds)
0826 {
0827     Q_UNUSED(defaultBounds);
0828 
0829     const qint32 radius = 1;
0830     return rect.adjusted(-radius, -radius, radius, radius);
0831 }
0832 
0833 void KisSmoothSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0834 {
0835     // Simple convolution filter to smooth a mask (1bpp)
0836     quint8      *buf[3];
0837 
0838     qint32 width = rect.width();
0839     qint32 height = rect.height();
0840 
0841 
0842     quint8* out = new quint8[width];
0843     for (qint32 i = 0; i < 3; i++)
0844         buf[i] = new quint8[width + 2];
0845 
0846 
0847     // load top of image
0848     pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1);
0849 
0850     buf[0][0]         = buf[0][1];
0851     buf[0][width + 1] = buf[0][width];
0852 
0853     memcpy(buf[1], buf[0], width + 2);
0854 
0855     for (qint32 y = 0; y < height; y++) {
0856         if (y + 1 < height) {
0857             pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1);
0858 
0859             buf[2][0]         = buf[2][1];
0860             buf[2][width + 1] = buf[2][width];
0861         } else {
0862             memcpy(buf[2], buf[1], width + 2);
0863         }
0864 
0865         for (qint32 x = 0 ; x < width; x++) {
0866             qint32 value = (buf[0][x] + buf[0][x+1] + buf[0][x+2] +
0867                             buf[1][x] + buf[2][x+1] + buf[1][x+2] +
0868                             buf[2][x] + buf[1][x+1] + buf[2][x+2]);
0869 
0870             out[x] = value / 9;
0871         }
0872 
0873         pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1);
0874         rotatePointers(buf, 3);
0875     }
0876 
0877     for (qint32 i = 0; i < 3; i++)
0878         delete[] buf[i];
0879     delete[] out;
0880 }
0881 
0882 
0883 KUndo2MagicString KisInvertSelectionFilter::name()
0884 {
0885     return kundo2_i18n("Invert Selection");
0886 }
0887 
0888 QRect KisInvertSelectionFilter::changeRect(const QRect &rect, KisDefaultBoundsBaseSP defaultBounds)
0889 {
0890     Q_UNUSED(rect);
0891     return defaultBounds->bounds();
0892 }
0893 
0894 void KisInvertSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0895 {
0896     Q_UNUSED(rect);
0897 
0898     const QRect imageRect = pixelSelection->defaultBounds()->bounds();
0899     const QRect selectionRect = pixelSelection->selectedExactRect();
0900 
0901     /**
0902      * A special treatment for the user-visible selection inversion:
0903      *
0904      * If the selection is fully contained inside the image, then
0905      * just invert it pixel-by-pixel without changing the default
0906      * pixel. It makes it selectedExactRect() work a little bit more
0907      * expected for the user (see bug 457820).
0908      *
0909      * If the selection spreads outside the image bounds, then
0910      * just invert it in a mathematical way adjusting the default
0911      * pixel.
0912      */
0913 
0914     if (!imageRect.contains(selectionRect)) {
0915         pixelSelection->invert();
0916     } else {
0917         KisSequentialIterator it(pixelSelection, imageRect);
0918         while(it.nextPixel()) {
0919             *(it.rawData()) = MAX_SELECTED - *(it.rawData());
0920         }
0921         pixelSelection->crop(imageRect);
0922         pixelSelection->invalidateOutlineCache();
0923     }
0924 }
0925 
0926 constexpr qint32 KisAntiAliasSelectionFilter::offsets[numSteps];
0927 
0928 KUndo2MagicString KisAntiAliasSelectionFilter::name()
0929 {
0930     return kundo2_i18n("Anti-Alias Selection");
0931 }
0932 
0933 bool KisAntiAliasSelectionFilter::getInterpolationValue(qint32 negativeSpanEndDistance,
0934                                                         qint32 positiveSpanEndDistance,
0935                                                         qint32 negativePixelDiff,
0936                                                         qint32 positivePixelDiff,
0937                                                         qint32 currentPixelDiff,
0938                                                         bool negativeSpanExtremeValid,
0939                                                         bool positiveSpanExtremeValid,
0940                                                         qint32 *interpolationValue) const
0941 {
0942     // Since we search a limited number of steps in each direction of the
0943     // current pixel, the end pixel of the span may still belong to the edge.
0944     // So we check for that, and if that's the case we must not smooth the
0945     // current pixel 
0946     const bool pixelDiffLessThanZero = currentPixelDiff < 0;
0947     quint32 distance;
0948     if (negativeSpanEndDistance < positiveSpanEndDistance) {
0949         if (!negativeSpanExtremeValid) {
0950             return false;
0951         }
0952         // The pixel is closer to the negative end
0953         const bool spanEndPixelDiffLessThanZero = negativePixelDiff < 0;
0954         if (pixelDiffLessThanZero == spanEndPixelDiffLessThanZero) {
0955             return false;
0956         }
0957         distance = negativeSpanEndDistance;
0958     } else {
0959         if (!positiveSpanExtremeValid) {
0960             return false;
0961         }
0962         // The pixel is closer to the positive end
0963         const bool spanEndPixelDiffLessThanZero = positivePixelDiff < 0;
0964         if (pixelDiffLessThanZero == spanEndPixelDiffLessThanZero) {
0965             return false;
0966         }
0967         distance = positiveSpanEndDistance;
0968     }
0969     const qint32 spanLength = positiveSpanEndDistance + negativeSpanEndDistance;
0970     *interpolationValue = ((distance << 8) / spanLength) + 128;
0971     return *interpolationValue >= 0;
0972 }
0973 
0974 void KisAntiAliasSelectionFilter::findSpanExtreme(quint8 **scanlines, qint32 x, qint32 pixelOffset,
0975                                                   qint32 rowMultiplier, qint32 colMultiplier, qint32 direction,
0976                                                   qint32 pixelAvg, qint32 scaledGradient, qint32 currentPixelDiff,
0977                                                   qint32 *spanEndDistance, qint32 *pixelDiff, bool *spanExtremeValid) const
0978 {
0979     *spanEndDistance = 0;
0980     *spanExtremeValid = true;
0981     for (qint32 i = 0; i < numSteps; ++i) {
0982         *spanEndDistance += offsets[i];
0983         const qint32 row1 = currentScanlineIndex + (direction * *spanEndDistance * rowMultiplier);
0984         const qint32 col1 = x + horizontalBorderSize + (direction * *spanEndDistance * colMultiplier);
0985         const qint32 row2 = row1 + pixelOffset * colMultiplier;
0986         const qint32 col2 = col1 + pixelOffset * rowMultiplier;
0987         const quint8 *pixel1 = scanlines[row1] + col1;
0988         const quint8 *pixel2 = scanlines[row2] + col2;
0989         // Get how different are these edge pixels from the current pixels and
0990         // stop searching if they are too different
0991         *pixelDiff = ((*pixel1 + *pixel2) >> 1) - pixelAvg;
0992         if (qAbs(*pixelDiff) > scaledGradient) {
0993             // If this is the end of the span then check if the corner belongs
0994             // to a jagged border or to a right angled part of the shape
0995             qint32 pixelDiff2;
0996             if ((currentPixelDiff < 0 && *pixelDiff < 0) || (currentPixelDiff > 0 && *pixelDiff > 0)) {
0997                 const qint32 row3 = row2 + pixelOffset * colMultiplier;
0998                 const qint32 col3 = col2 + pixelOffset * rowMultiplier;
0999                 const quint8 *pixel3 = scanlines[row3] + col3;
1000                 pixelDiff2 = ((*pixel2 + *pixel3) >> 1) - pixelAvg;
1001             } else {
1002                 const qint32 row3 = row1 - pixelOffset * colMultiplier;
1003                 const qint32 col3 = col1 - pixelOffset * rowMultiplier;
1004                 const quint8 *pixel3 = scanlines[row3] + col3;
1005                 pixelDiff2 = ((*pixel1 + *pixel3) >> 1) - pixelAvg;
1006             }
1007             *spanExtremeValid = !(qAbs(pixelDiff2) > scaledGradient);
1008             break;
1009         }
1010     }
1011 }
1012 
1013 void KisAntiAliasSelectionFilter::findSpanExtremes(quint8 **scanlines, qint32 x, qint32 pixelOffset,
1014                                                    qint32 rowMultiplier, qint32 colMultiplier,
1015                                                    qint32 pixelAvg, qint32 scaledGradient, qint32 currentPixelDiff,
1016                                                    qint32 *negativeSpanEndDistance, qint32 *positiveSpanEndDistance,
1017                                                    qint32 *negativePixelDiff, qint32 *positivePixelDiff,
1018                                                    bool *negativeSpanExtremeValid, bool *positiveSpanExtremeValid) const
1019 {
1020     findSpanExtreme(scanlines, x, pixelOffset, rowMultiplier, colMultiplier, -1, pixelAvg, scaledGradient,
1021                     currentPixelDiff, negativeSpanEndDistance, negativePixelDiff, negativeSpanExtremeValid);
1022     findSpanExtreme(scanlines, x, pixelOffset, rowMultiplier, colMultiplier, 1, pixelAvg, scaledGradient,
1023                     currentPixelDiff, positiveSpanEndDistance, positivePixelDiff, positiveSpanExtremeValid);
1024 }
1025 
1026 void KisAntiAliasSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect &rect)
1027 {
1028     const quint8 defaultPixel = *pixelSelection->defaultPixel().data();
1029     // Size of a scanline
1030     const quint32 bytesPerScanline = rect.width() + 2 * horizontalBorderSize;
1031     // Size of a scanline padded to a multiple of 8
1032     const quint32 bytesPerPaddedScanline = ((bytesPerScanline + 7) / 8) * 8;
1033 
1034     // This buffer contains the number of consecutive scanlines needed to
1035     // process the current scanline
1036     QVector<quint8> buffer(bytesPerPaddedScanline * numberOfScanlines);
1037 
1038     // These pointers point to the individual scanlines in the buffer
1039     quint8 *scanlines[numberOfScanlines];
1040     for (quint32 i = 0; i < numberOfScanlines; ++i) {
1041         scanlines[i] = buffer.data() + i * bytesPerPaddedScanline;
1042     }
1043 
1044     // Initialize the scanlines
1045     // Set the border scanlines on the top
1046     for (qint32 i = 0; i < verticalBorderSize; ++i) {
1047         memset(scanlines[i], defaultPixel, bytesPerScanline);
1048     }
1049     // Copy the first scanlines of the image
1050     const quint32 numberOfFirstRows = qMin(rect.height(), numberOfScanlines - verticalBorderSize);
1051     for (quint32 i = verticalBorderSize; i < verticalBorderSize + numberOfFirstRows; ++i) {
1052         // Set the border pixels on the left
1053         memset(scanlines[i], defaultPixel, horizontalBorderSize);
1054         // Copy the pixel data
1055         pixelSelection->readBytes(scanlines[i] + horizontalBorderSize, rect.x(), rect.y() + i - verticalBorderSize, rect.width(), 1);
1056         // Set the border pixels on the right
1057         memset(scanlines[i] + horizontalBorderSize + rect.width(), defaultPixel, horizontalBorderSize);
1058     }
1059     // Set the border scanlines on the bottom
1060     if (verticalBorderSize + numberOfFirstRows < numberOfScanlines) {
1061         for (quint32 i = verticalBorderSize + numberOfFirstRows; i < numberOfScanlines; ++i) {
1062             memset(scanlines[i], defaultPixel, bytesPerScanline);
1063         }
1064     }
1065     // Buffer that contains the current output scanline
1066     QVector<quint8> antialiasedScanline(rect.width());
1067     // Main loop
1068     for (int y = 0; y < rect.height(); ++y)
1069     {
1070         // Move to the next scanline
1071         if (y > 0) {
1072             // Update scanline pointers
1073             std::rotate(std::begin(scanlines), std::begin(scanlines) + 1, std::end(scanlines));
1074             // Copy the next scanline
1075             if (y < rect.height() - verticalBorderSize) {
1076                 // Set the border pixels on the left
1077                 memset(scanlines[numberOfScanlines - 1], defaultPixel, horizontalBorderSize);
1078                 // Copy the pixel data
1079                 pixelSelection->readBytes(scanlines[numberOfScanlines - 1] + horizontalBorderSize, rect.x(), rect.y() + y + verticalBorderSize, rect.width(), 1);
1080                 // Set the border pixels on the right
1081                 memset(scanlines[numberOfScanlines - 1] + horizontalBorderSize + rect.width(), defaultPixel, horizontalBorderSize);
1082             } else {
1083                 memset(scanlines[numberOfScanlines - 1], defaultPixel, bytesPerScanline);
1084             }
1085         }
1086         // Process the pixels in the current scanline
1087         for (int x = 0; x < rect.width(); ++x)
1088         {
1089             // Get the current pixel and neighbors
1090             quint8 *pixelPtrM = scanlines[currentScanlineIndex    ] + x + horizontalBorderSize;
1091             quint8 *pixelPtrN = scanlines[currentScanlineIndex - 1] + x + horizontalBorderSize;
1092             quint8 *pixelPtrS = scanlines[currentScanlineIndex + 1] + x + horizontalBorderSize;
1093             const qint32 pixelNW = *(pixelPtrN - 1);
1094             const qint32 pixelN  = *(pixelPtrN    );
1095             const qint32 pixelNE = *(pixelPtrN + 1);
1096             const qint32 pixelW  = *(pixelPtrM - 1);
1097             const qint32 pixelM  = *(pixelPtrM    );
1098             const qint32 pixelE  = *(pixelPtrM + 1);
1099             const qint32 pixelSW = *(pixelPtrS - 1);
1100             const qint32 pixelS  = *(pixelPtrS    );
1101             const qint32 pixelSE = *(pixelPtrS + 1);
1102             // Get the gradients
1103             const qint32 rowNSum = (pixelNW >> 2) + (pixelN >> 1) + (pixelNE >> 2);
1104             const qint32 rowMSum = (pixelW  >> 2) + (pixelM >> 1) + (pixelE  >> 2);
1105             const qint32 rowSSum = (pixelSW >> 2) + (pixelS >> 1) + (pixelSE >> 2);
1106             const qint32 colWSum = (pixelNW >> 2) + (pixelW >> 1) + (pixelSW >> 2);
1107             const qint32 colMSum = (pixelN  >> 2) + (pixelM >> 1) + (pixelS  >> 2);
1108             const qint32 colESum = (pixelNE >> 2) + (pixelE >> 1) + (pixelSE >> 2);
1109             const qint32 gradientN = qAbs(rowMSum - rowNSum);
1110             const qint32 gradientS = qAbs(rowSSum - rowMSum);
1111             const qint32 gradientW = qAbs(colMSum - colWSum);
1112             const qint32 gradientE = qAbs(colESum - colMSum);
1113             // Get the maximum gradient
1114             const qint32 maxGradientNS = qMax(gradientN, gradientS);
1115             const qint32 maxGradientWE = qMax(gradientW, gradientE);
1116             const qint32 maxGradient = qMax(maxGradientNS, maxGradientWE);
1117             // Return early if the gradient is bellow some threshold (given by
1118             // the value bellow which the jagged edge is not noticeable)
1119             if (maxGradient < edgeThreshold) {
1120                 antialiasedScanline[x] = pixelM;
1121                 continue;
1122             }
1123             // Collect some info about the pixel and neighborhood
1124             qint32 neighborPixel, gradient;
1125             qint32 pixelOffset, rowMultiplier, colMultiplier;
1126             if (maxGradientNS > maxGradientWE) {
1127                 // Horizontal span
1128                 if (gradientN > gradientS) {
1129                     // The edge is formed with the top pixel
1130                     neighborPixel = pixelN;
1131                     gradient = gradientN;
1132                     pixelOffset = -1;
1133                 } else {
1134                     // The edge is formed with the bottom pixel
1135                     neighborPixel = pixelS;
1136                     gradient = gradientS;
1137                     pixelOffset = 1;
1138                 }
1139                 rowMultiplier = 0;
1140                 colMultiplier = 1;
1141             } else {
1142                 // Vertical span
1143                 if (gradientW > gradientE) {
1144                     // The edge is formed with the left pixel
1145                     neighborPixel = pixelW;
1146                     gradient = gradientW;
1147                     pixelOffset = -1;
1148                 } else {
1149                     // The edge is formed with the right pixel
1150                     neighborPixel = pixelE;
1151                     gradient = gradientE;
1152                     pixelOffset = 1;
1153                 }
1154                 rowMultiplier = 1;
1155                 colMultiplier = 0;
1156             }
1157             // Find the span extremes
1158             const qint32 pixelAvg = (neighborPixel + pixelM) >> 1;
1159             const qint32 currentPixelDiff = pixelM - pixelAvg;
1160             qint32 negativePixelDiff, positivePixelDiff;
1161             qint32 negativeSpanEndDistance, positiveSpanEndDistance;
1162             bool negativeSpanExtremeValid, positiveSpanExtremeValid;
1163             findSpanExtremes(scanlines, x, pixelOffset,
1164                              rowMultiplier, colMultiplier,
1165                              pixelAvg, gradient >> 2, currentPixelDiff,
1166                              &negativeSpanEndDistance, &positiveSpanEndDistance,
1167                              &negativePixelDiff, &positivePixelDiff,
1168                              &negativeSpanExtremeValid, &positiveSpanExtremeValid);
1169             // Get the interpolation value for this pixel given the span extent
1170             // and perform linear interpolation between the current pixel and
1171             // the edge neighbor
1172             qint32 interpolationValue;
1173             if (!getInterpolationValue(negativeSpanEndDistance, positiveSpanEndDistance,
1174                                        negativePixelDiff, positivePixelDiff, currentPixelDiff,
1175                                        negativeSpanExtremeValid, positiveSpanExtremeValid, &interpolationValue)) {
1176                 antialiasedScanline[x] = pixelM;
1177             } else {
1178                 antialiasedScanline[x] = neighborPixel + ((pixelM - neighborPixel) * interpolationValue >> 8);
1179             }
1180         }
1181         // Copy the scanline data to the mask
1182         pixelSelection->writeBytes(antialiasedScanline.data(), rect.x(), rect.y() + y, rect.width(), 1);
1183     }
1184 }
1185 
1186 KisGrowUntilDarkestPixelSelectionFilter::KisGrowUntilDarkestPixelSelectionFilter(qint32 radius,
1187                                                                                  KisPaintDeviceSP referenceDevice)
1188     : m_radius(radius)
1189     , m_referenceDevice(referenceDevice)
1190 {
1191 }
1192 
1193 KUndo2MagicString KisGrowUntilDarkestPixelSelectionFilter::name()
1194 {
1195     return kundo2_i18n("Grow Selection Until Darkest Pixel");
1196 }
1197 
1198 QRect KisGrowUntilDarkestPixelSelectionFilter::changeRect(const QRect &rect, KisDefaultBoundsBaseSP defaultBounds)
1199 {
1200     Q_UNUSED(defaultBounds);
1201 
1202     return rect.adjusted(-m_radius, -m_radius, m_radius, m_radius);
1203 }
1204 
1205 void KisGrowUntilDarkestPixelSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect &rect)
1206 {
1207     // Copy the original selection. We will grow this adaptively until the
1208     // darkest or mor opaque pixels or until the maximum grow is reached.
1209     KisPixelSelectionSP mask = new KisPixelSelection(*pixelSelection);
1210     // Grow the original selection normally. At the end this selection will be
1211     // masked with the adaptively grown mask. We cannot grow adaptively this
1212     // selection directly since it may have semi-transparent or soft edges.
1213     // Those need to be retained in the final selection. This normally grown
1214     // selection is also used as a stop condition for the adaptive mask, which
1215     // cannot grow pass the limits of the this normally grown selection.
1216     KisGrowSelectionFilter growFilter(m_radius, m_radius);
1217     growFilter.process(pixelSelection, rect);
1218 
1219     const qint32 maskScanLineSize = rect.width();
1220     const KoColorSpace *referenceColorSpace = m_referenceDevice->colorSpace();
1221     const qint32 referencePixelSize = referenceColorSpace->pixelSize();
1222     const qint32 referenceScanLineSize = maskScanLineSize * referencePixelSize;
1223     // Some buffers to store the working scanlines
1224     QVector<quint8> maskBuffer(maskScanLineSize * 2);
1225     QVector<quint8> referenceBuffer(referenceScanLineSize * 2);
1226     QVector<quint8> selectionBuffer(maskScanLineSize);
1227     quint8 *maskScanLines[2] = {maskBuffer.data(), maskBuffer.data() + maskScanLineSize};
1228     quint8 *referenceScanLines[2] = {referenceBuffer.data(), referenceBuffer.data() + referenceScanLineSize};
1229     quint8 *selectionScanLine = selectionBuffer.data();
1230     // Helper function to test if a pixel can be selected
1231     auto testSelectPixel = 
1232         [referenceColorSpace]
1233         (quint8 pixelOpacity, quint8 pixelIntensity,
1234          const quint8 *testMaskPixel, const quint8 *testReferencePixel) -> bool
1235         {
1236             if (*testMaskPixel) {
1237                 const quint8 testOpacity = referenceColorSpace->opacityU8(testReferencePixel);
1238                 if (pixelOpacity >= testOpacity) {
1239                     // Special case for when the neighbor pixel is fully transparent.
1240                     // In that case do not compare the intensity
1241                     if (testOpacity == MIN_SELECTED) {
1242                         return true;
1243                     }
1244                     // If the opacity test passes we still have to perform the
1245                     // intensity test
1246                     const quint8 testIntensity = referenceColorSpace->intensity8(testReferencePixel);
1247                     if (pixelIntensity <= testIntensity) {
1248                         return true;
1249                     }
1250                 }
1251             }
1252             return false;
1253         };
1254 
1255     // Top-left to bottom-right pass
1256     // First row
1257     {
1258         mask->readBytes(maskScanLines[1], rect.left(), rect.top(), maskScanLineSize, 1);
1259         m_referenceDevice->readBytes(referenceScanLines[1], rect.left(), rect.top(), maskScanLineSize, 1);
1260         pixelSelection->readBytes(selectionScanLine, rect.left(), rect.top(), maskScanLineSize, 1);
1261         quint8 *currentMaskScanLineBegin = maskScanLines[1];
1262         quint8 *currentMaskScanLineEnd = maskScanLines[1] + maskScanLineSize;
1263         quint8 *currentReferenceScanLineBegin = referenceScanLines[1];
1264         quint8 *currentSelectionScanLineBegin = selectionScanLine;
1265         // First pixel
1266         ++currentMaskScanLineBegin;
1267         currentReferenceScanLineBegin += referencePixelSize;
1268         ++currentSelectionScanLineBegin;
1269         // Rest of pixels
1270         while (currentMaskScanLineBegin != currentMaskScanLineEnd) {
1271             if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1272                 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1273                 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1274 
1275                 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1276                                                        currentMaskScanLineBegin - 1,
1277                                                        currentReferenceScanLineBegin - referencePixelSize);
1278                 if (pixelIsSelected) {
1279                     *currentMaskScanLineBegin = MAX_SELECTED;
1280                 }
1281             }
1282             ++currentMaskScanLineBegin;
1283             currentReferenceScanLineBegin += referencePixelSize;
1284             ++currentSelectionScanLineBegin;
1285         }
1286         mask->writeBytes(maskScanLines[1], rect.left(), rect.top(), maskScanLineSize, 1);
1287     }
1288     // Rest of rows
1289     for (qint32 y = rect.top() + 1; y <= rect.bottom(); ++y) {
1290         rotatePointers(maskScanLines, 2);
1291         rotatePointers(referenceScanLines, 2);
1292         mask->readBytes(maskScanLines[1], rect.left(), y, maskScanLineSize, 1);
1293         m_referenceDevice->readBytes(referenceScanLines[1], rect.left(), y, maskScanLineSize, 1);
1294         pixelSelection->readBytes(selectionScanLine, rect.left(), y, maskScanLineSize, 1);
1295         quint8 *currentMaskScanLineBegin = maskScanLines[1];
1296         quint8 *currentMaskScanLineEnd = maskScanLines[1] + maskScanLineSize;
1297         quint8 *currentReferenceScanLineBegin = referenceScanLines[1];
1298         quint8 *topMaskScanLineBegin = maskScanLines[0];
1299         quint8 *topReferenceScanLineBegin = referenceScanLines[0];
1300         quint8 *currentSelectionScanLineBegin = selectionScanLine;
1301         // First pixel
1302         {
1303             if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1304                 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1305                 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1306 
1307                 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1308                                                        topMaskScanLineBegin,
1309                                                        topReferenceScanLineBegin);
1310                 if (!pixelIsSelected) {
1311                     pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1312                                                         topMaskScanLineBegin + 1,
1313                                                         topReferenceScanLineBegin + referencePixelSize);
1314                 }
1315                 if (pixelIsSelected) {
1316                     *currentMaskScanLineBegin = MAX_SELECTED;
1317                 }
1318             }
1319             ++currentMaskScanLineBegin;
1320             currentReferenceScanLineBegin += referencePixelSize;
1321             ++topMaskScanLineBegin;
1322             topReferenceScanLineBegin += referencePixelSize;
1323             ++currentSelectionScanLineBegin;
1324         }
1325         // Rest of pixels
1326         while (currentMaskScanLineBegin != (currentMaskScanLineEnd - 1)) {
1327             if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1328                 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1329                 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1330 
1331                 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity, 
1332                                                        topMaskScanLineBegin - 1,
1333                                                        topReferenceScanLineBegin - referencePixelSize);
1334                 if (!pixelIsSelected) {
1335                     pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1336                                                       topMaskScanLineBegin,
1337                                                       topReferenceScanLineBegin);
1338                     if (!pixelIsSelected) {
1339                         pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1340                                                           topMaskScanLineBegin + 1,
1341                                                           topReferenceScanLineBegin + referencePixelSize);
1342                         if (!pixelIsSelected) {
1343                             pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1344                                                               currentMaskScanLineBegin - 1,
1345                                                               currentReferenceScanLineBegin - referencePixelSize);
1346                         }
1347                     }
1348                 }
1349                 if (pixelIsSelected) {
1350                     *currentMaskScanLineBegin = MAX_SELECTED;
1351                 }
1352             }
1353             ++currentMaskScanLineBegin;
1354             currentReferenceScanLineBegin += referencePixelSize;
1355             ++topMaskScanLineBegin;
1356             topReferenceScanLineBegin += referencePixelSize;
1357             ++currentSelectionScanLineBegin;
1358         }
1359         // Last pixel
1360         {
1361             if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1362                 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1363                 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1364 
1365                 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity, 
1366                                                        topMaskScanLineBegin - 1,
1367                                                        topReferenceScanLineBegin - referencePixelSize);
1368                 if (!pixelIsSelected) {
1369                     pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1370                                                       topMaskScanLineBegin,
1371                                                       topReferenceScanLineBegin);
1372                     if (!pixelIsSelected) {
1373                         pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1374                                                           currentMaskScanLineBegin - 1,
1375                                                           currentReferenceScanLineBegin - referencePixelSize);
1376                     }
1377                 }
1378                 if (pixelIsSelected) {
1379                     *currentMaskScanLineBegin = MAX_SELECTED;
1380                 }
1381             }
1382         }
1383         mask->writeBytes(maskScanLines[1], rect.left(), y, maskScanLineSize, 1);
1384     }
1385 
1386     // Bottom-right to top-left pass
1387     // Last row
1388     {
1389         mask->readBytes(maskScanLines[1], rect.left(), rect.bottom(), maskScanLineSize, 1);
1390         m_referenceDevice->readBytes(referenceScanLines[1], rect.left(), rect.bottom(), maskScanLineSize, 1);
1391         pixelSelection->readBytes(selectionScanLine, rect.left(), rect.bottom(), maskScanLineSize, 1);
1392         quint8 *currentMaskScanLineBegin = maskScanLines[1] + maskScanLineSize - 1;
1393         quint8 *currentMaskScanLineEnd = maskScanLines[1] - 1;
1394         quint8 *currentReferenceScanLineBegin = referenceScanLines[1] + referenceScanLineSize - referencePixelSize;
1395         quint8 *currentSelectionScanLineBegin = selectionScanLine + maskScanLineSize - 1;
1396         // Last pixel
1397         --currentMaskScanLineBegin;
1398         currentReferenceScanLineBegin -= referencePixelSize;
1399         --currentSelectionScanLineBegin;
1400         // Rest of pixels
1401         while (currentMaskScanLineBegin != currentMaskScanLineEnd) {
1402             if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1403                 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1404                 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1405 
1406                 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1407                                                        currentMaskScanLineBegin + 1,
1408                                                        currentReferenceScanLineBegin + referencePixelSize);
1409                 if (pixelIsSelected) {
1410                     *currentMaskScanLineBegin = MAX_SELECTED;
1411                 }
1412             }
1413             --currentMaskScanLineBegin;
1414             currentReferenceScanLineBegin -= referencePixelSize;
1415             --currentSelectionScanLineBegin;
1416         }
1417         mask->writeBytes(maskScanLines[1], rect.left(), rect.top(), maskScanLineSize, 1);
1418     }
1419     // Rest of rows
1420     for (qint32 y = rect.bottom() - 1; y >= rect.top(); --y) {
1421         rotatePointers(maskScanLines, 2);
1422         rotatePointers(referenceScanLines, 2);
1423         mask->readBytes(maskScanLines[1], rect.left(), y, maskScanLineSize, 1);
1424         m_referenceDevice->readBytes(referenceScanLines[1], rect.left(), y, maskScanLineSize, 1);
1425         pixelSelection->readBytes(selectionScanLine, rect.left(), y, maskScanLineSize, 1);
1426         quint8 *currentMaskScanLineBegin = maskScanLines[1] + maskScanLineSize - 1;
1427         quint8 *currentMaskScanLineEnd = maskScanLines[1] - 1;
1428         quint8 *currentReferenceScanLineBegin = referenceScanLines[1] + referenceScanLineSize - referencePixelSize;
1429         quint8 *bottomMaskScanLineBegin = maskScanLines[0] + maskScanLineSize - 1;
1430         quint8 *bottomReferenceScanLineBegin = referenceScanLines[0] + referenceScanLineSize - referencePixelSize;
1431         quint8 *currentSelectionScanLineBegin = selectionScanLine + maskScanLineSize - 1;
1432         // Last pixel
1433         {
1434             if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1435                 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1436                 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1437 
1438                 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1439                                                        bottomMaskScanLineBegin,
1440                                                        bottomReferenceScanLineBegin);
1441                 if (!pixelIsSelected) {
1442                     pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1443                                                       bottomMaskScanLineBegin - 1,
1444                                                       bottomReferenceScanLineBegin - referencePixelSize);
1445                 }
1446                 if (pixelIsSelected) {
1447                     *currentMaskScanLineBegin = MAX_SELECTED;
1448                 }
1449             }
1450             --currentMaskScanLineBegin;
1451             currentReferenceScanLineBegin -= referencePixelSize;
1452             --bottomMaskScanLineBegin;
1453             bottomReferenceScanLineBegin -= referencePixelSize;
1454             --currentSelectionScanLineBegin;
1455         }
1456         // Rest of pixels
1457         while (currentMaskScanLineBegin != (currentMaskScanLineEnd + 1)) {
1458             if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1459                 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1460                 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1461 
1462                 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity, 
1463                                                        bottomMaskScanLineBegin + 1,
1464                                                        bottomReferenceScanLineBegin + referencePixelSize);
1465                 if (!pixelIsSelected) {
1466                     pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1467                                                       bottomMaskScanLineBegin,
1468                                                       bottomReferenceScanLineBegin);
1469                     if (!pixelIsSelected) {
1470                         pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1471                                                           bottomMaskScanLineBegin - 1,
1472                                                           bottomReferenceScanLineBegin - referencePixelSize);
1473                         if (!pixelIsSelected) {
1474                             pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1475                                                               currentMaskScanLineBegin + 1,
1476                                                               currentReferenceScanLineBegin + referencePixelSize);
1477                         }
1478                     }
1479                 }
1480                 if (pixelIsSelected) {
1481                     *currentMaskScanLineBegin = MAX_SELECTED;
1482                 }
1483             }
1484             --currentMaskScanLineBegin;
1485             currentReferenceScanLineBegin -= referencePixelSize;
1486             --bottomMaskScanLineBegin;
1487             bottomReferenceScanLineBegin -= referencePixelSize;
1488             --currentSelectionScanLineBegin;
1489         }
1490         // First pixel
1491         {
1492             if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1493                 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1494                 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1495 
1496                 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity, 
1497                                                        bottomMaskScanLineBegin + 1,
1498                                                        bottomReferenceScanLineBegin + referencePixelSize);
1499                 if (!pixelIsSelected) {
1500                     pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1501                                                       bottomMaskScanLineBegin,
1502                                                       bottomReferenceScanLineBegin);
1503                     if (!pixelIsSelected) {
1504                         pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1505                                                             currentMaskScanLineBegin + 1,
1506                                                             currentReferenceScanLineBegin + referencePixelSize);
1507                     }
1508                 }
1509                 if (pixelIsSelected) {
1510                     *currentMaskScanLineBegin = MAX_SELECTED;
1511                 }
1512             }
1513         }
1514         mask->writeBytes(maskScanLines[1], rect.left(), y, maskScanLineSize, 1);
1515     }
1516 
1517     // Combine the adaptively grown mask with the normally grown mask. The
1518     // adaptively grown mask is used as a binary mask to erase some of the
1519     // pixels of the normally grown mask
1520     {
1521         KisSequentialConstIterator it1(mask, rect);
1522         KisSequentialIterator it2(pixelSelection, rect);
1523         while (it1.nextPixel() && it2.nextPixel()) {
1524             *it2.rawData() *= (*it1.rawDataConst() != MIN_SELECTED);
1525         }
1526     }
1527 }