mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
LibGfx: Implement flood fill algorithm in Bitmap class
This change implements a flood fill algorithm for the Bitmap class. This will be leveraged by various Tools in PixelPaint. Moving the code into Bitmap reduces the duplication of the algorithm throughout the PixelPaint Tools (currently Bucket Tool and Wand Select). The flood fill function requires you to pass in a threshold value (0 - 100) as well as a lambda for what to do when a pixel gets reached. The lambda is provided an IntPoint representing the coordinates of the pixel that was just reached. The genericized lambda approach allows for a variety of things to be done as the flood algorithm progresses. For example, the Bucket Tool will paint each pixel that gets reached with the fill_color. The Wand Select tool wont actually alter the bitmap itself, instead it uses the reached pixels to alter a selection mask.
This commit is contained in:
parent
6933644b2e
commit
eec881ea34
Notes:
sideshowbarker
2024-07-17 16:42:19 +09:00
Author: https://github.com/tslater2006 Commit: https://github.com/SerenityOS/serenity/commit/eec881ea34 Pull-request: https://github.com/SerenityOS/serenity/pull/15394 Reviewed-by: https://github.com/MacDue Reviewed-by: https://github.com/TobyAsE ✅
3 changed files with 62 additions and 0 deletions
|
@ -1,14 +1,17 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2022, Timothy Slater <tslater2006@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Bitmap.h>
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Queue.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Try.h>
|
||||
|
@ -633,4 +636,50 @@ Optional<Color> Bitmap::solid_color(u8 alpha_threshold) const
|
|||
return color;
|
||||
}
|
||||
|
||||
void Bitmap::flood_visit_from_point(Gfx::IntPoint const& start_point, int threshold,
|
||||
Function<void(Gfx::IntPoint location)> pixel_reached)
|
||||
{
|
||||
|
||||
VERIFY(rect().contains(start_point));
|
||||
|
||||
auto target_color = get_pixel(start_point.x(), start_point.y());
|
||||
|
||||
float threshold_normalized_squared = (threshold / 100.0f) * (threshold / 100.0f);
|
||||
|
||||
Queue<Gfx::IntPoint> points_to_visit = Queue<Gfx::IntPoint>();
|
||||
|
||||
points_to_visit.enqueue(start_point);
|
||||
pixel_reached(start_point);
|
||||
auto flood_mask = AK::Bitmap::must_create(width() * height(), false);
|
||||
|
||||
flood_mask.set(width() * start_point.y() + start_point.x(), true);
|
||||
|
||||
// This implements a non-recursive flood fill. This is a breadth-first search of paintable neighbors
|
||||
// As we find neighbors that are reachable we call the location_reached callback, add them to the queue, and mark them in the mask
|
||||
while (!points_to_visit.is_empty()) {
|
||||
auto current_point = points_to_visit.dequeue();
|
||||
auto candidate_points = Array {
|
||||
current_point.moved_left(1),
|
||||
current_point.moved_right(1),
|
||||
current_point.moved_up(1),
|
||||
current_point.moved_down(1)
|
||||
};
|
||||
for (auto candidate_point : candidate_points) {
|
||||
auto flood_mask_index = width() * candidate_point.y() + candidate_point.x();
|
||||
if (!rect().contains(candidate_point))
|
||||
continue;
|
||||
|
||||
auto pixel_color = get_pixel<Gfx::StorageFormat::BGRA8888>(candidate_point.x(), candidate_point.y());
|
||||
auto can_paint = pixel_color.distance_squared_to(target_color) <= threshold_normalized_squared;
|
||||
|
||||
if (flood_mask.get(flood_mask_index) == false && can_paint) {
|
||||
points_to_visit.enqueue(candidate_point);
|
||||
pixel_reached(candidate_point);
|
||||
}
|
||||
|
||||
flood_mask.set(flood_mask_index, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2022, Timothy Slater <tslater2006@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -7,6 +8,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <LibCore/AnonymousBuffer.h>
|
||||
#include <LibGfx/Color.h>
|
||||
|
@ -242,6 +244,8 @@ public:
|
|||
|
||||
[[nodiscard]] Optional<Color> solid_color(u8 alpha_threshold = 0) const;
|
||||
|
||||
void flood_visit_from_point(Gfx::IntPoint const& start_point, int threshold, Function<void(Gfx::IntPoint location)> pixel_reached);
|
||||
|
||||
private:
|
||||
Bitmap(BitmapFormat, IntSize const&, int, BackingStore const&);
|
||||
Bitmap(BitmapFormat, IntSize const&, int, size_t pitch, void*);
|
||||
|
|
|
@ -255,6 +255,15 @@ public:
|
|||
alpha() * other.alpha() / 255);
|
||||
}
|
||||
|
||||
constexpr float distance_squared_to(Color const& other) const
|
||||
{
|
||||
int a = other.red() - red();
|
||||
int b = other.green() - green();
|
||||
int c = other.blue() - blue();
|
||||
int d = other.alpha() - alpha();
|
||||
return (a * a + b * b + c * c + d * d) / (4.0f * 255.0f * 255.0f);
|
||||
}
|
||||
|
||||
constexpr u8 luminosity() const
|
||||
{
|
||||
return (red() * 0.2126f + green() * 0.7152f + blue() * 0.0722f);
|
||||
|
|
Loading…
Reference in a new issue