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 1186 of file kis_selection_filters.cpp.

1188 : m_radius(radius)
1189 , m_referenceDevice(referenceDevice)
1190{
1191}

Member Function Documentation

◆ changeRect()

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

Reimplemented from KisSelectionFilter.

Definition at line 1198 of file kis_selection_filters.cpp.

1199{
1200 Q_UNUSED(defaultBounds);
1201
1202 return rect.adjusted(-m_radius, -m_radius, m_radius, m_radius);
1203}

References m_radius.

◆ name()

KUndo2MagicString KisGrowUntilDarkestPixelSelectionFilter::name ( )
overridevirtual

Reimplemented from KisSelectionFilter.

Definition at line 1193 of file kis_selection_filters.cpp.

1194{
1195 return kundo2_i18n("Grow Selection Until Darkest Pixel");
1196}
KUndo2MagicString kundo2_i18n(const char *text)

References kundo2_i18n().

◆ process()

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

Implements KisSelectionFilter.

Definition at line 1205 of file kis_selection_filters.cpp.

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