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 }