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