Skip to content

Commit

Permalink
Merge pull request #19 from scribblemaniac/bucket-tool-optimizations2
Browse files Browse the repository at this point in the history
More bucket tool optimizations
  • Loading branch information
MrStevns authored Apr 13, 2022
2 parents a93b0ce + 8c0fabf commit c89ebb4
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 20 deletions.
65 changes: 46 additions & 19 deletions core_lib/src/graphics/bitmap/bitmapimage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -774,12 +774,12 @@ bool BitmapImage::compareColor(QRgb newColor, QRgb oldColor, int tolerance, QHas
// Get Eulcidian distance between colors
// Not an accurate representation of human perception,
// but it's the best any image editing program ever does
int diffRed = static_cast<int>(qPow(qRed(oldColor) - qRed(newColor), 2));
int diffGreen = static_cast<int>(qPow(qGreen(oldColor) - qGreen(newColor), 2));
int diffBlue = static_cast<int>(qPow(qBlue(oldColor) - qBlue(newColor), 2));
int diffRed = abs(qRed(oldColor) - qRed(newColor)) << 2;
int diffGreen = abs(qGreen(oldColor) - qGreen(newColor)) << 2;
int diffBlue = abs(qBlue(oldColor) - qBlue(newColor)) << 2;
// This may not be the best way to handle alpha since the other channels become less relevant as
// the alpha is reduces (ex. QColor(0,0,0,0) is the same as QColor(255,255,255,0))
int diffAlpha = static_cast<int>(qPow(qAlpha(oldColor) - qAlpha(newColor), 2));
int diffAlpha = abs(qAlpha(oldColor) - qAlpha(newColor)) << 2;

bool isSimilar = (diffRed + diffGreen + diffBlue + diffAlpha) <= tolerance;

Expand All @@ -800,8 +800,10 @@ bool BitmapImage::floodFill(BitmapImage** replaceImage,
int tolerance,
const int expandValue)
{
QRect fillBounds = targetImage->mBounds;
// Fill region must be 1 pixel larger than the target image to fill regions on the edge connected only by transparent pixels
QRect fillBounds = targetImage->mBounds.adjusted(-1, -1, 1, 1);
QRect maxBounds = cameraRect.united(fillBounds).adjusted(-expandValue, -expandValue, expandValue, expandValue);
const int maxWidth = maxBounds.width(), left = maxBounds.left(), top = maxBounds.top();

// If the point we are supposed to fill is outside the max bounds, do nothing
if(!maxBounds.contains(point))
Expand All @@ -813,31 +815,42 @@ bool BitmapImage::floodFill(BitmapImage** replaceImage,
tolerance = static_cast<int>(qPow(tolerance, 2));

QRect newBounds;
bool *filledPixels = floodFillPoints(targetImage, fillBounds, maxBounds, point, tolerance, newBounds);
bool shouldFillBorder = false;
bool *filledPixels = floodFillPoints(targetImage, fillBounds, maxBounds, point, tolerance, newBounds, shouldFillBorder);

const QRect& expandRect = newBounds.adjusted(-expandValue, -expandValue, expandValue, expandValue);
QRect translatedSearchBounds = newBounds.translated(-maxBounds.topLeft());

if (shouldFillBorder)
{
for (int y = 0; y < maxBounds.height(); y++)
{
for (int x = 0; x < maxBounds.width(); x++)
{
if(!translatedSearchBounds.contains(x, y))
{
filledPixels[y*maxWidth+x] = true;
}
}
}
newBounds = maxBounds;
}

// The scanned bounds should take the expansion into account
const QRect& expandRect = newBounds.adjusted(-expandValue, -expandValue, expandValue, expandValue);
if (expandValue > 0) {
newBounds = expandRect;
}

// The new bounds was smaller than the max, so set the size to maxBounds
// This ensures that the expanded area is not doubled.
// because the initial maxBounds is already expanded
if (!maxBounds.contains(newBounds)) {
newBounds = maxBounds;
}
translatedSearchBounds = newBounds.translated(-maxBounds.topLeft());

*replaceImage = new BitmapImage(newBounds, Qt::transparent);

const int maxWidth = maxBounds.width(), left = maxBounds.left(), top = maxBounds.top();

const QRect& translatedSearchBounds = newBounds.translated(-maxBounds.topLeft());
if (expandValue > 0) {
expandFill(filledPixels, translatedSearchBounds, maxBounds, expandValue);
}

*replaceImage = new BitmapImage(newBounds, Qt::transparent);

// Fill all the found pixels
for (int y = translatedSearchBounds.top(); y <= translatedSearchBounds.bottom(); y++)
{
Expand All @@ -864,10 +877,12 @@ bool* BitmapImage::floodFillPoints(const BitmapImage* targetImage,
const QRect& maxBounds,
QPoint point,
const int tolerance,
QRect& newBounds)
QRect& newBounds,
bool& fillBorder)
{
QRgb oldColor = targetImage->constScanLine(point.x(), point.y());
oldColor = qRgba(qRed(oldColor), qGreen(oldColor), qBlue(oldColor), qAlpha(oldColor));
QRect borderBounds = searchBounds.adjusted(1,1,-1,-1);

// Preparations
QList<QPoint> queue; // queue all the pixels of the filled area (as they are found)
Expand All @@ -879,11 +894,20 @@ bool* BitmapImage::floodFillPoints(const BitmapImage* targetImage,
bool spanLeft = false;
bool spanRight = false;

if (!searchBounds.contains(point))
{
// If point is outside the search area, move it anywhere in the 1px transparent border
point = searchBounds.topLeft();
}

queue.append(point);
// Preparations END

bool *filledPixels = new bool[maxBounds.height()*maxBounds.width()]{};

// True if the algorithm has attempted to fill a pixel outside the search bounds
bool checkOutside = false;

BlitRect blitBounds(point);
while (!queue.empty())
{
Expand All @@ -898,8 +922,8 @@ bool* BitmapImage::floodFillPoints(const BitmapImage* targetImage,
int yCoord = point.y() - maxBounds.top();

// In case we fill outside the searchBounds, expand the search area to the max.
if (!searchBounds.contains(xCoord, yCoord)) {
searchBounds = maxBounds;
if (!borderBounds.contains(point)) {
checkOutside = true;
}

if (filledPixels[yCoord*maxBounds.width()+xCoord]) continue;
Expand Down Expand Up @@ -947,7 +971,10 @@ bool* BitmapImage::floodFillPoints(const BitmapImage* targetImage,
}
}

fillBorder = checkOutside && compareColor(Qt::transparent, oldColor, tolerance, cache.data());

newBounds = blitBounds;

return filledPixels;
}

Expand Down
3 changes: 2 additions & 1 deletion core_lib/src/graphics/bitmap/bitmapimage.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ class BitmapImage : public KeyFrame
QRect searchBounds, const QRect& maxBounds,
QPoint point,
const int tolerance,
QRect& newBounds);
QRect& newBounds,
bool &fillBorder);
static void expandFill(bool* fillPixels, const QRect& searchBounds, const QRect& maxBounds, int expand);

void drawLine(QPointF P1, QPointF P2, QPen pen, QPainter::CompositionMode cm, bool antialiasing);
Expand Down

0 comments on commit c89ebb4

Please sign in to comment.