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

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