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

0001 /*
0002  * KDE. Krita Project.
0003  *
0004  * SPDX-FileCopyrightText: 2022 Deif Lou <ginoba@gmail.com>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include <krita_utils.h>
0010 #include <floodfill/kis_scanline_fill.h>
0011 #include <kis_selection_filters.h>
0012 #include <kis_iterator_ng.h>
0013 #include <KoUpdater.h>
0014 #include <kis_default_bounds.h>
0015 #include <KisImageResolutionProxy.h>
0016 
0017 #include "KisEncloseAndFillPainter.h"
0018 
0019 namespace KisEncloseAndFillPainterDetail {
0020 
0021     struct DifferencePolicyBase
0022     {
0023         const KoColorSpace *colorSpace;
0024         const KoColor color;
0025     };
0026 
0027     struct TransparentDifferencePolicy : public DifferencePolicyBase
0028     {
0029         TransparentDifferencePolicy(const KoColorSpace *colorSpace, const KoColor &color)
0030             : DifferencePolicyBase{colorSpace, color}
0031         {}
0032         // differences in the range [0, 100]
0033         quint8 getDifferenceFor(const quint8 *srcData) const
0034         {
0035             return static_cast<quint8>(colorSpace->opacityU8(srcData) * 100 / quint8_MAX);
0036         }
0037     };
0038 
0039     struct SpecificColorDifferencePolicy : public DifferencePolicyBase
0040     {
0041         SpecificColorDifferencePolicy(const KoColorSpace *colorSpace, const KoColor &color)
0042             : DifferencePolicyBase{colorSpace, color.convertedTo(colorSpace)}
0043         {}
0044         // differences in the range [0, 100]
0045         quint8 getDifferenceFor(const quint8 *srcData) const
0046         {
0047             return colorSpace->differenceA(srcData, color.data());
0048         }
0049     };
0050 
0051     struct TransparentForHalosDifferencePolicy : public DifferencePolicyBase
0052     {
0053         TransparentForHalosDifferencePolicy(const KoColorSpace *colorSpace, const KoColor &color)
0054             : DifferencePolicyBase{colorSpace, color.convertedTo(colorSpace)}
0055         {}
0056         // differences in the range [0, 100]
0057         quint8 getDifferenceFor(const quint8 *srcData) const
0058         {
0059             const quint8 opacity = colorSpace->opacityU8(srcData);
0060             if (opacity == quint8_MAX) {
0061                 return 100;
0062             }
0063             const quint8 colorDifference = colorSpace->difference(srcData, color.data());
0064             const quint8 opacityDifference = opacity * 100 / quint8_MAX;
0065             return qMin(colorDifference, opacityDifference);
0066         }
0067     };
0068 
0069     struct SpecificColorOrTransparentDifferencePolicy : public DifferencePolicyBase
0070     {
0071         SpecificColorOrTransparentDifferencePolicy(const KoColorSpace *colorSpace, const KoColor &color)
0072             : DifferencePolicyBase{colorSpace, color.convertedTo(colorSpace)}
0073         {}
0074         // differences in the range [0, 100]
0075         quint8 getDifferenceFor(const quint8 *srcData) const
0076         {
0077             const quint8 colorDifference = colorSpace->difference(srcData, color.data());
0078             const quint8 opacityDifference = colorSpace->opacityU8(srcData) * 100 / quint8_MAX;
0079             return qMin(colorDifference, opacityDifference);
0080         }
0081     };
0082 
0083     template <typename DifferencePolicy>
0084     struct HardSelectionPolicy : public DifferencePolicy
0085     {
0086         const int threshold;
0087         HardSelectionPolicy(const KoColorSpace *colorSpace, const KoColor &color, int threshold)
0088             : DifferencePolicy(colorSpace, color)
0089             , threshold(threshold)
0090         {}
0091         // differences in the range [0, 100]
0092         quint8 getSelectionFor(const quint8 *srcData) const
0093         {
0094             return this->getDifferenceFor(srcData) <= threshold ? MAX_SELECTED : MIN_SELECTED;
0095         }
0096     };
0097 
0098     template <typename DifferencePolicy>
0099     struct SoftSelectionPolicy : public DifferencePolicy
0100     {
0101         const int threshold;
0102         const int softness;
0103         SoftSelectionPolicy(const KoColorSpace *colorSpace, const KoColor &color, int threshold, int softness)
0104             : DifferencePolicy(colorSpace, color)
0105             , threshold(threshold)
0106             , softness(softness)
0107         {}
0108         // differences in the range [0, 100]
0109         quint8 getSelectionFor(const quint8 *srcData) const
0110         {
0111             if (threshold == 0) {
0112                 return MIN_SELECTED;
0113             }
0114             // Integer version of: (threshold - diff) / (threshold * softness)
0115             const int diff = this->getDifferenceFor(srcData);
0116             if (diff < threshold) {
0117                 const int v = (threshold - diff) * MAX_SELECTED * 100 / (threshold * softness);
0118                 return v > MAX_SELECTED ? MAX_SELECTED : v;
0119             } else {
0120                 return MIN_SELECTED;
0121             }
0122         }
0123     };
0124 };
0125 
0126 class KisEncloseAndFillPainter::Private
0127 {
0128 public:
0129     KisEncloseAndFillPainter *q {nullptr};
0130     RegionSelectionMethod regionSelectionMethod {SelectAllRegions};
0131     KoColor regionSelectionColor;
0132     bool regionSelectionInvert {false};
0133     bool regionSelectionIncludeContourRegions {true};
0134     bool regionSelectionIncludeSurroundingRegions {true};
0135     QRect imageRect;
0136 
0137     Private(KisEncloseAndFillPainter *q) : q(q) {}
0138     
0139     void computeEnclosedRegionsMask(KisPixelSelectionSP resultMask,
0140                                     QRect *resultMaskRect,
0141                                     KisPixelSelectionSP enclosingMask,
0142                                     const QRect &enclosingMaskRect,
0143                                     KisPaintDeviceSP referenceDevice) const;
0144 
0145     void selectAllRegions(KisPixelSelectionSP resultMask,
0146                           QRect *resultMaskRect,
0147                           KisPixelSelectionSP enclosingMask,
0148                           const QRect &enclosingMaskRect,
0149                           KisPaintDeviceSP referenceDevice) const;
0150 
0151     void selectRegionsFilledWithSpecificColor(KisPixelSelectionSP resultMask,
0152                                               QRect *resultMaskRect,
0153                                               KisPixelSelectionSP enclosingMask,
0154                                               const QRect &enclosingMaskRect,
0155                                               KisPaintDeviceSP referenceDevice) const;
0156     void selectRegionsFilledWithTransparent(KisPixelSelectionSP resultMask,
0157                                             QRect *resultMaskRect,
0158                                             KisPixelSelectionSP enclosingMask,
0159                                             const QRect &enclosingMaskRect,
0160                                             KisPaintDeviceSP referenceDevice) const;
0161     void selectRegionsFilledWithSpecificColorOrTransparent(KisPixelSelectionSP resultMask,
0162                                                            QRect *resultMaskRect,
0163                                                            KisPixelSelectionSP enclosingMask,
0164                                                            const QRect &enclosingMaskRect,
0165                                                            KisPaintDeviceSP referenceDevice) const;
0166     template <typename SelectionPolicy>
0167     void selectRegionsFilledWithSpecificColorGeneric(KisPixelSelectionSP resultMask,
0168                                                      QRect *resultMaskRect,
0169                                                      KisPixelSelectionSP enclosingMask,
0170                                                      const QRect &enclosingMaskRect,
0171                                                      KisPaintDeviceSP referenceDevice,
0172                                                      SelectionPolicy selectionPolicy) const;
0173 
0174     void selectAllRegionsExceptFilledWithSpecificColor(KisPixelSelectionSP resultMask,
0175                                                        QRect *resultMaskRect,
0176                                                        KisPixelSelectionSP enclosingMask,
0177                                                        const QRect &enclosingMaskRect,
0178                                                        KisPaintDeviceSP referenceDevice) const;
0179     void selectAllRegionsExceptFilledWithTransparent(KisPixelSelectionSP resultMask,
0180                                                      QRect *resultMaskRect,
0181                                                      KisPixelSelectionSP enclosingMask,
0182                                                      const QRect &enclosingMaskRect,
0183                                                      KisPaintDeviceSP referenceDevice) const;
0184     void selectAllRegionsExceptFilledWithSpecificColorOrTransparent(KisPixelSelectionSP resultMask,
0185                                                                     QRect *resultMaskRect,
0186                                                                     KisPixelSelectionSP enclosingMask,
0187                                                                     const QRect &enclosingMaskRect,
0188                                                                     KisPaintDeviceSP referenceDevice) const;
0189     template <typename SelectionPolicy>
0190     void selectAllRegionsExceptFilledWithSpecificColorGeneric(KisPixelSelectionSP resultMask,
0191                                                               QRect *resultMaskRect,
0192                                                               KisPixelSelectionSP enclosingMask,
0193                                                               const QRect &enclosingMaskRect,
0194                                                               KisPaintDeviceSP referenceDevice,
0195                                                               SelectionPolicy selectionPolicy) const;
0196 
0197     void selectRegionsSurroundedBySpecificColor(KisPixelSelectionSP resultMask,
0198                                                 QRect *resultMaskRect,
0199                                                 KisPixelSelectionSP enclosingMask,
0200                                                 const QRect &enclosingMaskRect,
0201                                                 KisPaintDeviceSP referenceDevice) const;
0202     void selectRegionsSurroundedByTransparent(KisPixelSelectionSP resultMask,
0203                                               QRect *resultMaskRect,
0204                                               KisPixelSelectionSP enclosingMask,
0205                                               const QRect &enclosingMaskRect,
0206                                               KisPaintDeviceSP referenceDevice) const;
0207     void selectRegionsSurroundedBySpecificColorOrTransparent(KisPixelSelectionSP resultMask,
0208                                                              QRect *resultMaskRect,
0209                                                              KisPixelSelectionSP enclosingMask,
0210                                                              const QRect &enclosingMaskRect,
0211                                                              KisPaintDeviceSP referenceDevice) const;
0212     template <typename SelectionPolicy>
0213     void selectRegionsSurroundedBySpecificColorGeneric(KisPixelSelectionSP resultMask,
0214                                                        QRect *resultMaskRect,
0215                                                        KisPixelSelectionSP enclosingMask,
0216                                                        const QRect &enclosingMaskRect,
0217                                                        KisPaintDeviceSP referenceDevice,
0218                                                        SelectionPolicy selectionPolicy,
0219                                                        bool colorOrTransparent = false) const;
0220 
0221     QVector<QPoint> getEnclosingContourPoints(KisPixelSelectionSP enclosingMask,
0222                                               const QRect &enclosingMaskRect) const;
0223 
0224     void applyPostProcessing(KisPixelSelectionSP mask,
0225                              KisPaintDeviceSP referenceDevice) const;
0226 
0227     void invertIfNeeded(KisPixelSelectionSP resultMask, KisPixelSelectionSP enclosingMask) const;
0228 
0229     template <typename SelectionPolicy>
0230     int selectSimilarRegions(KisPixelSelectionSP resultMask,
0231                              KisPixelSelectionSP enclosingMask,
0232                              const QRect &enclosingMaskRect,
0233                              KisPaintDeviceSP referenceDevice,
0234                              SelectionPolicy selectionPolicy) const;
0235     template <typename SelectionPolicy>
0236     int selectDissimilarRegions(KisPixelSelectionSP resultMask,
0237                                 KisPixelSelectionSP enclosingMask,
0238                                 const QRect &enclosingMaskRect,
0239                                 KisPaintDeviceSP referenceDevice,
0240                                 SelectionPolicy selectionPolicy) const;
0241 
0242     void selectRegionsFromContour(KisPixelSelectionSP resultMask,
0243                                   KisPixelSelectionSP enclosingMask,
0244                                   const QRect &enclosingMaskRect,
0245                                   KisPaintDeviceSP referenceDevice) const;
0246     void selectRegionsFromContour(KisPixelSelectionSP resultMask,
0247                                   KisPixelSelectionSP enclosingMask,
0248                                   const QVector<QPoint> &enclosingPoints,
0249                                   const QRect &enclosingMaskRect,
0250                                   KisPaintDeviceSP referenceDevice) const;
0251 
0252     void selectRegionsFromContourUntilColor(KisPixelSelectionSP resultMask,
0253                                             KisPixelSelectionSP enclosingMask,
0254                                             const QRect &enclosingMaskRect,
0255                                             KisPaintDeviceSP referenceDevice,
0256                                             const KoColor &color) const;
0257     void selectRegionsFromContourUntilColor(KisPixelSelectionSP resultMask,
0258                                             KisPixelSelectionSP enclosingMask,
0259                                             const QVector<QPoint> &enclosingPoints,
0260                                             const QRect &enclosingMaskRect,
0261                                             KisPaintDeviceSP referenceDevice,
0262                                             const KoColor &color) const;
0263 
0264     void selectRegionsFromContourUntilColorOrTransparent(KisPixelSelectionSP resultMask,
0265                                                          KisPixelSelectionSP enclosingMask,
0266                                                          const QRect &enclosingMaskRect,
0267                                                          KisPaintDeviceSP referenceDevice,
0268                                                          const KoColor &color) const;
0269     void selectRegionsFromContourUntilColorOrTransparent(KisPixelSelectionSP resultMask,
0270                                                          KisPixelSelectionSP enclosingMask,
0271                                                          const QVector<QPoint> &enclosingPoints,
0272                                                          const QRect &enclosingMaskRect,
0273                                                          KisPaintDeviceSP referenceDevice,
0274                                                          const KoColor &color) const;
0275 
0276     void removeContourRegions(KisPixelSelectionSP resultMask,
0277                               KisPixelSelectionSP enclosingMask,
0278                               const QRect &enclosingMaskRect) const;
0279     void removeContourRegions(KisPixelSelectionSP resultMask,
0280                               const QVector<QPoint> &enclosingPoints,
0281                               const QRect &enclosingMaskRect) const;
0282 
0283     void subtractSelectionsSpecial(KisPixelSelectionSP mask1,
0284                                    KisPixelSelectionSP mask2,
0285                                    const QRect &rect) const;
0286 };
0287 
0288 KisEncloseAndFillPainter::KisEncloseAndFillPainter(const QSize &imageSize)
0289     : m_d(new Private(this))
0290 {
0291     setWidth(imageSize.width());
0292     setHeight(imageSize.height());
0293     m_d->imageRect = QRect(QPoint(0, 0), imageSize);
0294 }
0295 
0296 KisEncloseAndFillPainter::KisEncloseAndFillPainter(KisPaintDeviceSP device, const QSize &imageSize)
0297     : KisFillPainter(device)
0298     , m_d(new Private(this))
0299 {
0300     setWidth(imageSize.width());
0301     setHeight(imageSize.height());
0302     m_d->imageRect = QRect(QPoint(0, 0), imageSize);
0303 }
0304 
0305 KisEncloseAndFillPainter::KisEncloseAndFillPainter(KisPaintDeviceSP device, KisSelectionSP selection, const QSize &imageSize)
0306     : KisFillPainter(device, selection)
0307     , m_d(new Private(this))
0308 {
0309     setWidth(imageSize.width());
0310     setHeight(imageSize.height());
0311     m_d->imageRect = QRect(QPoint(0, 0), imageSize);
0312 }
0313 
0314 KisEncloseAndFillPainter::~KisEncloseAndFillPainter()
0315 {}
0316 
0317 void KisEncloseAndFillPainter::encloseAndFillColor(KisPixelSelectionSP enclosingMask, KisPaintDeviceSP referenceDevice)
0318 {
0319     genericEncloseAndFillStart(enclosingMask, referenceDevice);
0320 
0321     // Now create a layer and fill it
0322     KisPaintDeviceSP filled = device()->createCompositionSourceDevice();
0323     const QRect fillRect = currentFillSelection()->selectedExactRect();
0324     Q_CHECK_PTR(filled);
0325     KisFillPainter painter(filled);
0326     painter.fillRect(fillRect, paintColor());
0327     painter.end();
0328 
0329     genericEncloseAndFillEnd(filled);
0330 }
0331 
0332 void KisEncloseAndFillPainter::encloseAndFillPattern(KisPixelSelectionSP enclosingMask,
0333                                                      KisPaintDeviceSP referenceDevice,
0334                                                      QTransform patternTransform)
0335 {
0336     genericEncloseAndFillStart(enclosingMask, referenceDevice);
0337 
0338     // Now create a layer and fill it
0339     KisPaintDeviceSP filled = device()->createCompositionSourceDevice();
0340     const QRect fillRect = currentFillSelection()->selectedExactRect();
0341     Q_CHECK_PTR(filled);
0342     KisFillPainter painter(filled);
0343     painter.fillRectNoCompose(fillRect, pattern(), patternTransform);
0344     painter.end();
0345 
0346     genericEncloseAndFillEnd(filled);
0347 }
0348 
0349 void KisEncloseAndFillPainter::genericEncloseAndFillStart(KisPixelSelectionSP enclosingMask, KisPaintDeviceSP referenceDevice)
0350 {
0351     // Create a selection from the closed regions
0352     KisPixelSelectionSP pixelSelection = createEncloseAndFillSelection(enclosingMask, referenceDevice,
0353                                                                        (selection().isNull() ? 0 : selection()->pixelSelection()));
0354     KisSelectionSP newSelection = new KisSelection(pixelSelection->defaultBounds(), KisImageResolutionProxy::identity());
0355     newSelection->pixelSelection()->applySelection(pixelSelection, SELECTION_REPLACE);
0356     setCurrentFillSelection(newSelection);
0357 }
0358 
0359 void KisEncloseAndFillPainter::genericEncloseAndFillEnd(KisPaintDeviceSP filled)
0360 {
0361     KisFillPainter::genericFillEnd(filled);
0362 }
0363 
0364 KisPixelSelectionSP KisEncloseAndFillPainter::createEncloseAndFillSelection(KisPixelSelectionSP enclosingMask,
0365                                                                             KisPaintDeviceSP referenceDevice,
0366                                                                             KisPixelSelectionSP existingSelection)
0367 {
0368     KisPixelSelectionSP newSelection = new KisPixelSelection(new KisSelectionDefaultBounds(device()));
0369     return createEncloseAndFillSelection(newSelection, enclosingMask, referenceDevice, existingSelection);
0370 }
0371 
0372 KisPixelSelectionSP KisEncloseAndFillPainter::createEncloseAndFillSelection(KisPixelSelectionSP newSelection,
0373                                                                             KisPixelSelectionSP enclosingMask,
0374                                                                             KisPaintDeviceSP referenceDevice,
0375                                                                             KisPixelSelectionSP existingSelection)
0376 {
0377     Q_ASSERT(newSelection);
0378     Q_ASSERT(enclosingMask);
0379     Q_ASSERT(referenceDevice);
0380 
0381     const QRect enclosingMaskRect = enclosingMask->selectedExactRect();
0382     if (enclosingMaskRect.isEmpty()) {
0383         return newSelection;
0384     }
0385     QRect newSelectionRect;
0386     // Get the mask that includes all the closed regions inside the enclosing mask
0387     m_d->computeEnclosedRegionsMask(newSelection, &newSelectionRect, enclosingMask, enclosingMaskRect, referenceDevice);
0388     if (newSelectionRect.isEmpty()) {
0389         return newSelection;
0390     }
0391     // Invert
0392     m_d->invertIfNeeded(newSelection, enclosingMask);
0393     // Intersect the regions mask with the current selection if it should be used as boundary
0394     if (useSelectionAsBoundary() && existingSelection) {
0395         newSelection->applySelection(existingSelection, SELECTION_INTERSECT);
0396     }
0397     // Post-process
0398     m_d->applyPostProcessing(newSelection, referenceDevice);
0399 
0400     return newSelection;
0401 }
0402 
0403 void KisEncloseAndFillPainter::setRegionSelectionMethod(RegionSelectionMethod regionSelectionMethod)
0404 {
0405     m_d->regionSelectionMethod = regionSelectionMethod;
0406 }
0407 
0408 KisEncloseAndFillPainter::RegionSelectionMethod KisEncloseAndFillPainter::regionSelectionMethod() const
0409 {
0410     return m_d->regionSelectionMethod;
0411 }
0412 
0413 void KisEncloseAndFillPainter::setRegionSelectionColor(const KoColor &color)
0414 {
0415     m_d->regionSelectionColor = color;
0416 }
0417 
0418 KoColor KisEncloseAndFillPainter::regionSelectionColor() const
0419 {
0420     return m_d->regionSelectionColor;
0421 }
0422 
0423 void KisEncloseAndFillPainter::setRegionSelectionInvert(bool invert)
0424 {
0425     m_d->regionSelectionInvert = invert;
0426 }
0427 
0428 bool KisEncloseAndFillPainter::regionSelectionInvert() const
0429 {
0430     return m_d->regionSelectionInvert;
0431 }
0432 
0433 void KisEncloseAndFillPainter::setRegionSelectionIncludeContourRegions(bool include)
0434 {
0435     m_d->regionSelectionIncludeContourRegions = include;
0436 }
0437 
0438 bool KisEncloseAndFillPainter::regionSelectionIncludeContourRegions() const
0439 {
0440     return m_d->regionSelectionIncludeContourRegions;
0441 }
0442 
0443 void KisEncloseAndFillPainter::setRegionSelectionIncludeSurroundingRegions(bool include)
0444 {
0445     m_d->regionSelectionIncludeSurroundingRegions = include;
0446 }
0447 
0448 bool KisEncloseAndFillPainter::regionSelectionIncludeSurroundingRegions() const
0449 {
0450     return m_d->regionSelectionIncludeSurroundingRegions;
0451 }
0452 
0453 void KisEncloseAndFillPainter::Private::computeEnclosedRegionsMask(KisPixelSelectionSP resultMask,
0454                                                                    QRect *resultMaskRect,
0455                                                                    KisPixelSelectionSP enclosingMask,
0456                                                                    const QRect &enclosingMaskRect,
0457                                                                    KisPaintDeviceSP referenceDevice) const
0458 {
0459     // Create the regions mask
0460     switch (regionSelectionMethod) {
0461         case SelectAllRegions:
0462             selectAllRegions(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice);
0463             break;
0464         case SelectRegionsFilledWithSpecificColor:
0465             selectRegionsFilledWithSpecificColor(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice);
0466             break;
0467         case SelectRegionsFilledWithTransparent:
0468             selectRegionsFilledWithTransparent(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice);
0469             break;
0470         case SelectRegionsFilledWithSpecificColorOrTransparent:
0471             selectRegionsFilledWithSpecificColorOrTransparent(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice);
0472             break;
0473         case SelectAllRegionsExceptFilledWithSpecificColor:
0474             selectAllRegionsExceptFilledWithSpecificColor(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice);
0475             break;
0476         case SelectAllRegionsExceptFilledWithTransparent:
0477             selectAllRegionsExceptFilledWithTransparent(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice);
0478             break;
0479         case SelectAllRegionsExceptFilledWithSpecificColorOrTransparent:
0480             selectAllRegionsExceptFilledWithSpecificColorOrTransparent(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice);
0481             break;
0482         case SelectRegionsSurroundedBySpecificColor:
0483             selectRegionsSurroundedBySpecificColor(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice);
0484             break;
0485         case SelectRegionsSurroundedByTransparent:
0486             selectRegionsSurroundedByTransparent(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice);
0487             break;
0488         case SelectRegionsSurroundedBySpecificColorOrTransparent:
0489             selectRegionsSurroundedBySpecificColorOrTransparent(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice);
0490             break;
0491         default: return;
0492     }
0493 }
0494 
0495 void KisEncloseAndFillPainter::Private::selectAllRegions(KisPixelSelectionSP resultMask,
0496                                                          QRect *resultMaskRect,
0497                                                          KisPixelSelectionSP enclosingMask,
0498                                                          const QRect &enclosingMaskRect,
0499                                                          KisPaintDeviceSP referenceDevice) const
0500 {
0501     resultMask->applySelection(enclosingMask, SELECTION_REPLACE);
0502     KisPixelSelectionSP mask = new KisPixelSelection(new KisSelectionDefaultBounds(enclosingMask));
0503     selectRegionsFromContour(mask, enclosingMask, enclosingMaskRect, referenceDevice);
0504     resultMask->applySelection(mask, SELECTION_SUBTRACT);
0505     if (resultMaskRect) {
0506         *resultMaskRect = resultMask->selectedExactRect();
0507     }
0508 }
0509 
0510 void KisEncloseAndFillPainter::Private::selectRegionsFilledWithSpecificColor(KisPixelSelectionSP resultMask,
0511                                                                              QRect *resultMaskRect,
0512                                                                              KisPixelSelectionSP enclosingMask,
0513                                                                              const QRect &enclosingMaskRect,
0514                                                                              KisPaintDeviceSP referenceDevice) const
0515 {
0516     using namespace KisEncloseAndFillPainterDetail;
0517     const int softness = 100 - q->opacitySpread();
0518     if (softness == 0) {
0519         HardSelectionPolicy<SpecificColorDifferencePolicy> policy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold());
0520         selectRegionsFilledWithSpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, policy);
0521     } else {
0522         SoftSelectionPolicy<SpecificColorDifferencePolicy> policy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold(), softness);
0523         selectRegionsFilledWithSpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, policy);
0524     }
0525 }
0526 
0527 void KisEncloseAndFillPainter::Private::selectRegionsFilledWithTransparent(KisPixelSelectionSP resultMask,
0528                                                                            QRect *resultMaskRect,
0529                                                                            KisPixelSelectionSP enclosingMask,
0530                                                                            const QRect &enclosingMaskRect,
0531                                                                            KisPaintDeviceSP referenceDevice) const
0532 {
0533     using namespace KisEncloseAndFillPainterDetail;
0534     const int softness = 100 - q->opacitySpread();
0535     if (softness == 0) {
0536         HardSelectionPolicy<TransparentDifferencePolicy> policy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold());
0537         selectRegionsFilledWithSpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, policy);
0538     } else {
0539         SoftSelectionPolicy<TransparentDifferencePolicy> policy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold(), softness);
0540         selectRegionsFilledWithSpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, policy);
0541     }
0542 }
0543 
0544 void KisEncloseAndFillPainter::Private::selectRegionsFilledWithSpecificColorOrTransparent(KisPixelSelectionSP resultMask,
0545                                                                                           QRect *resultMaskRect,
0546                                                                                           KisPixelSelectionSP enclosingMask,
0547                                                                                           const QRect &enclosingMaskRect,
0548                                                                                           KisPaintDeviceSP referenceDevice) const
0549 {
0550     using namespace KisEncloseAndFillPainterDetail;
0551     const int softness = 100 - q->opacitySpread();
0552     // Here we must compute the specific color and transparent masks separately
0553     // so that the contour regions can be removed independently if they are
0554     // transparent or of the specific color and are connected
0555     KisPixelSelectionSP resultMaskTransparent = new KisPixelSelection(new KisSelectionDefaultBounds(resultMask));
0556     if (softness == 0) {
0557         HardSelectionPolicy<SpecificColorDifferencePolicy> colorPolicy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold());
0558         selectRegionsFilledWithSpecificColorGeneric(resultMask, nullptr, enclosingMask, enclosingMaskRect, referenceDevice, colorPolicy);
0559         HardSelectionPolicy<TransparentForHalosDifferencePolicy> transparentPolicy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold());
0560         selectRegionsFilledWithSpecificColorGeneric(resultMaskTransparent, nullptr, enclosingMask, enclosingMaskRect, referenceDevice, transparentPolicy);
0561     } else {
0562         SoftSelectionPolicy<SpecificColorDifferencePolicy> colorPolicy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold(), softness);
0563         selectRegionsFilledWithSpecificColorGeneric(resultMask, nullptr, enclosingMask, enclosingMaskRect, referenceDevice, colorPolicy);
0564         HardSelectionPolicy<TransparentForHalosDifferencePolicy> transparentPolicy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold());
0565         selectRegionsFilledWithSpecificColorGeneric(resultMaskTransparent, nullptr, enclosingMask, enclosingMaskRect, referenceDevice, transparentPolicy);
0566     }
0567     // Compose the masks
0568     resultMask->applySelection(resultMaskTransparent, SELECTION_ADD);
0569     if (resultMaskRect) {
0570         *resultMaskRect = resultMask->selectedExactRect();
0571     }
0572 }
0573 
0574 template <typename SelectionPolicy>
0575 void KisEncloseAndFillPainter::Private::selectRegionsFilledWithSpecificColorGeneric(KisPixelSelectionSP resultMask,
0576                                                                                     QRect *resultMaskRect,
0577                                                                                     KisPixelSelectionSP enclosingMask,
0578                                                                                     const QRect &enclosingMaskRect,
0579                                                                                     KisPaintDeviceSP referenceDevice,
0580                                                                                     SelectionPolicy selectionPolicy) const
0581 {
0582     // Select all the pixels using the given selection policy and
0583     // return if there are no selected pixels
0584     if (selectSimilarRegions(resultMask, enclosingMask, enclosingMaskRect, referenceDevice, selectionPolicy) == 0) {
0585         if (resultMaskRect) {
0586             *resultMaskRect = QRect();
0587         }
0588         return;
0589     }
0590     // Remove the regions that touch the enclosing area
0591     if (!regionSelectionIncludeContourRegions) {
0592         removeContourRegions(resultMask, enclosingMask, enclosingMaskRect);
0593     }
0594     if (resultMaskRect) {
0595         *resultMaskRect = resultMask->selectedExactRect();
0596     }
0597 }
0598 
0599 void KisEncloseAndFillPainter::Private::selectAllRegionsExceptFilledWithSpecificColor(KisPixelSelectionSP resultMask,
0600                                                                                       QRect *resultMaskRect,
0601                                                                                       KisPixelSelectionSP enclosingMask,
0602                                                                                       const QRect &enclosingMaskRect,
0603                                                                                       KisPaintDeviceSP referenceDevice) const
0604 {
0605     using namespace KisEncloseAndFillPainterDetail;
0606     const int softness = 100 - q->opacitySpread();
0607     if (softness == 0) {
0608         HardSelectionPolicy<SpecificColorDifferencePolicy> policy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold());
0609         selectAllRegionsExceptFilledWithSpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, policy);
0610     } else {
0611         SoftSelectionPolicy<SpecificColorDifferencePolicy> policy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold(), softness);
0612         selectAllRegionsExceptFilledWithSpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, policy);
0613     }
0614 }
0615 
0616 void KisEncloseAndFillPainter::Private::selectAllRegionsExceptFilledWithTransparent(KisPixelSelectionSP resultMask,
0617                                                                                     QRect *resultMaskRect,
0618                                                                                     KisPixelSelectionSP enclosingMask,
0619                                                                                     const QRect &enclosingMaskRect,
0620                                                                                     KisPaintDeviceSP referenceDevice) const
0621 {
0622     using namespace KisEncloseAndFillPainterDetail;
0623     const int softness = 100 - q->opacitySpread();
0624     if (softness == 0) {
0625         HardSelectionPolicy<TransparentDifferencePolicy> policy(referenceDevice->colorSpace(), KoColor::createTransparent(referenceDevice->colorSpace()), q->fillThreshold());
0626         selectAllRegionsExceptFilledWithSpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, policy);
0627     } else {
0628         SoftSelectionPolicy<TransparentDifferencePolicy> policy(referenceDevice->colorSpace(), KoColor::createTransparent(referenceDevice->colorSpace()), q->fillThreshold(), softness);
0629         selectAllRegionsExceptFilledWithSpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, policy);
0630     }
0631 }
0632 
0633 void KisEncloseAndFillPainter::Private::selectAllRegionsExceptFilledWithSpecificColorOrTransparent(KisPixelSelectionSP resultMask,
0634                                                                                                    QRect *resultMaskRect,
0635                                                                                                    KisPixelSelectionSP enclosingMask,
0636                                                                                                    const QRect &enclosingMaskRect,
0637                                                                                                    KisPaintDeviceSP referenceDevice) const
0638 {
0639     using namespace KisEncloseAndFillPainterDetail;
0640     const int softness = 100 - q->opacitySpread();
0641     // Here we must compute the specific color and transparent masks separately
0642     if (softness == 0) {
0643         HardSelectionPolicy<SpecificColorOrTransparentDifferencePolicy> policy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold());
0644         selectAllRegionsExceptFilledWithSpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, policy);
0645     } else {
0646         SoftSelectionPolicy<SpecificColorOrTransparentDifferencePolicy> policy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold(), softness);
0647         selectAllRegionsExceptFilledWithSpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, policy);
0648     }
0649 }
0650 
0651 template <typename SelectionPolicy>
0652 void KisEncloseAndFillPainter::Private::selectAllRegionsExceptFilledWithSpecificColorGeneric(KisPixelSelectionSP resultMask,
0653                                                                                              QRect *resultMaskRect,
0654                                                                                              KisPixelSelectionSP enclosingMask,
0655                                                                                              const QRect &enclosingMaskRect,
0656                                                                                              KisPaintDeviceSP referenceDevice,
0657                                                                                              SelectionPolicy selectionPolicy) const
0658 {
0659     const QVector<QPoint> enclosingPoints = getEnclosingContourPoints(enclosingMask, enclosingMaskRect);
0660     // Remove the regions that touch the enclosing area
0661     if (selectDissimilarRegions(resultMask, enclosingMask, enclosingMaskRect, referenceDevice, selectionPolicy) == 0) {
0662         if (resultMaskRect) {
0663             *resultMaskRect = QRect();
0664         }
0665         return;
0666     }
0667     if (!regionSelectionIncludeContourRegions) {
0668         // Here we don't use removeContourRegions because the contour regions
0669         // in the mask may include multiple connected color regions
0670         KisPixelSelectionSP mask = new KisPixelSelection(new KisSelectionDefaultBounds(enclosingMask));
0671         selectRegionsFromContour(mask, enclosingMask, enclosingPoints, enclosingMaskRect, referenceDevice);
0672         resultMask->applySelection(mask, SELECTION_SUBTRACT);
0673     }
0674     if (resultMaskRect) {
0675         *resultMaskRect = resultMask->selectedExactRect();
0676     }
0677 }
0678 
0679 void KisEncloseAndFillPainter::Private::selectRegionsSurroundedBySpecificColor(KisPixelSelectionSP resultMask,
0680                                                                                QRect *resultMaskRect,
0681                                                                                KisPixelSelectionSP enclosingMask,
0682                                                                                const QRect &enclosingMaskRect,
0683                                                                                KisPaintDeviceSP referenceDevice) const
0684 {
0685     using namespace KisEncloseAndFillPainterDetail;
0686     const int softness = 100 - q->opacitySpread();
0687     if (softness == 0) {
0688         HardSelectionPolicy<SpecificColorDifferencePolicy> policy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold());
0689         selectRegionsSurroundedBySpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, policy);
0690     } else {
0691         SoftSelectionPolicy<SpecificColorDifferencePolicy> policy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold(), softness);
0692         selectRegionsSurroundedBySpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, policy);
0693     }
0694 }
0695 
0696 void KisEncloseAndFillPainter::Private::selectRegionsSurroundedByTransparent(KisPixelSelectionSP resultMask,
0697                                                                              QRect *resultMaskRect,
0698                                                                              KisPixelSelectionSP enclosingMask,
0699                                                                              const QRect &enclosingMaskRect,
0700                                                                              KisPaintDeviceSP referenceDevice) const
0701 {
0702     using namespace KisEncloseAndFillPainterDetail;
0703     const int softness = 100 - q->opacitySpread();
0704     if (softness == 0) {
0705         HardSelectionPolicy<TransparentDifferencePolicy> policy(referenceDevice->colorSpace(), KoColor::createTransparent(referenceDevice->colorSpace()), q->fillThreshold());
0706         selectRegionsSurroundedBySpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, policy);
0707     } else {
0708         SoftSelectionPolicy<TransparentDifferencePolicy> policy(referenceDevice->colorSpace(), KoColor::createTransparent(referenceDevice->colorSpace()), q->fillThreshold(), softness);
0709         selectRegionsSurroundedBySpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, policy);
0710     }
0711 }
0712 
0713 void KisEncloseAndFillPainter::Private::selectRegionsSurroundedBySpecificColorOrTransparent(KisPixelSelectionSP resultMask,
0714                                                                                             QRect *resultMaskRect,
0715                                                                                             KisPixelSelectionSP enclosingMask,
0716                                                                                             const QRect &enclosingMaskRect,
0717                                                                                             KisPaintDeviceSP referenceDevice) const
0718 {
0719     using namespace KisEncloseAndFillPainterDetail;
0720     const int softness = 100 - q->opacitySpread();
0721     // Here we must compute the specific color and transparent masks separately
0722     KisPixelSelectionSP resultMaskTransparent = new KisPixelSelection(new KisSelectionDefaultBounds(resultMask));
0723     if (softness == 0) {
0724         HardSelectionPolicy<SpecificColorOrTransparentDifferencePolicy> colorPolicy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold());
0725         selectRegionsSurroundedBySpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, colorPolicy, true);
0726     } else {
0727         SoftSelectionPolicy<SpecificColorOrTransparentDifferencePolicy> colorPolicy(referenceDevice->colorSpace(), regionSelectionColor, q->fillThreshold(), softness);
0728         selectRegionsSurroundedBySpecificColorGeneric(resultMask, resultMaskRect, enclosingMask, enclosingMaskRect, referenceDevice, colorPolicy, true);
0729     }
0730 }
0731 
0732 template <typename SelectionPolicy>
0733 void KisEncloseAndFillPainter::Private::selectRegionsSurroundedBySpecificColorGeneric(KisPixelSelectionSP resultMask,
0734                                                                                       QRect *resultMaskRect,
0735                                                                                       KisPixelSelectionSP enclosingMask,
0736                                                                                       const QRect &enclosingMaskRect,
0737                                                                                       KisPaintDeviceSP referenceDevice,
0738                                                                                       SelectionPolicy selectionPolicy,
0739                                                                                       bool colorOrTransparent) const
0740 {
0741     // Get the enclosing mask contour points
0742     const QVector<QPoint> enclosingPoints = getEnclosingContourPoints(enclosingMask, enclosingMaskRect);
0743     if (enclosingPoints.isEmpty()) {
0744         return;
0745     }
0746     // Here we just fill all the areas from the border towards inside until the
0747     // specific color. This selects any region that touches the enclosing area
0748     // contour and that is not equal to the surrounding color
0749     if (colorOrTransparent) {
0750         selectRegionsFromContourUntilColorOrTransparent(resultMask, enclosingMask, enclosingPoints, enclosingMaskRect, referenceDevice, selectionPolicy.color);
0751     } else {
0752         selectRegionsFromContourUntilColor(resultMask, enclosingMask, enclosingPoints, enclosingMaskRect, referenceDevice, selectionPolicy.color);
0753     }
0754     // Invert the mask since it contains the regions surrounding the regions we
0755     // want, that is, the regions that touch the enclosing area contour and that
0756     // are not equal to the surrounding color. We want the opposite
0757     resultMask->invert();
0758     // Since, after inverting, the mask includes the region outside the enclosing
0759     // mask, we must intersect the current mask with the enclosing mask. The result
0760     // is a mask that includes all the closed regions inside the enclosing mask
0761     resultMask->applySelection(enclosingMask, SELECTION_INTERSECT);
0762     
0763     if (regionSelectionIncludeSurroundingRegions) {
0764         // Remove the regions that touch the enclosing area
0765         removeContourRegions(resultMask, enclosingPoints, enclosingMaskRect);
0766     } else {
0767         // Remove the surrounding regions. Also there shouldn't be any regions
0768         // that touch the enclosing area contour after this step
0769         KisPixelSelectionSP mask = new KisPixelSelection(new KisSelectionDefaultBounds(enclosingMask));
0770         selectSimilarRegions(mask, enclosingMask, enclosingMaskRect, referenceDevice, selectionPolicy);
0771         resultMask->applySelection(mask, SELECTION_SUBTRACT);
0772     }
0773     if (resultMaskRect) {
0774         *resultMaskRect = resultMask->selectedExactRect();
0775     }
0776 }
0777 
0778 QVector<QPoint> KisEncloseAndFillPainter::Private::getEnclosingContourPoints(KisPixelSelectionSP enclosingMask,
0779                                                                              const QRect &enclosingMaskRect) const
0780 {
0781     QVector<QPoint> enclosingPoints;
0782     const int scanlineWidth = enclosingMaskRect.width() + 2;
0783     QVector<quint8> buffer(scanlineWidth * 3);
0784     quint8 *scanlines[3] = {buffer.data(), buffer.data() + scanlineWidth, buffer.data() + scanlineWidth * 2};
0785     // Initialize the buffer
0786     // Top, outside row
0787     memset(scanlines[0], MIN_SELECTED, scanlineWidth);
0788     // Middle row
0789     // Left, outside pixel
0790     *(scanlines[1]) = MIN_SELECTED;
0791     // Middle, inside pixels
0792     enclosingMask->readBytes(scanlines[1] + 1, enclosingMaskRect.x(), enclosingMaskRect.y(), enclosingMaskRect.width(), 1);
0793     // Right, outside pixel
0794     *(scanlines[2] - 1) = MIN_SELECTED;
0795     // Bottom row
0796     if (enclosingMaskRect.height() == 1) {
0797         // Bottom, outside row
0798         memset(scanlines[2], MIN_SELECTED, scanlineWidth);
0799     } else {
0800         // Left, outside pixel
0801         *(scanlines[2]) = MIN_SELECTED;
0802         // Middle, inside pixels
0803         enclosingMask->readBytes(scanlines[2] + 1, enclosingMaskRect.x(), enclosingMaskRect.y() + 1, enclosingMaskRect.width(), 1);
0804         // Right, outside pixel
0805         *(scanlines[2] + scanlineWidth - 1) = MIN_SELECTED;
0806     }
0807 
0808     for (int y = 0; y < enclosingMaskRect.height(); ++y) {
0809         if (y > 0) {
0810             // Rotate pointers
0811             quint8 *tmp = scanlines[0];
0812             scanlines[0] = scanlines[1];
0813             scanlines[1] = scanlines[2];
0814             scanlines[2] = tmp;
0815             // Read new row
0816             if (y == enclosingMaskRect.height() - 1) {
0817                 // Bottom, outside row
0818                 memset(scanlines[2], MIN_SELECTED, scanlineWidth);
0819             } else {
0820                 // Left, outside pixel
0821                 *(scanlines[2]) = MIN_SELECTED;
0822                 // Middle, inside pixels
0823                 enclosingMask->readBytes(scanlines[2] + 1, enclosingMaskRect.x(), enclosingMaskRect.y() + y + 1, enclosingMaskRect.width(), 1);
0824                 // Right, outside pixel
0825                 *(scanlines[2] + scanlineWidth - 1) = MIN_SELECTED;
0826             }
0827         }
0828         const quint8 *topPixel = scanlines[0] + 1;
0829         const quint8 *middlePixel = scanlines[1] + 1;
0830         const quint8 *bottomPixel = scanlines[2] + 1;
0831         for (int x = 0; x < enclosingMaskRect.width(); ++x, ++topPixel, ++middlePixel, ++bottomPixel) {
0832             // Continue if the current pixel is not in the selection
0833             if (*middlePixel == MIN_SELECTED) {
0834                 continue;
0835             }
0836             // Get all eight neighbor pixels. If at least one of them is not
0837             // in the selection then the current pixel is a border pixel and
0838             // we add it to the list
0839             quint8 neighbors = 0;
0840             neighbors |= (*(topPixel    - 1) == MIN_SELECTED) << 7;
0841             neighbors |= (*(topPixel       ) == MIN_SELECTED) << 6;
0842             neighbors |= (*(topPixel    + 1) == MIN_SELECTED) << 5;
0843             neighbors |= (*(middlePixel - 1) == MIN_SELECTED) << 4;
0844             neighbors |= (*(middlePixel + 1) == MIN_SELECTED) << 3;
0845             neighbors |= (*(bottomPixel - 1) == MIN_SELECTED) << 2;
0846             neighbors |= (*(bottomPixel    ) == MIN_SELECTED) << 1;
0847             neighbors |= (*(bottomPixel + 1) == MIN_SELECTED) << 0;
0848             if (neighbors != 0) {
0849                 enclosingPoints.push_back(QPoint(enclosingMaskRect.x() + x, enclosingMaskRect.y() + y));
0850             }
0851         }
0852     }
0853 
0854     return enclosingPoints;
0855 }
0856 
0857 void KisEncloseAndFillPainter::Private::applyPostProcessing(KisPixelSelectionSP mask,
0858                                                             KisPaintDeviceSP referenceDevice) const
0859 {
0860     if (q->sizemod() > 0) {
0861         if (q->stopGrowingAtDarkestPixel()) {
0862             KisGrowUntilDarkestPixelSelectionFilter biggy(q->sizemod(), referenceDevice);
0863             biggy.process(
0864                 mask, 
0865                 mask->selectedRect().adjusted(
0866                     -q->sizemod(), -q->sizemod(), q->sizemod(), q->sizemod()
0867                 )
0868             );
0869         } else {
0870             KisGrowSelectionFilter biggy(q->sizemod(), q->sizemod());
0871             biggy.process(
0872                 mask,
0873                 mask->selectedRect().adjusted(
0874                     -q->sizemod(), -q->sizemod(), q->sizemod(), q->sizemod()
0875                 )
0876             );
0877         }
0878     } else if (q->sizemod() < 0) {
0879         KisShrinkSelectionFilter tiny(-q->sizemod(), -q->sizemod(), false);
0880         tiny.process(mask, mask->selectedRect());
0881     }
0882     // Since the feathering already smooths the selection, the antiAlias
0883     // is not applied if we must feather
0884     if (q->feather() > 0) {
0885         KisFeatherSelectionFilter feathery(q->feather());
0886         feathery.process(mask, mask->selectedRect().adjusted(-q->feather(), -q->feather(), q->feather(), q->feather()));
0887     } else if (q->antiAlias()) {
0888         KisAntiAliasSelectionFilter antiAliasFilter;
0889         antiAliasFilter.process(mask, mask->selectedRect());
0890     }
0891 }
0892 
0893 void KisEncloseAndFillPainter::Private::invertIfNeeded(KisPixelSelectionSP resultMask, KisPixelSelectionSP enclosingMask) const
0894 {
0895     if (!regionSelectionInvert) {
0896         return;
0897     }
0898     resultMask->invert();
0899     // Since, after inverting, the mask includes the region outside the enclosing
0900     // mask, we must intersect the current mask with the enclosing mask. The result
0901     // is a mask that includes all the closed regions inside the enclosing mask
0902     resultMask->applySelection(enclosingMask, SELECTION_INTERSECT);
0903 }
0904 
0905 template <typename SelectionPolicy>
0906 int KisEncloseAndFillPainter::Private::selectSimilarRegions(KisPixelSelectionSP resultMask,
0907                                                             KisPixelSelectionSP enclosingMask,
0908                                                             const QRect &enclosingMaskRect,
0909                                                             KisPaintDeviceSP referenceDevice,
0910                                                             SelectionPolicy selectionPolicy) const
0911 {
0912     KisSequentialIterator resultMaskIterator(resultMask, enclosingMaskRect);
0913     KisSequentialConstIterator enclosingMaskIterator(enclosingMask, enclosingMaskRect);
0914     KisSequentialConstIterator referenceDeviceIterator(referenceDevice, enclosingMaskRect);
0915     int nPixels = 0;
0916     // Select all the pixels using the given selection policy
0917     while (resultMaskIterator.nextPixel() && enclosingMaskIterator.nextPixel() && referenceDeviceIterator.nextPixel()) {
0918         if (*enclosingMaskIterator.oldRawData() == MIN_SELECTED) {
0919             continue;
0920         }
0921         const quint8 selection = selectionPolicy.getSelectionFor(referenceDeviceIterator.oldRawData());
0922         if (selection > MIN_SELECTED) {
0923             *resultMaskIterator.rawData() = selection;
0924             ++nPixels;
0925         }
0926     }
0927     return nPixels;
0928 }
0929 
0930 template <typename SelectionPolicy>
0931 int KisEncloseAndFillPainter::Private::selectDissimilarRegions(KisPixelSelectionSP resultMask,
0932                                                                KisPixelSelectionSP enclosingMask,
0933                                                                const QRect &enclosingMaskRect,
0934                                                                KisPaintDeviceSP referenceDevice,
0935                                                                SelectionPolicy selectionPolicy) const
0936 {
0937     KisSequentialIterator resultMaskIterator(resultMask, enclosingMaskRect);
0938     KisSequentialConstIterator enclosingMaskIterator(enclosingMask, enclosingMaskRect);
0939     KisSequentialConstIterator referenceDeviceIterator(referenceDevice, enclosingMaskRect);
0940     int nPixels = 0;
0941     // Select all the pixels using the given selection policy
0942     while (resultMaskIterator.nextPixel() && enclosingMaskIterator.nextPixel() && referenceDeviceIterator.nextPixel()) {
0943         if (*enclosingMaskIterator.oldRawData() == MIN_SELECTED) {
0944             continue;
0945         }
0946         const quint8 selection = MAX_SELECTED - selectionPolicy.getSelectionFor(referenceDeviceIterator.oldRawData());
0947         if (selection > MIN_SELECTED) {
0948             *resultMaskIterator.rawData() = selection;
0949             ++nPixels;
0950         }
0951     }
0952     return nPixels;
0953 }
0954 
0955 void KisEncloseAndFillPainter::Private::selectRegionsFromContour(KisPixelSelectionSP resultMask,
0956                                                                  KisPixelSelectionSP enclosingMask,
0957                                                                  const QRect &enclosingMaskRect,
0958                                                                  KisPaintDeviceSP referenceDevice) const
0959 {
0960     const QVector<QPoint> enclosingPoints = getEnclosingContourPoints(enclosingMask, enclosingMaskRect);
0961     selectRegionsFromContour(resultMask, enclosingMask, enclosingPoints, enclosingMaskRect, referenceDevice);
0962 }
0963 
0964 void KisEncloseAndFillPainter::Private::selectRegionsFromContour(KisPixelSelectionSP resultMask,
0965                                                                  KisPixelSelectionSP enclosingMask,
0966                                                                  const QVector<QPoint> &enclosingPoints,
0967                                                                  const QRect &enclosingMaskRect,
0968                                                                  KisPaintDeviceSP referenceDevice) const
0969 {
0970     if (enclosingPoints.isEmpty()) {
0971         return;
0972     }
0973     const QRect inclusionRect = q->device()->defaultBounds()->wrapAroundMode()
0974                                 ? enclosingMaskRect
0975                                 : imageRect;
0976     // Here we just fill all the areas from the border towards inside
0977     for (const QPoint &point : enclosingPoints) {
0978         if (!inclusionRect.contains(point)) {
0979             continue;
0980         }
0981         // Continue if the region under the point was already filled
0982         if (*(resultMask->pixel(point).data()) == MAX_SELECTED) {
0983             continue;
0984         }
0985         KisPixelSelectionSP mask = new KisPixelSelection(new KisSelectionDefaultBounds(resultMask));
0986         KisScanlineFill gc(referenceDevice, point, inclusionRect);
0987         gc.setThreshold(q->fillThreshold());
0988         gc.setOpacitySpread(q->opacitySpread());
0989         // Use the enclosing mask as boundary so that we don't fill
0990         // potentially large regions on the outside
0991         gc.fillSelection(mask, enclosingMask);
0992         resultMask->applySelection(mask, SELECTION_ADD);
0993     }
0994 }
0995 
0996 void KisEncloseAndFillPainter::Private::selectRegionsFromContourUntilColor(KisPixelSelectionSP resultMask,
0997                                                                            KisPixelSelectionSP enclosingMask,
0998                                                                            const QRect &enclosingMaskRect,
0999                                                                            KisPaintDeviceSP referenceDevice,
1000                                                                            const KoColor &color) const
1001 {
1002     const QVector<QPoint> enclosingPoints = getEnclosingContourPoints(enclosingMask, enclosingMaskRect);
1003     selectRegionsFromContourUntilColor(resultMask, enclosingMask, enclosingPoints, enclosingMaskRect, referenceDevice, color);
1004 }
1005 
1006 void KisEncloseAndFillPainter::Private::selectRegionsFromContourUntilColor(KisPixelSelectionSP resultMask,
1007                                                                            KisPixelSelectionSP enclosingMask,
1008                                                                            const QVector<QPoint> &enclosingPoints,
1009                                                                            const QRect &enclosingMaskRect,
1010                                                                            KisPaintDeviceSP referenceDevice,
1011                                                                            const KoColor &color) const
1012 {
1013     if (enclosingPoints.isEmpty()) {
1014         return;
1015     }
1016     const QRect inclusionRect = q->device()->defaultBounds()->wrapAroundMode()
1017                                 ? enclosingMaskRect
1018                                 : imageRect;
1019     // Here we just fill all the areas from the border towards inside until the specific color
1020     for (const QPoint &point : enclosingPoints) {
1021         if (!inclusionRect.contains(point)) {
1022             continue;
1023         }
1024         // Continue if the region under the point was already filled
1025         if (*(resultMask->pixel(point).data()) == MAX_SELECTED) {
1026             continue;
1027         }
1028         KisPixelSelectionSP mask = new KisPixelSelection(new KisSelectionDefaultBounds(resultMask));
1029         KisScanlineFill gc(referenceDevice, point, inclusionRect);
1030         gc.setThreshold(q->fillThreshold());
1031         gc.setOpacitySpread(q->opacitySpread());
1032         // Use the enclosing mask as boundary so that we don't fill
1033         // potentially large regions in the outside
1034         gc.fillSelectionUntilColor(mask, color, enclosingMask);
1035         resultMask->applySelection(mask, SELECTION_ADD);
1036     }
1037 }
1038 
1039 void KisEncloseAndFillPainter::Private::selectRegionsFromContourUntilColorOrTransparent(KisPixelSelectionSP resultMask,
1040                                                                                         KisPixelSelectionSP enclosingMask,
1041                                                                                         const QRect &enclosingMaskRect,
1042                                                                                         KisPaintDeviceSP referenceDevice,
1043                                                                                         const KoColor &color) const
1044 {
1045     const QVector<QPoint> enclosingPoints = getEnclosingContourPoints(enclosingMask, enclosingMaskRect);
1046     selectRegionsFromContourUntilColorOrTransparent(resultMask, enclosingMask, enclosingPoints, enclosingMaskRect, referenceDevice, color);
1047 }
1048 
1049 void KisEncloseAndFillPainter::Private::selectRegionsFromContourUntilColorOrTransparent(KisPixelSelectionSP resultMask,
1050                                                                                         KisPixelSelectionSP enclosingMask,
1051                                                                                         const QVector<QPoint> &enclosingPoints,
1052                                                                                         const QRect &enclosingMaskRect,
1053                                                                                         KisPaintDeviceSP referenceDevice,
1054                                                                                         const KoColor &color) const
1055 {
1056     if (enclosingPoints.isEmpty()) {
1057         return;
1058     }
1059     const QRect inclusionRect = q->device()->defaultBounds()->wrapAroundMode()
1060                                 ? enclosingMaskRect
1061                                 : imageRect;
1062     // Here we just fill all the areas from the border towards inside until the specific color
1063     for (const QPoint &point : enclosingPoints) {
1064         if (!inclusionRect.contains(point)) {
1065             continue;
1066         }
1067         // Continue if the region under the point was already filled
1068         if (*(resultMask->pixel(point).data()) == MAX_SELECTED) {
1069             continue;
1070         }
1071         KisPixelSelectionSP mask = new KisPixelSelection(new KisSelectionDefaultBounds(resultMask));
1072         KisScanlineFill gc(referenceDevice, point, inclusionRect);
1073         gc.setThreshold(q->fillThreshold());
1074         gc.setOpacitySpread(q->opacitySpread());
1075         // Use the enclosing mask as boundary so that we don't fill
1076         // potentially large regions in the outside
1077         gc.fillSelectionUntilColorOrTransparent(mask, color, enclosingMask);
1078         resultMask->applySelection(mask, SELECTION_ADD);
1079     }
1080 }
1081 
1082 void KisEncloseAndFillPainter::Private::removeContourRegions(KisPixelSelectionSP resultMask,
1083                                                              KisPixelSelectionSP enclosingMask,
1084                                                              const QRect &enclosingMaskRect) const
1085 {
1086     const QVector<QPoint> enclosingPoints = getEnclosingContourPoints(enclosingMask, enclosingMaskRect);
1087     removeContourRegions(resultMask, enclosingPoints, enclosingMaskRect);
1088 }
1089 
1090 void KisEncloseAndFillPainter::Private::removeContourRegions(KisPixelSelectionSP resultMask,
1091                                                              const QVector<QPoint> &enclosingPoints,
1092                                                              const QRect &enclosingMaskRect) const
1093 {
1094     if (enclosingPoints.isEmpty()) {
1095         return;
1096     }
1097     const QRect inclusionRect = q->device()->defaultBounds()->wrapAroundMode()
1098                                 ? enclosingMaskRect
1099                                 : imageRect;
1100     // Here we just fill all the non-zero areas from the border towards inside
1101     for (const QPoint &point : enclosingPoints) {
1102         if (!inclusionRect.contains(point)) {
1103             continue;
1104         }
1105         // Continue if the region under the point was already filled
1106         if (*(resultMask->pixel(point).data()) == MIN_SELECTED) {
1107             continue;
1108         }
1109         KisScanlineFill gc(resultMask, point, inclusionRect);
1110         gc.clearNonZeroComponent();
1111     }
1112 }