Krita Source Code Documentation
Loading...
Searching...
No Matches
KisGrowUntilDarkestPixelSelectionFilter Class Reference

Filter that dilates a selection and that can stop dilating adaptively at areas of higher darkness or opacity. This is useful to grow selections used to fill line art, since the growing will stop most likely inside the lines, without overflowing to the other side. More...

#include <kis_selection_filters.h>

+ Inheritance diagram for KisGrowUntilDarkestPixelSelectionFilter:

Public Member Functions

QRect changeRect (const QRect &rect, KisDefaultBoundsBaseSP defaultBounds) override
 
 KisGrowUntilDarkestPixelSelectionFilter (qint32 radius, KisPaintDeviceSP referenceDevice)
 
KUndo2MagicString name () override
 
void process (KisPixelSelectionSP pixelSelection, const QRect &rect) override
 
- Public Member Functions inherited from KisSelectionFilter
virtual ~KisSelectionFilter ()
 

Private Attributes

qint32 m_radius
 
KisPaintDeviceSP m_referenceDevice
 

Additional Inherited Members

- Protected Member Functions inherited from KisSelectionFilter
void computeBorder (qint32 *circ, qint32 xradius, qint32 yradius)
 
void computeTransition (quint8 *transition, quint8 **buf, qint32 width)
 
void rotatePointers (quint8 **p, quint32 n)
 

Detailed Description

Filter that dilates a selection and that can stop dilating adaptively at areas of higher darkness or opacity. This is useful to grow selections used to fill line art, since the growing will stop most likely inside the lines, without overflowing to the other side.

Definition at line 226 of file kis_selection_filters.h.

Constructor & Destructor Documentation

◆ KisGrowUntilDarkestPixelSelectionFilter()

KisGrowUntilDarkestPixelSelectionFilter::KisGrowUntilDarkestPixelSelectionFilter ( qint32 radius,
KisPaintDeviceSP referenceDevice )
Parameters
radiusThe radius of the structuring element used for the dilation.
referenceDeviceThe device used to check if the dilation reached the darkest or most opaque pixels.

Definition at line 1191 of file kis_selection_filters.cpp.

1193 : m_radius(radius)
1194 , m_referenceDevice(referenceDevice)
1195{
1196}

Member Function Documentation

◆ changeRect()

QRect KisGrowUntilDarkestPixelSelectionFilter::changeRect ( const QRect & rect,
KisDefaultBoundsBaseSP defaultBounds )
overridevirtual

Reimplemented from KisSelectionFilter.

Definition at line 1203 of file kis_selection_filters.cpp.

1204{
1205 Q_UNUSED(defaultBounds);
1206
1207 return rect.adjusted(-m_radius, -m_radius, m_radius, m_radius);
1208}

References m_radius.

◆ name()

KUndo2MagicString KisGrowUntilDarkestPixelSelectionFilter::name ( )
overridevirtual

Reimplemented from KisSelectionFilter.

Definition at line 1198 of file kis_selection_filters.cpp.

1199{
1200 return kundo2_i18n("Grow Selection Until Darkest Pixel");
1201}
KUndo2MagicString kundo2_i18n(const char *text)

References kundo2_i18n().

◆ process()

void KisGrowUntilDarkestPixelSelectionFilter::process ( KisPixelSelectionSP pixelSelection,
const QRect & rect )
overridevirtual

Implements KisSelectionFilter.

Definition at line 1210 of file kis_selection_filters.cpp.

