File indexing completed on 2024-05-12 15:58:41

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 
0019 #define MAX(a, b) ((a) > (b) ? (a) : (b))
0020 #define MIN(a, b) ((a) < (b) ? (a) : (b))
0021 #define RINT(x) floor ((x) + 0.5)
0022 
0023 KisSelectionFilter::~KisSelectionFilter()
0024 {
0025 }
0026 
0027 KUndo2MagicString KisSelectionFilter::name()
0028 {
0029     return KUndo2MagicString();
0030 }
0031 
0032 QRect KisSelectionFilter::changeRect(const QRect &rect, KisDefaultBoundsBaseSP defaultBounds)
0033 {
0034     Q_UNUSED(defaultBounds);
0035     return rect;
0036 }
0037 
0038 void KisSelectionFilter::computeBorder(qint32* circ, qint32 xradius, qint32 yradius)
0039 {
0040     qint32 i;
0041     qint32 diameter = xradius * 2 + 1;
0042     double tmp;
0043 
0044     for (i = 0; i < diameter; i++) {
0045         if (i > xradius)
0046             tmp = (i - xradius) - 0.5;
0047         else if (i < xradius)
0048             tmp = (xradius - i) - 0.5;
0049         else
0050             tmp = 0.0;
0051 
0052         double divisor = (double) xradius;
0053         if (divisor == 0.0) {
0054             divisor = 1.0;
0055         }
0056         circ[i] = (qint32) RINT(yradius * sqrt(xradius * xradius - tmp * tmp) / divisor);
0057     }
0058 }
0059 
0060 void KisSelectionFilter::rotatePointers(quint8** p, quint32 n)
0061 {
0062     quint32 i;
0063     quint8  *p0 = p[0];
0064     for (i = 0; i < n - 1; i++) {
0065         p[i] = p[i + 1];
0066     }
0067     p[i] = p0;
0068 }
0069 
0070 void KisSelectionFilter::computeTransition(quint8* transition, quint8** buf, qint32 width)
0071 {
0072     qint32 x = 0;
0073 
0074     if (width == 1) {
0075         if (buf[1][x] > 127 && (buf[0][x] < 128 || buf[2][x] < 128))
0076             transition[x] = 255;
0077         else
0078             transition[x] = 0;
0079         return;
0080     }
0081     if (buf[1][x] > 127) {
0082         if (buf[0][x] < 128 || buf[0][x + 1] < 128 ||
0083             buf[1][x + 1] < 128 ||
0084             buf[2][x] < 128 || buf[2][x + 1] < 128)
0085             transition[x] = 255;
0086         else
0087             transition[x] = 0;
0088     } else
0089         transition[x] = 0;
0090     for (qint32 x = 1; x < width - 1; x++) {
0091         if (buf[1][x] >= 128) {
0092             if (buf[0][x - 1] < 128 || buf[0][x] < 128 || buf[0][x + 1] < 128 ||
0093                 buf[1][x - 1] < 128           ||          buf[1][x + 1] < 128 ||
0094                 buf[2][x - 1] < 128 || buf[2][x] < 128 || buf[2][x + 1] < 128)
0095                 transition[x] = 255;
0096             else
0097                 transition[x] = 0;
0098         } else
0099             transition[x] = 0;
0100     }
0101     if (buf[1][x] >= 128) {
0102         if (buf[0][x - 1] < 128 || buf[0][x] < 128 ||
0103             buf[1][x - 1] < 128 ||
0104             buf[2][x - 1] < 128 || buf[2][x] < 128)
0105             transition[x] = 255;
0106         else
0107             transition[x] = 0;
0108     } else
0109         transition[x] = 0;
0110 }
0111 
0112 
0113 KUndo2MagicString KisErodeSelectionFilter::name()
0114 {
0115     return kundo2_i18n("Erode Selection");
0116 }
0117 
0118 QRect KisErodeSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds)
0119 {
0120     Q_UNUSED(defaultBounds);
0121 
0122     const qint32 radius = 1;
0123     return rect.adjusted(-radius, -radius, radius, radius);
0124 }
0125 
0126 void KisErodeSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0127 {
0128     // Erode (radius 1 pixel) a mask (1bpp)
0129     quint8* buf[3];
0130 
0131     qint32 width = rect.width();
0132     qint32 height = rect.height();
0133 
0134     quint8* out = new quint8[width];
0135     for (qint32 i = 0; i < 3; i++)
0136         buf[i] = new quint8[width + 2];
0137 
0138 
0139     // load top of image
0140     pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1);
0141 
0142     buf[0][0]         = buf[0][1];
0143     buf[0][width + 1] = buf[0][width];
0144 
0145     memcpy(buf[1], buf[0], width + 2);
0146 
0147     for (qint32 y = 0; y < height; y++) {
0148         if (y + 1 < height) {
0149             pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1);
0150 
0151             buf[2][0]         = buf[2][1];
0152             buf[2][width + 1] = buf[2][width];
0153         } else {
0154             memcpy(buf[2], buf[1], width + 2);
0155         }
0156 
0157         for (qint32 x = 0 ; x < width; x++) {
0158             qint32 min = 255;
0159 
0160             if (buf[0][x+1] < min) min = buf[0][x+1];
0161             if (buf[1][x]   < min) min = buf[1][x];
0162             if (buf[1][x+1] < min) min = buf[1][x+1];
0163             if (buf[1][x+2] < min) min = buf[1][x+2];
0164             if (buf[2][x+1] < min) min = buf[2][x+1];
0165 
0166             out[x] = min;
0167         }
0168 
0169         pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1);
0170         rotatePointers(buf, 3);
0171     }
0172 
0173     for (qint32 i = 0; i < 3; i++)
0174         delete[] buf[i];
0175     delete[] out;
0176 }
0177 
0178 
0179 KUndo2MagicString KisDilateSelectionFilter::name()
0180 {
0181     return kundo2_i18n("Dilate Selection");
0182 }
0183 
0184 QRect KisDilateSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds)
0185 {
0186     Q_UNUSED(defaultBounds);
0187 
0188     const qint32 radius = 1;
0189     return rect.adjusted(-radius, -radius, radius, radius);
0190 }
0191 
0192 void KisDilateSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0193  {
0194     // dilate (radius 1 pixel) a mask (1bpp)
0195     quint8* buf[3];
0196 
0197     qint32 width = rect.width();
0198     qint32 height = rect.height();
0199 
0200     quint8* out = new quint8[width];
0201     for (qint32 i = 0; i < 3; i++)
0202         buf[i] = new quint8[width + 2];
0203 
0204 
0205     // load top of image
0206     pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1);
0207 
0208     buf[0][0]         = buf[0][1];
0209     buf[0][width + 1] = buf[0][width];
0210 
0211     memcpy(buf[1], buf[0], width + 2);
0212 
0213     for (qint32 y = 0; y < height; y++) {
0214         if (y + 1 < height) {
0215             pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1);
0216 
0217             buf[2][0]         = buf[2][1];
0218             buf[2][width + 1] = buf[2][width];
0219         } else {
0220             memcpy(buf[2], buf[1], width + 2);
0221         }
0222 
0223         for (qint32 x = 0 ; x < width; x++) {
0224             qint32 max = 0;
0225 
0226             if (buf[0][x+1] > max) max = buf[0][x+1];
0227             if (buf[1][x]   > max) max = buf[1][x];
0228             if (buf[1][x+1] > max) max = buf[1][x+1];
0229             if (buf[1][x+2] > max) max = buf[1][x+2];
0230             if (buf[2][x+1] > max) max = buf[2][x+1];
0231 
0232             out[x] = max;
0233         }
0234 
0235         pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1);
0236         rotatePointers(buf, 3);
0237     }
0238 
0239     for (qint32 i = 0; i < 3; i++)
0240         delete[] buf[i];
0241     delete[] out;
0242 }
0243 
0244 
0245 KisBorderSelectionFilter::KisBorderSelectionFilter(qint32 xRadius, qint32 yRadius, bool antialiasing)
0246   : m_xRadius(xRadius),
0247     m_yRadius(yRadius),
0248     m_antialiasing(antialiasing)
0249 {
0250 }
0251 
0252 KUndo2MagicString KisBorderSelectionFilter::name()
0253 {
0254     return kundo2_i18n("Border Selection");
0255 }
0256 
0257 QRect KisBorderSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds)
0258 {
0259     Q_UNUSED(defaultBounds);
0260 
0261     return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius);
0262 }
0263 
0264 void KisBorderSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0265 {
0266     if (m_xRadius <= 0 || m_yRadius <= 0) return;
0267 
0268     quint8  *buf[3];
0269     quint8 **density;
0270     quint8 **transition;
0271 
0272     if (m_xRadius == 1 && m_yRadius == 1) {
0273         // optimize this case specifically
0274         quint8* source[3];
0275 
0276         for (qint32 i = 0; i < 3; i++)
0277             source[i] = new quint8[rect.width()];
0278 
0279         quint8* transition = new quint8[rect.width()];
0280 
0281         pixelSelection->readBytes(source[0], rect.x(), rect.y(), rect.width(), 1);
0282         memcpy(source[1], source[0], rect.width());
0283         if (rect.height() > 1)
0284             pixelSelection->readBytes(source[2], rect.x(), rect.y() + 1, rect.width(), 1);
0285         else
0286             memcpy(source[2], source[1], rect.width());
0287 
0288         computeTransition(transition, source, rect.width());
0289         pixelSelection->writeBytes(transition, rect.x(), rect.y(), rect.width(), 1);
0290 
0291         for (qint32 y = 1; y < rect.height(); y++) {
0292             rotatePointers(source, 3);
0293             if (y + 1 < rect.height())
0294                 pixelSelection->readBytes(source[2], rect.x(), rect.y() + y + 1, rect.width(), 1);
0295             else
0296                 memcpy(source[2], source[1], rect.width());
0297             computeTransition(transition, source, rect.width());
0298             pixelSelection->writeBytes(transition, rect.x(), rect.y() + y, rect.width(), 1);
0299         }
0300 
0301         for (qint32 i = 0; i < 3; i++)
0302             delete[] source[i];
0303         delete[] transition;
0304         return;
0305     }
0306 
0307     qint32* max = new qint32[rect.width() + 2 * m_xRadius];
0308     for (qint32 i = 0; i < (rect.width() + 2 * m_xRadius); i++)
0309         max[i] = m_yRadius + 2;
0310     max += m_xRadius;
0311 
0312     for (qint32 i = 0; i < 3; i++)
0313         buf[i] = new quint8[rect.width()];
0314 
0315     transition = new quint8*[m_yRadius + 1];
0316     for (qint32 i = 0; i < m_yRadius + 1; i++) {
0317         transition[i] = new quint8[rect.width() + 2 * m_xRadius];
0318         memset(transition[i], 0, rect.width() + 2 * m_xRadius);
0319         transition[i] += m_xRadius;
0320     }
0321     quint8* out = new quint8[rect.width()];
0322     density = new quint8*[2 * m_xRadius + 1];
0323     density += m_xRadius;
0324 
0325     for (qint32 x = 0; x < (m_xRadius + 1); x++) { // allocate density[][]
0326         density[ x]  = new quint8[2 * m_yRadius + 1];
0327         density[ x] += m_yRadius;
0328         density[-x]  = density[x];
0329     }
0330 
0331     // compute density[][]
0332     if (m_antialiasing) {
0333         KIS_SAFE_ASSERT_RECOVER_NOOP(m_xRadius == m_yRadius && "anisotropic fading is not implemented");
0334         const qreal maxRadius = 0.5 * (m_xRadius + m_yRadius);
0335         const qreal minRadius = maxRadius - 1.0;
0336 
0337         for (qint32 x = 0; x < (m_xRadius + 1); x++) {
0338             double dist;
0339             quint8 a;
0340 
0341             for (qint32 y = 0; y < (m_yRadius + 1); y++) {
0342 
0343                 dist = sqrt(pow2(x) + pow2(y));
0344 
0345                 if (dist > maxRadius) {
0346                     a = 0;
0347                 } else if (dist > minRadius) {
0348                     a = qRound((1.0 - dist + minRadius) * 255.0);
0349                 } else {
0350                     a = 255;
0351                 }
0352 
0353                 density[ x][ y] = a;
0354                 density[ x][-y] = a;
0355                 density[-x][ y] = a;
0356                 density[-x][-y] = a;
0357             }
0358         }
0359 
0360     } else {
0361         for (qint32 x = 0; x < (m_xRadius + 1); x++) {
0362             double tmpx, tmpy, dist;
0363             quint8 a;
0364 
0365             tmpx = x > 0.0 ? x - 0.5 : 0.0;
0366 
0367             for (qint32 y = 0; y < (m_yRadius + 1); y++) {
0368                 tmpy = y > 0.0 ? y - 0.5 : 0.0;
0369 
0370                 dist = (pow2(tmpy) / pow2(m_yRadius) +
0371                         pow2(tmpx) / pow2(m_xRadius));
0372 
0373                 a = dist <= 1.0 ? 255 : 0;
0374 
0375                 density[ x][ y] = a;
0376                 density[ x][-y] = a;
0377                 density[-x][ y] = a;
0378                 density[-x][-y] = a;
0379             }
0380         }
0381     }
0382 
0383     pixelSelection->readBytes(buf[0], rect.x(), rect.y(), rect.width(), 1);
0384     memcpy(buf[1], buf[0], rect.width());
0385     if (rect.height() > 1)
0386         pixelSelection->readBytes(buf[2], rect.x(), rect.y() + 1, rect.width(), 1);
0387     else
0388         memcpy(buf[2], buf[1], rect.width());
0389     computeTransition(transition[1], buf, rect.width());
0390 
0391     for (qint32 y = 1; y < m_yRadius && y + 1 < rect.height(); y++) { // set up top of image
0392         rotatePointers(buf, 3);
0393         pixelSelection->readBytes(buf[2], rect.x(), rect.y() + y + 1, rect.width(), 1);
0394         computeTransition(transition[y + 1], buf, rect.width());
0395     }
0396     for (qint32 x = 0; x < rect.width(); x++) { // set up max[] for top of image
0397         max[x] = -(m_yRadius + 7);
0398         for (qint32 j = 1; j < m_yRadius + 1; j++)
0399             if (transition[j][x]) {
0400                 max[x] = j;
0401                 break;
0402             }
0403     }
0404     for (qint32 y = 0; y < rect.height(); y++) { // main calculation loop
0405         rotatePointers(buf, 3);
0406         rotatePointers(transition, m_yRadius + 1);
0407         if (y < rect.height() - (m_yRadius + 1)) {
0408             pixelSelection->readBytes(buf[2], rect.x(), rect.y() + y + m_yRadius + 1, rect.width(), 1);
0409             computeTransition(transition[m_yRadius], buf, rect.width());
0410         } else
0411             memcpy(transition[m_yRadius], transition[m_yRadius - 1], rect.width());
0412 
0413         for (qint32 x = 0; x < rect.width(); x++) { // update max array
0414             if (max[x] < 1) {
0415                 if (max[x] <= -m_yRadius) {
0416                     if (transition[m_yRadius][x])
0417                         max[x] = m_yRadius;
0418                     else
0419                         max[x]--;
0420                 } else if (transition[-max[x]][x])
0421                     max[x] = -max[x];
0422                 else if (transition[-max[x] + 1][x])
0423                     max[x] = -max[x] + 1;
0424                 else
0425                     max[x]--;
0426             } else
0427                 max[x]--;
0428             if (max[x] < -m_yRadius - 1)
0429                 max[x] = -m_yRadius - 1;
0430         }
0431         quint8 last_max =  max[0][density[-1]];
0432         qint32 last_index = 1;
0433         for (qint32 x = 0 ; x < rect.width(); x++) { // render scan line
0434             last_index--;
0435             if (last_index >= 0) {
0436                 last_max = 0;
0437                 for (qint32 i = m_xRadius; i >= 0; i--)
0438                     if (max[x + i] <= m_yRadius && max[x + i] >= -m_yRadius && density[i][max[x+i]] > last_max) {
0439                         last_max = density[i][max[x + i]];
0440                         last_index = i;
0441                     }
0442                 out[x] = last_max;
0443             } else {
0444                 last_max = 0;
0445                 for (qint32 i = m_xRadius; i >= -m_xRadius; i--)
0446                     if (max[x + i] <= m_yRadius && max[x + i] >= -m_yRadius && density[i][max[x + i]] > last_max) {
0447                         last_max = density[i][max[x + i]];
0448                         last_index = i;
0449                     }
0450                 out[x] = last_max;
0451             }
0452             if (last_max == 0) {
0453                 qint32 i;
0454                 for (i = x + 1; i < rect.width(); i++) {
0455                     if (max[i] >= -m_yRadius)
0456                         break;
0457                 }
0458                 if (i - x > m_xRadius) {
0459                     for (; x < i - m_xRadius; x++)
0460                         out[x] = 0;
0461                     x--;
0462                 }
0463                 last_index = m_xRadius;
0464             }
0465         }
0466         pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1);
0467     }
0468     delete [] out;
0469 
0470     for (qint32 i = 0; i < 3; i++)
0471         delete[] buf[i];
0472 
0473     max -= m_xRadius;
0474     delete[] max;
0475 
0476     for (qint32 i = 0; i < m_yRadius + 1; i++) {
0477         transition[i] -= m_xRadius;
0478         delete transition[i];
0479     }
0480     delete[] transition;
0481 
0482     for (qint32 i = 0; i < m_xRadius + 1 ; i++) {
0483         density[i] -= m_yRadius;
0484         delete density[i];
0485     }
0486     density -= m_xRadius;
0487     delete[] density;
0488 }
0489 
0490 
0491 KisFeatherSelectionFilter::KisFeatherSelectionFilter(qint32 radius)
0492     : m_radius(radius)
0493 {
0494 }
0495 
0496 KUndo2MagicString KisFeatherSelectionFilter::name()
0497 {
0498     return kundo2_i18n("Feather Selection");
0499 }
0500 
0501 QRect KisFeatherSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds)
0502 {
0503     Q_UNUSED(defaultBounds);
0504 
0505     return rect.adjusted(-m_radius, -m_radius,
0506                          m_radius, m_radius);
0507 }
0508 
0509 void KisFeatherSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0510 {
0511     // compute horizontal kernel
0512     const uint kernelSize = m_radius * 2 + 1;
0513     Eigen::Matrix<qreal, Eigen::Dynamic, Eigen::Dynamic> gaussianMatrix(1, kernelSize);
0514 
0515     const qreal multiplicand = 1.0 / (2.0 * M_PI * m_radius * m_radius);
0516     const qreal exponentMultiplicand = 1.0 / (2.0 * m_radius * m_radius);
0517 
0518     for (uint x = 0; x < kernelSize; x++) {
0519         uint xDistance = qAbs((int)m_radius - (int)x);
0520         gaussianMatrix(0, x) = multiplicand * exp( -(qreal)((xDistance * xDistance) + (m_radius * m_radius)) * exponentMultiplicand );
0521     }
0522 
0523     KisConvolutionKernelSP kernelHoriz = KisConvolutionKernel::fromMatrix(gaussianMatrix, 0, gaussianMatrix.sum());
0524     KisConvolutionKernelSP kernelVertical = KisConvolutionKernel::fromMatrix(gaussianMatrix.transpose(), 0, gaussianMatrix.sum());
0525 
0526     KisPaintDeviceSP interm = new KisPaintDevice(pixelSelection->colorSpace());
0527     interm->prepareClone(pixelSelection);
0528 
0529     KisConvolutionPainter horizPainter(interm);
0530     horizPainter.setChannelFlags(interm->colorSpace()->channelFlags(false, true));
0531     horizPainter.applyMatrix(kernelHoriz, pixelSelection, rect.topLeft(), rect.topLeft(), rect.size(), BORDER_REPEAT);
0532     horizPainter.end();
0533 
0534     KisConvolutionPainter verticalPainter(pixelSelection);
0535     verticalPainter.setChannelFlags(pixelSelection->colorSpace()->channelFlags(false, true));
0536     verticalPainter.applyMatrix(kernelVertical, interm, rect.topLeft(), rect.topLeft(), rect.size(), BORDER_REPEAT);
0537     verticalPainter.end();
0538 }
0539 
0540 
0541 KisGrowSelectionFilter::KisGrowSelectionFilter(qint32 xRadius, qint32 yRadius)
0542     : m_xRadius(xRadius)
0543     , m_yRadius(yRadius)
0544 {
0545 }
0546 
0547 KUndo2MagicString KisGrowSelectionFilter::name()
0548 {
0549     return kundo2_i18n("Grow Selection");
0550 }
0551 
0552 QRect KisGrowSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds)
0553 {
0554     Q_UNUSED(defaultBounds);
0555 
0556     return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius);
0557 }
0558 
0559 void KisGrowSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0560 {
0561     if (m_xRadius <= 0 || m_yRadius <= 0) return;
0562 
0563     /**
0564         * Much code resembles Shrink filter, so please fix bugs
0565         * in both filters
0566         */
0567 
0568     quint8  **buf;  // caches the region's pixel data
0569     quint8  **max;  // caches the largest values for each column
0570 
0571     max = new quint8* [rect.width() + 2 * m_xRadius];
0572     buf = new quint8* [m_yRadius + 1];
0573     for (qint32 i = 0; i < m_yRadius + 1; i++) {
0574         buf[i] = new quint8[rect.width()];
0575     }
0576     quint8* buffer = new quint8[(rect.width() + 2 * m_xRadius) *(m_yRadius + 1)];
0577     for (qint32 i = 0; i < rect.width() + 2 * m_xRadius; i++) {
0578         if (i < m_xRadius)
0579             max[i] = buffer;
0580         else if (i < rect.width() + m_xRadius)
0581             max[i] = &buffer[(m_yRadius + 1) * (i - m_xRadius)];
0582         else
0583             max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius - 1)];
0584 
0585         for (qint32 j = 0; j < m_xRadius + 1; j++)
0586             max[i][j] = 0;
0587     }
0588     /* offset the max pointer by m_xRadius so the range of the array
0589         is [-m_xRadius] to [region->w + m_xRadius] */
0590     max += m_xRadius;
0591 
0592     quint8* out = new quint8[ rect.width()];  // holds the new scan line we are computing
0593 
0594     qint32* circ = new qint32[ 2 * m_xRadius + 1 ]; // holds the y coords of the filter's mask
0595     computeBorder(circ, m_xRadius, m_yRadius);
0596 
0597     /* offset the circ pointer by m_xRadius so the range of the array
0598         is [-m_xRadius] to [m_xRadius] */
0599     circ += m_xRadius;
0600 
0601     memset(buf[0], 0, rect.width());
0602     for (qint32 i = 0; i < m_yRadius && i < rect.height(); i++) { // load top of image
0603         pixelSelection->readBytes(buf[i + 1], rect.x(), rect.y() + i, rect.width(), 1);
0604     }
0605 
0606     for (qint32 x = 0; x < rect.width() ; x++) { // set up max for top of image
0607         max[x][0] = 0;         // buf[0][x] is always 0
0608         max[x][1] = buf[1][x]; // MAX (buf[1][x], max[x][0]) always = buf[1][x]
0609         for (qint32 j = 2; j < m_yRadius + 1; j++) {
0610             max[x][j] = MAX(buf[j][x], max[x][j-1]);
0611         }
0612     }
0613 
0614     for (qint32 y = 0; y < rect.height(); y++) {
0615         rotatePointers(buf, m_yRadius + 1);
0616         if (y < rect.height() - (m_yRadius))
0617             pixelSelection->readBytes(buf[m_yRadius], rect.x(), rect.y() + y + m_yRadius, rect.width(), 1);
0618         else
0619             memset(buf[m_yRadius], 0, rect.width());
0620         for (qint32 x = 0; x < rect.width(); x++) { /* update max array */
0621             for (qint32 i = m_yRadius; i > 0; i--) {
0622                 max[x][i] = MAX(MAX(max[x][i - 1], buf[i - 1][x]), buf[i][x]);
0623             }
0624             max[x][0] = buf[0][x];
0625         }
0626         qint32 last_max = max[0][circ[-1]];
0627         qint32 last_index = 1;
0628         for (qint32 x = 0; x < rect.width(); x++) { /* render scan line */
0629             last_index--;
0630             if (last_index >= 0) {
0631                 if (last_max == 255)
0632                     out[x] = 255;
0633                 else {
0634                     last_max = 0;
0635                     for (qint32 i = m_xRadius; i >= 0; i--)
0636                         if (last_max < max[x + i][circ[i]]) {
0637                             last_max = max[x + i][circ[i]];
0638                             last_index = i;
0639                         }
0640                     out[x] = last_max;
0641                 }
0642             } else {
0643                 last_index = m_xRadius;
0644                 last_max = max[x + m_xRadius][circ[m_xRadius]];
0645                 for (qint32 i = m_xRadius - 1; i >= -m_xRadius; i--)
0646                     if (last_max < max[x + i][circ[i]]) {
0647                         last_max = max[x + i][circ[i]];
0648                         last_index = i;
0649                     }
0650                 out[x] = last_max;
0651             }
0652         }
0653         pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1);
0654     }
0655     /* undo the offsets to the pointers so we can free the malloced memory */
0656     circ -= m_xRadius;
0657     max -= m_xRadius;
0658 
0659     delete[] circ;
0660     delete[] buffer;
0661     delete[] max;
0662     for (qint32 i = 0; i < m_yRadius + 1; i++)
0663         delete[] buf[i];
0664     delete[] buf;
0665     delete[] out;
0666 }
0667 
0668 
0669 KisShrinkSelectionFilter::KisShrinkSelectionFilter(qint32 xRadius, qint32 yRadius, bool edgeLock)
0670     : m_xRadius(xRadius)
0671     , m_yRadius(yRadius)
0672     , m_edgeLock(edgeLock)
0673 {
0674 }
0675 
0676 KUndo2MagicString KisShrinkSelectionFilter::name()
0677 {
0678     return kundo2_i18n("Shrink Selection");
0679 }
0680 
0681 QRect KisShrinkSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds)
0682 {
0683     return m_edgeLock ? defaultBounds->imageBorderRect() : rect;
0684 }
0685 
0686 void KisShrinkSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0687 {
0688     if (m_xRadius <= 0 || m_yRadius <= 0) return;
0689 
0690     /*
0691         pretty much the same as fatten_region only different
0692         blame all bugs in this function on jaycox@gimp.org
0693     */
0694     /* If edge_lock is true  we assume that pixels outside the region
0695         we are passed are identical to the edge pixels.
0696         If edge_lock is false, we assume that pixels outside the region are 0
0697     */
0698     quint8  **buf;  // caches the region's pixels
0699     quint8  **max;  // caches the smallest values for each column
0700     qint32    last_max, last_index;
0701 
0702     max = new quint8* [rect.width() + 2 * m_xRadius];
0703     buf = new quint8* [m_yRadius + 1];
0704     for (qint32 i = 0; i < m_yRadius + 1; i++) {
0705         buf[i] = new quint8[rect.width()];
0706     }
0707 
0708     qint32 buffer_size = (rect.width() + 2 * m_xRadius + 1) * (m_yRadius + 1);
0709     quint8* buffer = new quint8[buffer_size];
0710 
0711     if (m_edgeLock)
0712         memset(buffer, 255, buffer_size);
0713     else
0714         memset(buffer, 0, buffer_size);
0715 
0716     for (qint32 i = 0; i < rect.width() + 2 * m_xRadius; i++) {
0717         if (i < m_xRadius)
0718             if (m_edgeLock)
0719                 max[i] = buffer;
0720             else
0721                 max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius)];
0722         else if (i < rect.width() + m_xRadius)
0723             max[i] = &buffer[(m_yRadius + 1) * (i - m_xRadius)];
0724         else if (m_edgeLock)
0725             max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius - 1)];
0726         else
0727             max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius)];
0728     }
0729     if (!m_edgeLock)
0730         for (qint32 j = 0 ; j < m_xRadius + 1; j++) max[0][j] = 0;
0731 
0732     // offset the max pointer by m_xRadius so the range of the array is [-m_xRadius] to [region->w + m_xRadius]
0733     max += m_xRadius;
0734 
0735     quint8* out = new quint8[rect.width()]; // holds the new scan line we are computing
0736 
0737     qint32* circ = new qint32[2 * m_xRadius + 1]; // holds the y coords of the filter's mask
0738 
0739     computeBorder(circ, m_xRadius, m_yRadius);
0740 
0741     // offset the circ pointer by m_xRadius so the range of the array is [-m_xRadius] to [m_xRadius]
0742     circ += m_xRadius;
0743 
0744     for (qint32 i = 0; i < m_yRadius && i < rect.height(); i++) // load top of image
0745         pixelSelection->readBytes(buf[i + 1], rect.x(), rect.y() + i, rect.width(), 1);
0746 
0747     if (m_edgeLock)
0748         memcpy(buf[0], buf[1], rect.width());
0749     else
0750         memset(buf[0], 0, rect.width());
0751 
0752 
0753     for (qint32 x = 0; x < rect.width(); x++) { // set up max for top of image
0754         max[x][0] = buf[0][x];
0755         for (qint32 j = 1; j < m_yRadius + 1; j++)
0756             max[x][j] = MIN(buf[j][x], max[x][j-1]);
0757     }
0758 
0759     for (qint32 y = 0; y < rect.height(); y++) {
0760         rotatePointers(buf, m_yRadius + 1);
0761         if (y < rect.height() - m_yRadius)
0762             pixelSelection->readBytes(buf[m_yRadius], rect.x(), rect.y() + y + m_yRadius, rect.width(), 1);
0763         else if (m_edgeLock)
0764             memcpy(buf[m_yRadius], buf[m_yRadius - 1], rect.width());
0765         else
0766             memset(buf[m_yRadius], 0, rect.width());
0767 
0768         for (qint32 x = 0 ; x < rect.width(); x++) { // update max array
0769             for (qint32 i = m_yRadius; i > 0; i--) {
0770                 max[x][i] = MIN(MIN(max[x][i - 1], buf[i - 1][x]), buf[i][x]);
0771             }
0772             max[x][0] = buf[0][x];
0773         }
0774         last_max =  max[0][circ[-1]];
0775         last_index = 0;
0776 
0777         for (qint32 x = 0 ; x < rect.width(); x++) { // render scan line
0778             last_index--;
0779             if (last_index >= 0) {
0780                 if (last_max == 0)
0781                     out[x] = 0;
0782                 else {
0783                     last_max = 255;
0784                     for (qint32 i = m_xRadius; i >= 0; i--)
0785                         if (last_max > max[x + i][circ[i]]) {
0786                             last_max = max[x + i][circ[i]];
0787                             last_index = i;
0788                         }
0789                     out[x] = last_max;
0790                 }
0791             } else {
0792                 last_index = m_xRadius;
0793                 last_max = max[x + m_xRadius][circ[m_xRadius]];
0794                 for (qint32 i = m_xRadius - 1; i >= -m_xRadius; i--)
0795                     if (last_max > max[x + i][circ[i]]) {
0796                         last_max = max[x + i][circ[i]];
0797                         last_index = i;
0798                     }
0799                 out[x] = last_max;
0800             }
0801         }
0802         pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1);
0803     }
0804 
0805     // undo the offsets to the pointers so we can free the malloced memory
0806     circ -= m_xRadius;
0807     max -= m_xRadius;
0808 
0809     delete[] circ;
0810     delete[] buffer;
0811     delete[] max;
0812     for (qint32 i = 0; i < m_yRadius + 1; i++)
0813         delete[] buf[i];
0814     delete[] buf;
0815     delete[] out;
0816 }
0817 
0818 
0819 KUndo2MagicString KisSmoothSelectionFilter::name()
0820 {
0821     return kundo2_i18n("Smooth Selection");
0822 }
0823 
0824 QRect KisSmoothSelectionFilter::changeRect(const QRect& rect, KisDefaultBoundsBaseSP defaultBounds)
0825 {
0826     Q_UNUSED(defaultBounds);
0827 
0828     const qint32 radius = 1;
0829     return rect.adjusted(-radius, -radius, radius, radius);
0830 }
0831 
0832 void KisSmoothSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0833 {
0834     // Simple convolution filter to smooth a mask (1bpp)
0835     quint8      *buf[3];
0836 
0837     qint32 width = rect.width();
0838     qint32 height = rect.height();
0839 
0840 
0841     quint8* out = new quint8[width];
0842     for (qint32 i = 0; i < 3; i++)
0843         buf[i] = new quint8[width + 2];
0844 
0845 
0846     // load top of image
0847     pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1);
0848 
0849     buf[0][0]         = buf[0][1];
0850     buf[0][width + 1] = buf[0][width];
0851 
0852     memcpy(buf[1], buf[0], width + 2);
0853 
0854     for (qint32 y = 0; y < height; y++) {
0855         if (y + 1 < height) {
0856             pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1);
0857 
0858             buf[2][0]         = buf[2][1];
0859             buf[2][width + 1] = buf[2][width];
0860         } else {
0861             memcpy(buf[2], buf[1], width + 2);
0862         }
0863 
0864         for (qint32 x = 0 ; x < width; x++) {
0865             qint32 value = (buf[0][x] + buf[0][x+1] + buf[0][x+2] +
0866                             buf[1][x] + buf[2][x+1] + buf[1][x+2] +
0867                             buf[2][x] + buf[1][x+1] + buf[2][x+2]);
0868 
0869             out[x] = value / 9;
0870         }
0871 
0872         pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1);
0873         rotatePointers(buf, 3);
0874     }
0875 
0876     for (qint32 i = 0; i < 3; i++)
0877         delete[] buf[i];
0878     delete[] out;
0879 }
0880 
0881 
0882 KUndo2MagicString KisInvertSelectionFilter::name()
0883 {
0884     return kundo2_i18n("Invert Selection");
0885 }
0886 
0887 QRect KisInvertSelectionFilter::changeRect(const QRect &rect, KisDefaultBoundsBaseSP defaultBounds)
0888 {
0889     Q_UNUSED(rect);
0890     return defaultBounds->bounds();
0891 }
0892 
0893 void KisInvertSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect)
0894 {
0895     Q_UNUSED(rect);
0896     pixelSelection->invert();
0897 }
0898 
0899 constexpr qint32 KisAntiAliasSelectionFilter::offsets[numSteps];
0900 
0901 KUndo2MagicString KisAntiAliasSelectionFilter::name()
0902 {
0903     return kundo2_i18n("Anti-Alias Selection");
0904 }
0905 
0906 bool KisAntiAliasSelectionFilter::getInterpolationValue(qint32 negativeSpanEndDistance,
0907                                                         qint32 positiveSpanEndDistance,
0908                                                         qint32 negativePixelDiff,
0909                                                         qint32 positivePixelDiff,
0910                                                         qint32 currentPixelDiff,
0911                                                         bool negativeSpanExtremeValid,
0912                                                         bool positiveSpanExtremeValid,
0913                                                         qint32 *interpolationValue) const
0914 {
0915     // Since we search a limited number of steps in each direction of the
0916     // current pixel, the end pixel of the span may still belong to the edge.
0917     // So we check for that, and if that's the case we must not smooth the
0918     // current pixel 
0919     const bool pixelDiffLessThanZero = currentPixelDiff < 0;
0920     quint32 distance;
0921     if (negativeSpanEndDistance < positiveSpanEndDistance) {
0922         if (!negativeSpanExtremeValid) {
0923             return false;
0924         }
0925         // The pixel is closer to the negative end
0926         const bool spanEndPixelDiffLessThanZero = negativePixelDiff < 0;
0927         if (pixelDiffLessThanZero == spanEndPixelDiffLessThanZero) {
0928             return false;
0929         }
0930         distance = negativeSpanEndDistance;
0931     } else {
0932         if (!positiveSpanExtremeValid) {
0933             return false;
0934         }
0935         // The pixel is closer to the positive end
0936         const bool spanEndPixelDiffLessThanZero = positivePixelDiff < 0;
0937         if (pixelDiffLessThanZero == spanEndPixelDiffLessThanZero) {
0938             return false;
0939         }
0940         distance = positiveSpanEndDistance;
0941     }
0942     const qint32 spanLength = positiveSpanEndDistance + negativeSpanEndDistance;
0943     *interpolationValue = ((distance << 8) / spanLength) + 128;
0944     return *interpolationValue >= 0;
0945 }
0946 
0947 void KisAntiAliasSelectionFilter::findSpanExtreme(quint8 **scanlines, qint32 x, qint32 pixelOffset,
0948                                                   qint32 rowMultiplier, qint32 colMultiplier, qint32 direction,
0949                                                   qint32 pixelAvg, qint32 scaledGradient, qint32 currentPixelDiff,
0950                                                   qint32 *spanEndDistance, qint32 *pixelDiff, bool *spanExtremeValid) const
0951 {
0952     *spanEndDistance = 0;
0953     *spanExtremeValid = true;
0954     for (qint32 i = 0; i < numSteps; ++i) {
0955         *spanEndDistance += offsets[i];
0956         const qint32 row1 = currentScanlineIndex + (direction * *spanEndDistance * rowMultiplier);
0957         const qint32 col1 = x + horizontalBorderSize + (direction * *spanEndDistance * colMultiplier);
0958         const qint32 row2 = row1 + pixelOffset * colMultiplier;
0959         const qint32 col2 = col1 + pixelOffset * rowMultiplier;
0960         const quint8 *pixel1 = scanlines[row1] + col1;
0961         const quint8 *pixel2 = scanlines[row2] + col2;
0962         // Get how different are these edge pixels from the current pixels and
0963         // stop searching if they are too different
0964         *pixelDiff = ((*pixel1 + *pixel2) >> 1) - pixelAvg;
0965         if (qAbs(*pixelDiff) > scaledGradient) {
0966             // If this is the end of the span then check if the corner belongs
0967             // to a jagged border or to a right angled part of the shape
0968             qint32 pixelDiff2;
0969             if ((currentPixelDiff < 0 && *pixelDiff < 0) || (currentPixelDiff > 0 && *pixelDiff > 0)) {
0970                 const qint32 row3 = row2 + pixelOffset * colMultiplier;
0971                 const qint32 col3 = col2 + pixelOffset * rowMultiplier;
0972                 const quint8 *pixel3 = scanlines[row3] + col3;
0973                 pixelDiff2 = ((*pixel2 + *pixel3) >> 1) - pixelAvg;
0974             } else {
0975                 const qint32 row3 = row1 - pixelOffset * colMultiplier;
0976                 const qint32 col3 = col1 - pixelOffset * rowMultiplier;
0977                 const quint8 *pixel3 = scanlines[row3] + col3;
0978                 pixelDiff2 = ((*pixel1 + *pixel3) >> 1) - pixelAvg;
0979             }
0980             *spanExtremeValid = !(qAbs(pixelDiff2) > scaledGradient);
0981             break;
0982         }
0983     }
0984 }
0985 
0986 void KisAntiAliasSelectionFilter::findSpanExtremes(quint8 **scanlines, qint32 x, qint32 pixelOffset,
0987                                                    qint32 rowMultiplier, qint32 colMultiplier,
0988                                                    qint32 pixelAvg, qint32 scaledGradient, qint32 currentPixelDiff,
0989                                                    qint32 *negativeSpanEndDistance, qint32 *positiveSpanEndDistance,
0990                                                    qint32 *negativePixelDiff, qint32 *positivePixelDiff,
0991                                                    bool *negativeSpanExtremeValid, bool *positiveSpanExtremeValid) const
0992 {
0993     findSpanExtreme(scanlines, x, pixelOffset, rowMultiplier, colMultiplier, -1, pixelAvg, scaledGradient,
0994                     currentPixelDiff, negativeSpanEndDistance, negativePixelDiff, negativeSpanExtremeValid);
0995     findSpanExtreme(scanlines, x, pixelOffset, rowMultiplier, colMultiplier, 1, pixelAvg, scaledGradient,
0996                     currentPixelDiff, positiveSpanEndDistance, positivePixelDiff, positiveSpanExtremeValid);
0997 }
0998 
0999 void KisAntiAliasSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect &rect)
1000 {
1001     const quint8 defaultPixel = *pixelSelection->defaultPixel().data();
1002     // Size of a scanline
1003     const quint32 bytesPerScanline = rect.width() + 2 * horizontalBorderSize;
1004     // Size of a scanline padded to a multiple of 8
1005     const quint32 bytesPerPaddedScanline = ((bytesPerScanline + 7) / 8) * 8;
1006 
1007     // This buffer contains the number of consecutive scanlines needed to
1008     // process the current scanline
1009     QVector<quint8> buffer(bytesPerPaddedScanline * numberOfScanlines);
1010 
1011     // These pointers point to the individual scanlines in the buffer
1012     quint8 *scanlines[numberOfScanlines];
1013     for (quint32 i = 0; i < numberOfScanlines; ++i) {
1014         scanlines[i] = buffer.data() + i * bytesPerPaddedScanline;
1015     }
1016 
1017     // Initialize the scanlines
1018     // Set the border scanlines on the top
1019     for (qint32 i = 0; i < verticalBorderSize; ++i) {
1020         memset(scanlines[i], defaultPixel, bytesPerScanline);
1021     }
1022     // Copy the first scanlines of the image
1023     const quint32 numberOfFirstRows = qMin(rect.height(), numberOfScanlines - verticalBorderSize);
1024     for (quint32 i = verticalBorderSize; i < verticalBorderSize + numberOfFirstRows; ++i) {
1025         // Set the border pixels on the left
1026         memset(scanlines[i], defaultPixel, horizontalBorderSize);
1027         // Copy the pixel data
1028         pixelSelection->readBytes(scanlines[i] + horizontalBorderSize, rect.x(), rect.y() + i - verticalBorderSize, rect.width(), 1);
1029         // Set the border pixels on the right
1030         memset(scanlines[i] + horizontalBorderSize + rect.width(), defaultPixel, horizontalBorderSize);
1031     }
1032     // Set the border scanlines on the bottom
1033     if (verticalBorderSize + numberOfFirstRows < numberOfScanlines) {
1034         for (quint32 i = verticalBorderSize + numberOfFirstRows; i < numberOfScanlines; ++i) {
1035             memset(scanlines[i], defaultPixel, bytesPerScanline);
1036         }
1037     }
1038     // Bufffer that contains the current output scanline
1039     QVector<quint8> antialiasedScanline(rect.width());
1040     // Main loop
1041     for (int y = 0; y < rect.height(); ++y)
1042     {
1043         // Move to the next scanline
1044         if (y > 0) {
1045             // Update scanline pointers
1046             std::rotate(std::begin(scanlines), std::begin(scanlines) + 1, std::end(scanlines));
1047             // Copy the next scanline
1048             if (y < rect.height() - verticalBorderSize) {
1049                 // Set the border pixels on the left
1050                 memset(scanlines[numberOfScanlines - 1], defaultPixel, horizontalBorderSize);
1051                 // Copy the pixel data
1052                 pixelSelection->readBytes(scanlines[numberOfScanlines - 1] + horizontalBorderSize, rect.x(), rect.y() + y + verticalBorderSize, rect.width(), 1);
1053                 // Set the border pixels on the right
1054                 memset(scanlines[numberOfScanlines - 1] + horizontalBorderSize + rect.width(), defaultPixel, horizontalBorderSize);
1055             } else {
1056                 memset(scanlines[numberOfScanlines - 1], defaultPixel, bytesPerScanline);
1057             }
1058         }
1059         // Process the pixels in the current scanline
1060         for (int x = 0; x < rect.width(); ++x)
1061         {
1062             // Get the current pixel and neighbors
1063             quint8 *pixelPtrM = scanlines[currentScanlineIndex    ] + x + horizontalBorderSize;
1064             quint8 *pixelPtrN = scanlines[currentScanlineIndex - 1] + x + horizontalBorderSize;
1065             quint8 *pixelPtrS = scanlines[currentScanlineIndex + 1] + x + horizontalBorderSize;
1066             const qint32 pixelNW = *(pixelPtrN - 1);
1067             const qint32 pixelN  = *(pixelPtrN    );
1068             const qint32 pixelNE = *(pixelPtrN + 1);
1069             const qint32 pixelW  = *(pixelPtrM - 1);
1070             const qint32 pixelM  = *(pixelPtrM    );
1071             const qint32 pixelE  = *(pixelPtrM + 1);
1072             const qint32 pixelSW = *(pixelPtrS - 1);
1073             const qint32 pixelS  = *(pixelPtrS    );
1074             const qint32 pixelSE = *(pixelPtrS + 1);
1075             // Get the gradients
1076             const qint32 rowNSum = (pixelNW >> 2) + (pixelN >> 1) + (pixelNE >> 2);
1077             const qint32 rowMSum = (pixelW  >> 2) + (pixelM >> 1) + (pixelE  >> 2);
1078             const qint32 rowSSum = (pixelSW >> 2) + (pixelS >> 1) + (pixelSE >> 2);
1079             const qint32 colWSum = (pixelNW >> 2) + (pixelW >> 1) + (pixelSW >> 2);
1080             const qint32 colMSum = (pixelN  >> 2) + (pixelM >> 1) + (pixelS  >> 2);
1081             const qint32 colESum = (pixelNE >> 2) + (pixelE >> 1) + (pixelSE >> 2);
1082             const qint32 gradientN = qAbs(rowMSum - rowNSum);
1083             const qint32 gradientS = qAbs(rowSSum - rowMSum);
1084             const qint32 gradientW = qAbs(colMSum - colWSum);
1085             const qint32 gradientE = qAbs(colESum - colMSum);
1086             // Get the maximum gradient
1087             const qint32 maxGradientNS = qMax(gradientN, gradientS);
1088             const qint32 maxGradientWE = qMax(gradientW, gradientE);
1089             const qint32 maxGradient = qMax(maxGradientNS, maxGradientWE);
1090             // Return early if the gradient is bellow some threshold (given by
1091             // the value bellow which the jagged edge is not noticeable)
1092             if (maxGradient < edgeThreshold) {
1093                 antialiasedScanline[x] = pixelM;
1094                 continue;
1095             }
1096             // Collect some info about the pixel and neighborhood
1097             qint32 neighborPixel, gradient;
1098             qint32 pixelOffset, rowMultiplier, colMultiplier;
1099             if (maxGradientNS > maxGradientWE) {
1100                 // Horizontal span
1101                 if (gradientN > gradientS) {
1102                     // The edge is formed with the top pixel
1103                     neighborPixel = pixelN;
1104                     gradient = gradientN;
1105                     pixelOffset = -1;
1106                 } else {
1107                     // The edge is formed with the bottom pixel
1108                     neighborPixel = pixelS;
1109                     gradient = gradientS;
1110                     pixelOffset = 1;
1111                 }
1112                 rowMultiplier = 0;
1113                 colMultiplier = 1;
1114             } else {
1115                 // Vertical span
1116                 if (gradientW > gradientE) {
1117                     // The edge is formed with the left pixel
1118                     neighborPixel = pixelW;
1119                     gradient = gradientW;
1120                     pixelOffset = -1;
1121                 } else {
1122                     // The edge is formed with the right pixel
1123                     neighborPixel = pixelE;
1124                     gradient = gradientE;
1125                     pixelOffset = 1;
1126                 }
1127                 rowMultiplier = 1;
1128                 colMultiplier = 0;
1129             }
1130             // Find the span extremes
1131             const qint32 pixelAvg = (neighborPixel + pixelM) >> 1;
1132             const qint32 currentPixelDiff = pixelM - pixelAvg;
1133             qint32 negativePixelDiff, positivePixelDiff;
1134             qint32 negativeSpanEndDistance, positiveSpanEndDistance;
1135             bool negativeSpanExtremeValid, positiveSpanExtremeValid;
1136             findSpanExtremes(scanlines, x, pixelOffset,
1137                              rowMultiplier, colMultiplier,
1138                              pixelAvg, gradient >> 2, currentPixelDiff,
1139                              &negativeSpanEndDistance, &positiveSpanEndDistance,
1140                              &negativePixelDiff, &positivePixelDiff,
1141                              &negativeSpanExtremeValid, &positiveSpanExtremeValid);
1142             // Get the interpolation value for this pixel given the span extent
1143             // and perform linear interpolation between the current pixel and
1144             // the edge neighbor
1145             qint32 interpolationValue;
1146             if (!getInterpolationValue(negativeSpanEndDistance, positiveSpanEndDistance,
1147                                        negativePixelDiff, positivePixelDiff, currentPixelDiff,
1148                                        negativeSpanExtremeValid, positiveSpanExtremeValid, &interpolationValue)) {
1149                 antialiasedScanline[x] = pixelM;
1150             } else {
1151                 antialiasedScanline[x] = neighborPixel + ((pixelM - neighborPixel) * interpolationValue >> 8);
1152             }
1153         }
1154         // Copy the scanline data to the mask
1155         pixelSelection->writeBytes(antialiasedScanline.data(), rect.x(), rect.y() + y, rect.width(), 1);
1156     }
1157 }