File indexing completed on 2024-05-19 04:26:12
0001 /* 0002 * SPDX-FileCopyrightText: 2004 Adrian Page <adrian@pagenet.plus.com> 0003 * SPDX-FileCopyrightText: 2004 Bart Coppens <kde@bartcoppens.be> 0004 * SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "kis_fill_painter.h" 0010 0011 #include <stdlib.h> 0012 #include <string.h> 0013 #include <cfloat> 0014 #include <stack> 0015 0016 #include <QFontInfo> 0017 #include <QFontMetrics> 0018 #include <QPen> 0019 #include <QMatrix> 0020 #include <QImage> 0021 #include <QMap> 0022 #include <QPainter> 0023 #include <QRect> 0024 #include <QString> 0025 0026 #include <klocalizedstring.h> 0027 0028 #include <KoUpdater.h> 0029 0030 #include "generator/kis_generator.h" 0031 #include "filter/kis_filter_configuration.h" 0032 #include "generator/kis_generator_registry.h" 0033 #include "kis_processing_information.h" 0034 #include "kis_debug.h" 0035 #include "kis_image.h" 0036 #include "kis_layer.h" 0037 #include "kis_paint_device.h" 0038 #include <resources/KoPattern.h> 0039 #include "KoColorSpace.h" 0040 #include "kis_transaction.h" 0041 #include "kis_pixel_selection.h" 0042 #include <KoCompositeOpRegistry.h> 0043 #include <floodfill/kis_scanline_fill.h> 0044 #include "kis_selection_filters.h" 0045 #include <kis_perspectivetransform_worker.h> 0046 #include <kis_sequential_iterator.h> 0047 #include <KisColorSelectionPolicies.h> 0048 #include <krita_utils.h> 0049 #include <kis_default_bounds.h> 0050 #include <KisImageResolutionProxy.h> 0051 0052 0053 KisFillPainter::KisFillPainter() 0054 : KisPainter() 0055 { 0056 initFillPainter(); 0057 } 0058 0059 KisFillPainter::KisFillPainter(KisPaintDeviceSP device) 0060 : KisPainter(device) 0061 { 0062 initFillPainter(); 0063 } 0064 0065 KisFillPainter::KisFillPainter(KisPaintDeviceSP device, KisSelectionSP selection) 0066 : KisPainter(device, selection) 0067 { 0068 initFillPainter(); 0069 } 0070 0071 void KisFillPainter::initFillPainter() 0072 { 0073 m_width = m_height = -1; 0074 m_careForSelection = false; 0075 m_sizemod = 0; 0076 m_feather = 0; 0077 m_useCompositing = false; 0078 m_threshold = 0; 0079 m_opacitySpread = 0; 0080 m_useSelectionAsBoundary = false; 0081 m_antiAlias = false; 0082 m_regionFillingMode = RegionFillingMode_FloodFill; 0083 m_stopGrowingAtDarkestPixel = false; 0084 } 0085 0086 void KisFillPainter::fillSelection(const QRect &rc, const KoColor &color) 0087 { 0088 KisPaintDeviceSP fillDevice = new KisPaintDevice(device()->colorSpace()); 0089 fillDevice->setDefaultPixel(color); 0090 0091 bitBlt(rc.topLeft(), fillDevice, rc); 0092 } 0093 0094 // 'regular' filling 0095 // XXX: This also needs renaming, since filling ought to keep the opacity and the composite op in mind, 0096 // this is more eraseToColor. 0097 void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoColor& kc, quint8 opacity) 0098 { 0099 if (w > 0 && h > 0) { 0100 // Make sure we're in the right colorspace 0101 0102 KoColor kc2(kc); // get rid of const 0103 kc2.convertTo(device()->colorSpace()); 0104 quint8 * data = kc2.data(); 0105 device()->colorSpace()->setOpacity(data, opacity, 1); 0106 0107 device()->fill(x1, y1, w, h, data); 0108 0109 addDirtyRect(QRect(x1, y1, w, h)); 0110 } 0111 } 0112 0113 void KisFillPainter::fillRect(const QRect &rc, const KoPatternSP pattern, const QPoint &offset) 0114 { 0115 fillRect(rc.x(), rc.y(), rc.width(), rc.height(), pattern, offset); 0116 } 0117 0118 void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoPatternSP pattern, const QPoint &offset) 0119 { 0120 if (!pattern) return; 0121 if (!pattern->valid()) return; 0122 if (!device()) return; 0123 if (w < 1) return; 0124 if (h < 1) return; 0125 0126 KisPaintDeviceSP patternLayer = new KisPaintDevice(device()->compositionSourceColorSpace(), pattern->name()); 0127 patternLayer->convertFromQImage(pattern->pattern(), 0); 0128 0129 if (!offset.isNull()) { 0130 patternLayer->moveTo(offset); 0131 } 0132 0133 fillRect(x1, y1, w, h, patternLayer, QRect(offset.x(), offset.y(), pattern->width(), pattern->height())); 0134 } 0135 0136 void KisFillPainter::fillRectNoCompose(const QRect &rc, const KoPatternSP pattern, const QTransform transform) 0137 { 0138 if (!pattern) return; 0139 if (!pattern->valid()) return; 0140 if (!device()) return; 0141 if (rc.width() < 1) return; 0142 if (rc.height() < 1) return; 0143 0144 KisPaintDeviceSP patternLayer = new KisPaintDevice(device()->colorSpace(), pattern->name()); 0145 patternLayer->convertFromQImage(pattern->pattern(), 0); 0146 0147 fillRectNoCompose(rc.x(), rc.y(), rc.width(), rc.height(), patternLayer, QRect(0, 0, pattern->width(), pattern->height()), transform); 0148 } 0149 0150 void KisFillPainter::fillRectNoCompose(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisPaintDeviceSP device, const QRect& deviceRect, const QTransform transform) 0151 { 0152 /** 0153 * Since this function doesn't do any kind of compositing, so the pixel size 0154 * of the source and destination devices must be exactly the same. The color 0155 * space should ideally be also the same. 0156 */ 0157 KIS_SAFE_ASSERT_RECOVER_RETURN(device->pixelSize() == this->device()->pixelSize()); 0158 KIS_SAFE_ASSERT_RECOVER_NOOP(*device->colorSpace() == *this->device()->colorSpace()); 0159 0160 KisPaintDeviceSP wrapped = device; 0161 KisDefaultBoundsBaseSP oldBounds = wrapped->defaultBounds(); 0162 wrapped->setDefaultBounds(new KisWrapAroundBoundsWrapper(oldBounds, deviceRect)); 0163 const bool oldSupportsWrapAroundMode = wrapped->supportsWraproundMode(); 0164 wrapped->setSupportsWraparoundMode(true); 0165 0166 0167 KisPerspectiveTransformWorker worker(this->device(), transform, false, this->progressUpdater()); 0168 worker.runPartialDst(device, this->device(), QRect(x1, y1, w, h)); 0169 0170 addDirtyRect(QRect(x1, y1, w, h)); 0171 wrapped->setDefaultBounds(oldBounds); 0172 wrapped->setSupportsWraparoundMode(oldSupportsWrapAroundMode); 0173 } 0174 0175 void KisFillPainter::fillRect(const QRect &rc, const KisPaintDeviceSP device, const QRect& deviceRect) 0176 { 0177 fillRect(rc.x(), rc.y(), rc.width(), rc.height(), device, deviceRect); 0178 } 0179 0180 void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisPaintDeviceSP device, const QRect& deviceRect) 0181 { 0182 const QRect &patternRect = deviceRect; 0183 const QRect fillRect(x1, y1, w, h); 0184 0185 auto toPatternLocal = [](int value, int offset, int width) { 0186 const int normalizedValue = value - offset; 0187 return offset + (normalizedValue >= 0 ? 0188 normalizedValue % width : 0189 width - (-normalizedValue - 1) % width - 1); 0190 }; 0191 0192 int dstY = fillRect.y(); 0193 while (dstY <= fillRect.bottom()) { 0194 const int dstRowsRemaining = fillRect.bottom() - dstY + 1; 0195 0196 const int srcY = toPatternLocal(dstY, patternRect.y(), patternRect.height()); 0197 const int height = qMin(patternRect.height() - srcY + patternRect.y(), dstRowsRemaining); 0198 0199 int dstX = fillRect.x(); 0200 while (dstX <= fillRect.right()) { 0201 const int dstColumnsRemaining = fillRect.right() - dstX + 1; 0202 0203 const int srcX = toPatternLocal(dstX, patternRect.x(), patternRect.width()); 0204 const int width = qMin(patternRect.width() - srcX + patternRect.x(), dstColumnsRemaining); 0205 0206 bitBlt(dstX, dstY, device, srcX, srcY, width, height); 0207 0208 dstX += width; 0209 } 0210 dstY += height; 0211 } 0212 0213 addDirtyRect(QRect(x1, y1, w, h)); 0214 } 0215 0216 void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisFilterConfigurationSP generator) 0217 { 0218 if (!generator) return; 0219 KisGeneratorSP g = KisGeneratorRegistry::instance()->value(generator->name()); 0220 if (!device()) return; 0221 if (w < 1) return; 0222 if (h < 1) return; 0223 0224 QRect tmpRc(x1, y1, w, h); 0225 0226 KisProcessingInformation dstCfg(device(), tmpRc.topLeft(), 0); 0227 0228 g->generate(dstCfg, tmpRc.size(), generator); 0229 0230 addDirtyRect(tmpRc); 0231 } 0232 0233 // flood filling 0234 0235 void KisFillPainter::fillColor(int startX, int startY, KisPaintDeviceSP sourceDevice) 0236 { 0237 if (!m_useCompositing) { 0238 if (m_sizemod || m_feather || 0239 compositeOpId() != COMPOSITE_OVER || 0240 opacity() != MAX_SELECTED || 0241 sourceDevice != device()) { 0242 0243 warnKrita << "WARNING: Fast Flood Fill (no compositing mode)" 0244 << "does not support compositeOps, opacity, " 0245 << "selection enhancements and separate source " 0246 << "devices"; 0247 } 0248 0249 QRect fillBoundsRect(0, 0, m_width, m_height); 0250 QPoint startPoint(startX, startY); 0251 0252 if (!fillBoundsRect.contains(startPoint)) return; 0253 0254 KisScanlineFill gc(device(), startPoint, fillBoundsRect); 0255 gc.setThreshold(m_threshold); 0256 if (m_regionFillingMode == RegionFillingMode_FloodFill) { 0257 gc.fill(paintColor()); 0258 } else { 0259 gc.fillUntilColor(paintColor(), m_regionFillingBoundaryColor); 0260 } 0261 0262 } else { 0263 genericFillStart(startX, startY, sourceDevice); 0264 0265 // Now create a layer and fill it 0266 KisPaintDeviceSP filled = device()->createCompositionSourceDevice(); 0267 Q_CHECK_PTR(filled); 0268 KisFillPainter painter(filled); 0269 painter.fillRect(0, 0, m_width, m_height, paintColor()); 0270 painter.end(); 0271 0272 genericFillEnd(filled); 0273 } 0274 } 0275 0276 void KisFillPainter::fillPattern(int startX, int startY, KisPaintDeviceSP sourceDevice, QTransform patternTransform) 0277 { 0278 genericFillStart(startX, startY, sourceDevice); 0279 0280 // Now create a layer and fill it 0281 KisPaintDeviceSP filled = device()->createCompositionSourceDevice(); 0282 Q_CHECK_PTR(filled); 0283 KisFillPainter painter(filled); 0284 painter.fillRectNoCompose(QRect(0, 0, m_width, m_height), pattern(), patternTransform); 0285 painter.end(); 0286 0287 genericFillEnd(filled); 0288 } 0289 0290 void KisFillPainter::genericFillStart(int startX, int startY, KisPaintDeviceSP sourceDevice) 0291 { 0292 Q_ASSERT(m_width > 0); 0293 Q_ASSERT(m_height > 0); 0294 0295 // Create a selection from the surrounding area 0296 0297 KisPixelSelectionSP pixelSelection = createFloodSelection(startX, startY, sourceDevice, 0298 (selection().isNull() ? 0 : selection()->pixelSelection())); 0299 KisSelectionSP newSelection = new KisSelection(pixelSelection->defaultBounds(), 0300 selection() ? selection()->resolutionProxy() : KisImageResolutionProxy::identity()); 0301 newSelection->pixelSelection()->applySelection(pixelSelection, SELECTION_REPLACE); 0302 m_fillSelection = newSelection; 0303 } 0304 0305 void KisFillPainter::genericFillEnd(KisPaintDeviceSP filled) 0306 { 0307 if (progressUpdater() && progressUpdater()->interrupted()) { 0308 m_width = m_height = -1; 0309 return; 0310 } 0311 0312 // TODO: filling using the correct bound of the selection would be better, *but* 0313 // the selection is limited to the exact bound of a layer, while in reality, we don't 0314 // want that, since we want a transparent layer to be completely filled 0315 // QRect rc = m_fillSelection->selectedExactRect(); 0316 0317 0318 /** 0319 * Apply the real selection to a filled one 0320 */ 0321 KisSelectionSP realSelection = selection(); 0322 QRect rc; 0323 0324 if (realSelection) { 0325 rc = m_fillSelection->selectedExactRect().intersected(realSelection->projection()->selectedExactRect()); 0326 m_fillSelection->pixelSelection()->applySelection( 0327 realSelection->projection(), SELECTION_INTERSECT); 0328 } else { 0329 rc = m_fillSelection->selectedExactRect(); 0330 } 0331 0332 setSelection(m_fillSelection); 0333 bitBlt(rc.topLeft(), filled, rc); 0334 setSelection(realSelection); 0335 0336 if (progressUpdater()) progressUpdater()->setProgress(100); 0337 0338 m_width = m_height = -1; 0339 } 0340 0341 KisPixelSelectionSP KisFillPainter::createFloodSelection(int startX, int startY, KisPaintDeviceSP sourceDevice, 0342 KisPaintDeviceSP existingSelection) 0343 { 0344 KisPixelSelectionSP newSelection = new KisPixelSelection(new KisSelectionDefaultBounds(device())); 0345 return createFloodSelection(newSelection, startX, startY, sourceDevice, existingSelection); 0346 } 0347 0348 KisPixelSelectionSP KisFillPainter::createFloodSelection(KisPixelSelectionSP pixelSelection, int startX, int startY, 0349 KisPaintDeviceSP sourceDevice, KisPaintDeviceSP existingSelection) 0350 { 0351 0352 if (m_width < 0 || m_height < 0) { 0353 if (selection() && m_careForSelection) { 0354 QRect rc = selection()->selectedExactRect(); 0355 m_width = rc.width() - (startX - rc.x()); 0356 m_height = rc.height() - (startY - rc.y()); 0357 } 0358 } 0359 dbgImage << "Width: " << m_width << " Height: " << m_height; 0360 // Otherwise the width and height should have been set 0361 Q_ASSERT(m_width > 0 && m_height > 0); 0362 0363 QRect fillBoundsRect(0, 0, m_width, m_height); 0364 QPoint startPoint(startX, startY); 0365 0366 if (!fillBoundsRect.contains(startPoint)) { 0367 return pixelSelection; 0368 } 0369 0370 KisScanlineFill gc(sourceDevice, startPoint, fillBoundsRect); 0371 gc.setThreshold(m_threshold); 0372 gc.setOpacitySpread(m_useCompositing ? m_opacitySpread : 100); 0373 if (m_regionFillingMode == RegionFillingMode_FloodFill) { 0374 if (m_useSelectionAsBoundary && !pixelSelection.isNull()) { 0375 gc.fillSelection(pixelSelection, existingSelection); 0376 } else { 0377 gc.fillSelection(pixelSelection); 0378 } 0379 } else { 0380 if (m_useSelectionAsBoundary && !pixelSelection.isNull()) { 0381 gc.fillSelectionUntilColor(pixelSelection, m_regionFillingBoundaryColor, existingSelection); 0382 } else { 0383 gc.fillSelectionUntilColor(pixelSelection, m_regionFillingBoundaryColor); 0384 } 0385 } 0386 0387 if (m_useCompositing) { 0388 if (m_sizemod > 0) { 0389 if (m_stopGrowingAtDarkestPixel) { 0390 KisGrowUntilDarkestPixelSelectionFilter biggy(m_sizemod, sourceDevice); 0391 biggy.process(pixelSelection, pixelSelection->selectedRect().adjusted(-m_sizemod, -m_sizemod, m_sizemod, m_sizemod)); 0392 } else { 0393 KisGrowSelectionFilter biggy(m_sizemod, m_sizemod); 0394 biggy.process(pixelSelection, pixelSelection->selectedRect().adjusted(-m_sizemod, -m_sizemod, m_sizemod, m_sizemod)); 0395 } 0396 } 0397 else if (m_sizemod < 0) { 0398 KisShrinkSelectionFilter tiny(-m_sizemod, -m_sizemod, false); 0399 tiny.process(pixelSelection, pixelSelection->selectedRect()); 0400 } 0401 // Since the feathering already smooths the selection, the antiAlias 0402 // is not applied if we must feather 0403 if (m_feather > 0) { 0404 KisFeatherSelectionFilter feathery(m_feather); 0405 feathery.process(pixelSelection, pixelSelection->selectedRect().adjusted(-m_feather, -m_feather, m_feather, m_feather)); 0406 } else if (m_antiAlias) { 0407 KisAntiAliasSelectionFilter antiAliasFilter; 0408 antiAliasFilter.process(pixelSelection, pixelSelection->selectedRect()); 0409 } 0410 } 0411 0412 return pixelSelection; 0413 } 0414 0415 template <typename DifferencePolicy, typename SelectionPolicy> 0416 void createSimilarColorsSelectionImpl(KisPixelSelectionSP outSelection, 0417 KisPaintDeviceSP referenceDevice, 0418 const QRect &rect, 0419 KisPixelSelectionSP mask, 0420 DifferencePolicy differencePolicy, 0421 SelectionPolicy selectionPolicy, 0422 KoUpdater *updater = nullptr) 0423 { 0424 KisSequentialConstIterator referenceDeviceIterator(referenceDevice, rect); 0425 KisSequentialIterator outSelectionIterator(outSelection, rect); 0426 0427 const int totalNumberOfPixels = rect.width() * rect.height(); 0428 const int numberOfUpdates = 4; 0429 const int numberOfPixelsPerUpdate = totalNumberOfPixels / numberOfUpdates; 0430 const int progressIncrement = 100 / numberOfUpdates; 0431 int numberOfPixelsProcessed = 0; 0432 0433 if (mask) { 0434 KisSequentialConstIterator maskIterator(mask, rect); 0435 while (referenceDeviceIterator.nextPixel() && 0436 outSelectionIterator.nextPixel() && 0437 maskIterator.nextPixel()) { 0438 if (*maskIterator.rawDataConst() != MIN_SELECTED) { 0439 *outSelectionIterator.rawData() = 0440 selectionPolicy.opacityFromDifference( 0441 differencePolicy.difference(referenceDeviceIterator.rawDataConst()) 0442 ); 0443 } 0444 if (updater) { 0445 ++numberOfPixelsProcessed; 0446 if (numberOfPixelsProcessed > numberOfPixelsPerUpdate) { 0447 numberOfPixelsProcessed = 0; 0448 updater->setProgress(updater->progress() + progressIncrement); 0449 } 0450 } 0451 } 0452 } else { 0453 while (referenceDeviceIterator.nextPixel() && 0454 outSelectionIterator.nextPixel()) { 0455 *outSelectionIterator.rawData() = 0456 selectionPolicy.opacityFromDifference( 0457 differencePolicy.difference(referenceDeviceIterator.rawDataConst()) 0458 ); 0459 if (updater) { 0460 ++numberOfPixelsProcessed; 0461 if (numberOfPixelsProcessed > numberOfPixelsPerUpdate) { 0462 numberOfPixelsProcessed = 0; 0463 updater->setProgress(updater->progress() + progressIncrement); 0464 } 0465 } 0466 } 0467 } 0468 if (updater) { 0469 updater->setProgress(100); 0470 } 0471 } 0472 0473 void KisFillPainter::createSimilarColorsSelection(KisPixelSelectionSP outSelection, 0474 const KoColor &referenceColor, 0475 KisPaintDeviceSP referenceDevice, 0476 const QRect &rect, 0477 KisPixelSelectionSP mask) 0478 { 0479 if (rect.isEmpty()) { 0480 return; 0481 } 0482 0483 KoColor srcColor(referenceColor); 0484 srcColor.convertTo(referenceDevice->colorSpace()); 0485 0486 const int pixelSize = referenceDevice->pixelSize(); 0487 const int softness = 100 - opacitySpread(); 0488 0489 using namespace KisColorSelectionPolicies; 0490 0491 if (softness == 0) { 0492 HardSelectionPolicy sp(fillThreshold()); 0493 if (pixelSize == 1) { 0494 OptimizedDifferencePolicy<quint8> dp(srcColor, fillThreshold()); 0495 createSimilarColorsSelectionImpl(outSelection, referenceDevice, rect, mask, dp, sp); 0496 } else if (pixelSize == 2) { 0497 OptimizedDifferencePolicy<quint16> dp(srcColor, fillThreshold()); 0498 createSimilarColorsSelectionImpl(outSelection, referenceDevice, rect, mask, dp, sp); 0499 } else if (pixelSize == 4) { 0500 OptimizedDifferencePolicy<quint32> dp(srcColor, fillThreshold()); 0501 createSimilarColorsSelectionImpl(outSelection, referenceDevice, rect, mask, dp, sp); 0502 } else if (pixelSize == 8) { 0503 OptimizedDifferencePolicy<quint64> dp(srcColor, fillThreshold()); 0504 createSimilarColorsSelectionImpl(outSelection, referenceDevice, rect, mask, dp, sp); 0505 } else { 0506 SlowDifferencePolicy dp(srcColor, fillThreshold()); 0507 createSimilarColorsSelectionImpl(outSelection, referenceDevice, rect, mask, dp, sp); 0508 } 0509 } else { 0510 SoftSelectionPolicy sp(fillThreshold(), softness); 0511 if (pixelSize == 1) { 0512 OptimizedDifferencePolicy<quint8> dp(srcColor, fillThreshold()); 0513 createSimilarColorsSelectionImpl(outSelection, referenceDevice, rect, mask, dp, sp); 0514 } else if (pixelSize == 2) { 0515 OptimizedDifferencePolicy<quint16> dp(srcColor, fillThreshold()); 0516 createSimilarColorsSelectionImpl(outSelection, referenceDevice, rect, mask, dp, sp); 0517 } else if (pixelSize == 4) { 0518 OptimizedDifferencePolicy<quint32> dp(srcColor, fillThreshold()); 0519 createSimilarColorsSelectionImpl(outSelection, referenceDevice, rect, mask, dp, sp); 0520 } else if (pixelSize == 8) { 0521 OptimizedDifferencePolicy<quint64> dp(srcColor, fillThreshold()); 0522 createSimilarColorsSelectionImpl(outSelection, referenceDevice, rect, mask, dp, sp); 0523 } else { 0524 SlowDifferencePolicy dp(srcColor, fillThreshold()); 0525 createSimilarColorsSelectionImpl(outSelection, referenceDevice, rect, mask, dp, sp); 0526 } 0527 } 0528 } 0529 0530 QVector<KisStrokeJobData*> KisFillPainter::createSimilarColorsSelectionJobs( 0531 KisPixelSelectionSP outSelection, 0532 const QSharedPointer<KoColor> referenceColor, 0533 KisPaintDeviceSP referenceDevice, 0534 const QRect &rect, 0535 KisPixelSelectionSP mask, 0536 QSharedPointer<KisProcessingVisitor::ProgressHelper> progressHelper 0537 ) 0538 { 0539 if (rect.isEmpty()) { 0540 return {}; 0541 } 0542 0543 QVector<KisStrokeJobData*> jobsData; 0544 QVector<QRect> fillPatches = 0545 KritaUtils::splitRectIntoPatches(rect, KritaUtils::optimalPatchSize()); 0546 const int threshold = fillThreshold(); 0547 const int softness = 100 - opacitySpread(); 0548 const int sizemod = this->sizemod(); 0549 const bool stopGrowingAtDarkestPixel = this->stopGrowingAtDarkestPixel(); 0550 const int feather = this->feather(); 0551 const bool antiAlias = this->antiAlias(); 0552 0553 KritaUtils::addJobBarrier(jobsData, nullptr); 0554 0555 for (const QRect &patch : fillPatches) { 0556 KritaUtils::addJobConcurrent( 0557 jobsData, 0558 [referenceDevice, outSelection, mask, referenceColor, 0559 threshold, softness, patch, progressHelper]() mutable 0560 { 0561 if (patch.isEmpty()) { 0562 return; 0563 } 0564 0565 KoUpdater *updater = progressHelper ? progressHelper->updater() : nullptr; 0566 0567 using namespace KisColorSelectionPolicies; 0568 0569 const int pixelSize = referenceDevice->pixelSize(); 0570 KoColor srcColor(*referenceColor); 0571 srcColor.convertTo(referenceDevice->colorSpace()); 0572 0573 if (softness == 0) { 0574 HardSelectionPolicy sp(threshold); 0575 if (pixelSize == 1) { 0576 OptimizedDifferencePolicy<quint8> dp(srcColor, threshold); 0577 createSimilarColorsSelectionImpl(outSelection, referenceDevice, patch, mask, dp, sp, updater); 0578 } else if (pixelSize == 2) { 0579 OptimizedDifferencePolicy<quint16> dp(srcColor, threshold); 0580 createSimilarColorsSelectionImpl(outSelection, referenceDevice, patch, mask, dp, sp, updater); 0581 } else if (pixelSize == 4) { 0582 OptimizedDifferencePolicy<quint32> dp(srcColor, threshold); 0583 createSimilarColorsSelectionImpl(outSelection, referenceDevice, patch, mask, dp, sp, updater); 0584 } else if (pixelSize == 8) { 0585 OptimizedDifferencePolicy<quint64> dp(srcColor, threshold); 0586 createSimilarColorsSelectionImpl(outSelection, referenceDevice, patch, mask, dp, sp, updater); 0587 } else { 0588 SlowDifferencePolicy dp(srcColor, threshold); 0589 createSimilarColorsSelectionImpl(outSelection, referenceDevice, patch, mask, dp, sp, updater); 0590 } 0591 } else { 0592 SoftSelectionPolicy sp(threshold, softness); 0593 if (pixelSize == 1) { 0594 OptimizedDifferencePolicy<quint8> dp(srcColor, threshold); 0595 createSimilarColorsSelectionImpl(outSelection, referenceDevice, patch, mask, dp, sp, updater); 0596 } else if (pixelSize == 2) { 0597 OptimizedDifferencePolicy<quint16> dp(srcColor, threshold); 0598 createSimilarColorsSelectionImpl(outSelection, referenceDevice, patch, mask, dp, sp, updater); 0599 } else if (pixelSize == 4) { 0600 OptimizedDifferencePolicy<quint32> dp(srcColor, threshold); 0601 createSimilarColorsSelectionImpl(outSelection, referenceDevice, patch, mask, dp, sp, updater); 0602 } else if (pixelSize == 8) { 0603 OptimizedDifferencePolicy<quint64> dp(srcColor, threshold); 0604 createSimilarColorsSelectionImpl(outSelection, referenceDevice, patch, mask, dp, sp, updater); 0605 } else { 0606 SlowDifferencePolicy dp(srcColor, threshold); 0607 createSimilarColorsSelectionImpl(outSelection, referenceDevice, patch, mask, dp, sp, updater); 0608 } 0609 } 0610 } 0611 ); 0612 } 0613 0614 KritaUtils::addJobSequential( 0615 jobsData, 0616 [outSelection, referenceDevice, mask, 0617 sizemod, stopGrowingAtDarkestPixel, feather, antiAlias, progressHelper]() mutable 0618 { 0619 KoUpdater *updater = progressHelper ? progressHelper->updater() : nullptr; 0620 0621 if (sizemod > 0) { 0622 if (stopGrowingAtDarkestPixel) { 0623 KisGrowUntilDarkestPixelSelectionFilter biggy(sizemod, referenceDevice); 0624 biggy.process(outSelection, outSelection->selectedRect().adjusted(-sizemod, -sizemod, sizemod, sizemod)); 0625 } else { 0626 KisGrowSelectionFilter biggy(sizemod, sizemod); 0627 biggy.process(outSelection, outSelection->selectedRect().adjusted(-sizemod, -sizemod, sizemod, sizemod)); 0628 } 0629 } else if (sizemod < 0) { 0630 KisShrinkSelectionFilter tiny(-sizemod, -sizemod, false); 0631 tiny.process(outSelection, outSelection->selectedRect()); 0632 } 0633 if (updater) { 0634 updater->setProgress(33); 0635 } 0636 0637 // Since the feathering already smooths the selection, the antiAlias 0638 // is not applied if we must feather 0639 if (feather > 0) { 0640 KisFeatherSelectionFilter feathery(feather); 0641 feathery.process(outSelection, outSelection->selectedRect().adjusted(-feather, -feather, feather, feather)); 0642 } else if (antiAlias) { 0643 KisAntiAliasSelectionFilter antiAliasFilter; 0644 antiAliasFilter.process(outSelection, outSelection->selectedRect()); 0645 } 0646 if (updater) { 0647 updater->setProgress(66); 0648 } 0649 0650 if (mask) { 0651 outSelection->applySelection(mask, SELECTION_INTERSECT); 0652 } 0653 if (updater) { 0654 updater->setProgress(100); 0655 } 0656 } 0657 ); 0658 0659 return jobsData; 0660 }