1211{
1212 // Copy the original selection. We will grow this adaptively until the
1213 // darkest or more opaque pixels or until the maximum grow is reached.
1214 KisPixelSelectionSP mask = new KisPixelSelection(*pixelSelection);
1215 // Grow the original selection normally. At the end this selection will be
1216 // masked with the adaptively grown mask. We cannot grow adaptively this
1217 // selection directly since it may have semi-transparent or soft edges.
1218 // Those need to be retained in the final selection. This normally grown
1219 // selection is also used as a stop condition for the adaptive mask, which
1220 // cannot grow pass the limits of the this normally grown selection.
1222 growFilter.process(pixelSelection, rect);
1223
1224 const qint32 maskScanLineSize = rect.width();
1225 const KoColorSpace *referenceColorSpace = m_referenceDevice->colorSpace();
1226 const qint32 referencePixelSize = referenceColorSpace->pixelSize();
1227 const qint32 referenceScanLineSize = maskScanLineSize * referencePixelSize;
1228 // Some buffers to store the working scanlines
1229 QVector<quint8> maskBuffer(maskScanLineSize * 2);
1230 QVector<quint8> referenceBuffer(referenceScanLineSize * 2);
1231 QVector<quint8> selectionBuffer(maskScanLineSize);
1232 quint8 *maskScanLines[2] = {maskBuffer.data(), maskBuffer.data() + maskScanLineSize};
1233 quint8 *referenceScanLines[2] = {referenceBuffer.data(), referenceBuffer.data() + referenceScanLineSize};
1234 quint8 *selectionScanLine = selectionBuffer.data();
1235 // Helper function to test if a pixel can be selected
1236 auto testSelectPixel =
1237 [referenceColorSpace]
1238 (quint8 pixelOpacity, quint8 pixelIntensity,
1239 const quint8 *testMaskPixel, const quint8 *testReferencePixel) -> bool
1240 {
1241 if (*testMaskPixel) {
1242 const quint8 testOpacity = referenceColorSpace->opacityU8(testReferencePixel);
1243 if (pixelOpacity >= testOpacity) {
1244 // Special case for when the neighbor pixel is fully transparent.
1245 // In that case do not compare the intensity
1246 if (testOpacity == MIN_SELECTED) {
1247 return true;
1248 }
1249 // If the opacity test passes we still have to perform the
1250 // intensity test
1251 const quint8 testIntensity = referenceColorSpace->intensity8(testReferencePixel);
1252 if (pixelIntensity <= testIntensity) {
1253 return true;
1254 }
1255 }
1256 }
1257 return false;
1258 };
1259
1260 // Top-left to bottom-right pass
1261 // First row
1262 {
1263 mask->readBytes(maskScanLines[1], rect.left(), rect.top(), maskScanLineSize, 1);
1264 m_referenceDevice->readBytes(referenceScanLines[1], rect.left(), rect.top(), maskScanLineSize, 1);
1265 pixelSelection->readBytes(selectionScanLine, rect.left(), rect.top(), maskScanLineSize, 1);
1266 quint8 *currentMaskScanLineBegin = maskScanLines[1];
1267 quint8 *currentMaskScanLineEnd = maskScanLines[1] + maskScanLineSize;
1268 quint8 *currentReferenceScanLineBegin = referenceScanLines[1];
1269 quint8 *currentSelectionScanLineBegin = selectionScanLine;
1270 // First pixel
1271 ++currentMaskScanLineBegin;
1272 currentReferenceScanLineBegin += referencePixelSize;
1273 ++currentSelectionScanLineBegin;
1274 // Rest of pixels
1275 while (currentMaskScanLineBegin != currentMaskScanLineEnd) {
1276 if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1277 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1278 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1279
1280 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1281 currentMaskScanLineBegin - 1,
1282 currentReferenceScanLineBegin - referencePixelSize);
1283 if (pixelIsSelected) {
1284 *currentMaskScanLineBegin = MAX_SELECTED;
1285 }
1286 }
1287 ++currentMaskScanLineBegin;
1288 currentReferenceScanLineBegin += referencePixelSize;
1289 ++currentSelectionScanLineBegin;
1290 }
1291 mask->writeBytes(maskScanLines[1], rect.left(), rect.top(), maskScanLineSize, 1);
1292 }
1293 // Rest of rows
1294 for (qint32 y = rect.top() + 1; y <= rect.bottom(); ++y) {
1295 rotatePointers(maskScanLines, 2);
1296 rotatePointers(referenceScanLines, 2);
1297 mask->readBytes(maskScanLines[1], rect.left(), y, maskScanLineSize, 1);
1298 m_referenceDevice->readBytes(referenceScanLines[1], rect.left(), y, maskScanLineSize, 1);
1299 pixelSelection->readBytes(selectionScanLine, rect.left(), y, maskScanLineSize, 1);
1300 quint8 *currentMaskScanLineBegin = maskScanLines[1];
1301 quint8 *currentMaskScanLineEnd = maskScanLines[1] + maskScanLineSize;
1302 quint8 *currentReferenceScanLineBegin = referenceScanLines[1];
1303 quint8 *topMaskScanLineBegin = maskScanLines[0];
1304 quint8 *topReferenceScanLineBegin = referenceScanLines[0];
1305 quint8 *currentSelectionScanLineBegin = selectionScanLine;
1306 // First pixel
1307 {
1308 if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1309 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1310 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1311
1312 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1313 topMaskScanLineBegin,
1314 topReferenceScanLineBegin);
1315 if (!pixelIsSelected) {
1316 pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1317 topMaskScanLineBegin + 1,
1318 topReferenceScanLineBegin + referencePixelSize);
1319 }
1320 if (pixelIsSelected) {
1321 *currentMaskScanLineBegin = MAX_SELECTED;
1322 }
1323 }
1324 ++currentMaskScanLineBegin;
1325 currentReferenceScanLineBegin += referencePixelSize;
1326 ++topMaskScanLineBegin;
1327 topReferenceScanLineBegin += referencePixelSize;
1328 ++currentSelectionScanLineBegin;
1329 }
1330 // Rest of pixels
1331 while (currentMaskScanLineBegin != (currentMaskScanLineEnd - 1)) {
1332 if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1333 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1334 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1335
1336 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1337 topMaskScanLineBegin - 1,
1338 topReferenceScanLineBegin - referencePixelSize);
1339 if (!pixelIsSelected) {
1340 pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1341 topMaskScanLineBegin,
1342 topReferenceScanLineBegin);
1343 if (!pixelIsSelected) {
1344 pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1345 topMaskScanLineBegin + 1,
1346 topReferenceScanLineBegin + referencePixelSize);
1347 if (!pixelIsSelected) {
1348 pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1349 currentMaskScanLineBegin - 1,
1350 currentReferenceScanLineBegin - referencePixelSize);
1351 }
1352 }
1353 }
1354 if (pixelIsSelected) {
1355 *currentMaskScanLineBegin = MAX_SELECTED;
1356 }
1357 }
1358 ++currentMaskScanLineBegin;
1359 currentReferenceScanLineBegin += referencePixelSize;
1360 ++topMaskScanLineBegin;
1361 topReferenceScanLineBegin += referencePixelSize;
1362 ++currentSelectionScanLineBegin;
1363 }
1364 // Last pixel
1365 {
1366 if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1367 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1368 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1369
1370 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1371 topMaskScanLineBegin - 1,
1372 topReferenceScanLineBegin - referencePixelSize);
1373 if (!pixelIsSelected) {
1374 pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1375 topMaskScanLineBegin,
1376 topReferenceScanLineBegin);
1377 if (!pixelIsSelected) {
1378 pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1379 currentMaskScanLineBegin - 1,
1380 currentReferenceScanLineBegin - referencePixelSize);
1381 }
1382 }
1383 if (pixelIsSelected) {
1384 *currentMaskScanLineBegin = MAX_SELECTED;
1385 }
1386 }
1387 }
1388 mask->writeBytes(maskScanLines[1], rect.left(), y, maskScanLineSize, 1);
1389 }
1390
1391 // Bottom-right to top-left pass
1392 // Last row
1393 {
1394 mask->readBytes(maskScanLines[1], rect.left(), rect.bottom(), maskScanLineSize, 1);
1395 m_referenceDevice->readBytes(referenceScanLines[1], rect.left(), rect.bottom(), maskScanLineSize, 1);
1396 pixelSelection->readBytes(selectionScanLine, rect.left(), rect.bottom(), maskScanLineSize, 1);
1397 quint8 *currentMaskScanLineBegin = maskScanLines[1] + maskScanLineSize - 1;
1398 quint8 *currentMaskScanLineEnd = maskScanLines[1] - 1;
1399 quint8 *currentReferenceScanLineBegin = referenceScanLines[1] + referenceScanLineSize - referencePixelSize;
1400 quint8 *currentSelectionScanLineBegin = selectionScanLine + maskScanLineSize - 1;
1401 // Last pixel
1402 --currentMaskScanLineBegin;
1403 currentReferenceScanLineBegin -= referencePixelSize;
1404 --currentSelectionScanLineBegin;
1405 // Rest of pixels
1406 while (currentMaskScanLineBegin != currentMaskScanLineEnd) {
1407 if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1408 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1409 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1410
1411 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1412 currentMaskScanLineBegin + 1,
1413 currentReferenceScanLineBegin + referencePixelSize);
1414 if (pixelIsSelected) {
1415 *currentMaskScanLineBegin = MAX_SELECTED;
1416 }
1417 }
1418 --currentMaskScanLineBegin;
1419 currentReferenceScanLineBegin -= referencePixelSize;
1420 --currentSelectionScanLineBegin;
1421 }
1422 mask->writeBytes(maskScanLines[1], rect.left(), rect.top(), maskScanLineSize, 1);
1423 }
1424 // Rest of rows
1425 for (qint32 y = rect.bottom() - 1; y >= rect.top(); --y) {
1426 rotatePointers(maskScanLines, 2);
1427 rotatePointers(referenceScanLines, 2);
1428 mask->readBytes(maskScanLines[1], rect.left(), y, maskScanLineSize, 1);
1429 m_referenceDevice->readBytes(referenceScanLines[1], rect.left(), y, maskScanLineSize, 1);
1430 pixelSelection->readBytes(selectionScanLine, rect.left(), y, maskScanLineSize, 1);
1431 quint8 *currentMaskScanLineBegin = maskScanLines[1] + maskScanLineSize - 1;
1432 quint8 *currentMaskScanLineEnd = maskScanLines[1] - 1;
1433 quint8 *currentReferenceScanLineBegin = referenceScanLines[1] + referenceScanLineSize - referencePixelSize;
1434 quint8 *bottomMaskScanLineBegin = maskScanLines[0] + maskScanLineSize - 1;
1435 quint8 *bottomReferenceScanLineBegin = referenceScanLines[0] + referenceScanLineSize - referencePixelSize;
1436 quint8 *currentSelectionScanLineBegin = selectionScanLine + maskScanLineSize - 1;
1437 // Last pixel
1438 {
1439 if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1440 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1441 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1442
1443 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1444 bottomMaskScanLineBegin,
1445 bottomReferenceScanLineBegin);
1446 if (!pixelIsSelected) {
1447 pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1448 bottomMaskScanLineBegin - 1,
1449 bottomReferenceScanLineBegin - referencePixelSize);
1450 }
1451 if (pixelIsSelected) {
1452 *currentMaskScanLineBegin = MAX_SELECTED;
1453 }
1454 }
1455 --currentMaskScanLineBegin;
1456 currentReferenceScanLineBegin -= referencePixelSize;
1457 --bottomMaskScanLineBegin;
1458 bottomReferenceScanLineBegin -= referencePixelSize;
1459 --currentSelectionScanLineBegin;
1460 }
1461 // Rest of pixels
1462 while (currentMaskScanLineBegin != (currentMaskScanLineEnd + 1)) {
1463 if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1464 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1465 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1466
1467 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1468 bottomMaskScanLineBegin + 1,
1469 bottomReferenceScanLineBegin + referencePixelSize);
1470 if (!pixelIsSelected) {
1471 pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1472 bottomMaskScanLineBegin,
1473 bottomReferenceScanLineBegin);
1474 if (!pixelIsSelected) {
1475 pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1476 bottomMaskScanLineBegin - 1,
1477 bottomReferenceScanLineBegin - referencePixelSize);
1478 if (!pixelIsSelected) {
1479 pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1480 currentMaskScanLineBegin + 1,
1481 currentReferenceScanLineBegin + referencePixelSize);
1482 }
1483 }
1484 }
1485 if (pixelIsSelected) {
1486 *currentMaskScanLineBegin = MAX_SELECTED;
1487 }
1488 }
1489 --currentMaskScanLineBegin;
1490 currentReferenceScanLineBegin -= referencePixelSize;
1491 --bottomMaskScanLineBegin;
1492 bottomReferenceScanLineBegin -= referencePixelSize;
1493 --currentSelectionScanLineBegin;
1494 }
1495 // First pixel
1496 {
1497 if (*currentMaskScanLineBegin == MIN_SELECTED && *currentSelectionScanLineBegin != MIN_SELECTED) {
1498 const quint8 currentOpacity = referenceColorSpace->opacityU8(currentReferenceScanLineBegin);
1499 const quint8 currentIntensity = referenceColorSpace->intensity8(currentReferenceScanLineBegin);
1500
1501 bool pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1502 bottomMaskScanLineBegin + 1,
1503 bottomReferenceScanLineBegin + referencePixelSize);
1504 if (!pixelIsSelected) {
1505 pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1506 bottomMaskScanLineBegin,
1507 bottomReferenceScanLineBegin);
1508 if (!pixelIsSelected) {
1509 pixelIsSelected = testSelectPixel(currentOpacity, currentIntensity,
1510 currentMaskScanLineBegin + 1,
1511 currentReferenceScanLineBegin + referencePixelSize);
1512 }
1513 }
1514 if (pixelIsSelected) {
1515 *currentMaskScanLineBegin = MAX_SELECTED;
1516 }
1517 }
1518 }
1519 mask->writeBytes(maskScanLines[1], rect.left(), y, maskScanLineSize, 1);
1520 }
1521
1522 // Combine the adaptively grown mask with the normally grown mask. The
1523 // adaptively grown mask is used as a binary mask to erase some of the
1524 // pixels of the normally grown mask
1525 {
1527 KisSequentialIterator it2(pixelSelection, rect);
1528 while (it1.nextPixel() && it2.nextPixel()) {
1529 *it2.rawData() *= (*it1.rawDataConst() != MIN_SELECTED);
1530 }
1531 }
1532}
const KoColorSpace * colorSpace() const
void readBytes(quint8 *data, qint32 x, qint32 y, qint32 w, qint32 h) const
void writeBytes(const quint8 *data, qint32 x, qint32 y, qint32 w, qint32 h)
void rotatePointers(quint8 **p, quint32 n)
virtual quint8 intensity8(const quint8 *src) const =0
virtual quint32 pixelSize() const =0
virtual quint8 opacityU8(const quint8 *pixel) const =0
const quint8 MAX_SELECTED
Definition kis_global.h:32
const quint8 MIN_SELECTED
Definition kis_global.h:33

References KisPaintDevice::colorSpace(), KoColorSpace::intensity8(), m_radius, m_referenceDevice, MAX_SELECTED, MIN_SELECTED, KisSequentialIteratorBase< IteratorPolicy, SourcePolicy, ProgressPolicy >::nextPixel(), KoColorSpace::opacityU8(), KoColorSpace::pixelSize(), KisGrowSelectionFilter::process(), KisSequentialIteratorBase< IteratorPolicy, SourcePolicy, ProgressPolicy >::rawData(), KisSequentialIteratorBase< IteratorPolicy, SourcePolicy, ProgressPolicy >::rawDataConst(), KisPaintDevice::readBytes(), KisSelectionFilter::rotatePointers(), and KisPaintDevice::writeBytes().

Member Data Documentation

◆ m_radius

qint32 KisGrowUntilDarkestPixelSelectionFilter::m_radius
private

Definition at line 241 of file kis_selection_filters.h.

◆ m_referenceDevice

KisPaintDeviceSP KisGrowUntilDarkestPixelSelectionFilter::m_referenceDevice
private

Definition at line 242 of file kis_selection_filters.h.


The documentation for this class was generated from the following files: