mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 09:04:59 +00:00
pixelflut: Implement alternative flooding strategies
These allow column-wise, traditional scanline, or random pixel flooding. Depending on the use case and amount of contestion, any of the strategies may work best, as tested on 37c3.
This commit is contained in:
parent
fdfd4b5e3d
commit
bdd92e881f
1 changed files with 69 additions and 8 deletions
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <AK/Error.h>
|
#include <AK/Error.h>
|
||||||
#include <AK/NonnullRefPtr.h>
|
#include <AK/NonnullRefPtr.h>
|
||||||
|
#include <AK/QuickSelect.h>
|
||||||
#include <LibCore/ArgsParser.h>
|
#include <LibCore/ArgsParser.h>
|
||||||
#include <LibCore/EventLoop.h>
|
#include <LibCore/EventLoop.h>
|
||||||
#include <LibCore/Socket.h>
|
#include <LibCore/Socket.h>
|
||||||
|
@ -16,19 +17,42 @@
|
||||||
|
|
||||||
constexpr StringView get_command = "SIZE\n"sv;
|
constexpr StringView get_command = "SIZE\n"sv;
|
||||||
|
|
||||||
|
// Which strategies to use for flooding the image to the server.
|
||||||
|
enum class FloodStrategy {
|
||||||
|
// Send image row by row, like a CRT scanline.
|
||||||
|
Scanline,
|
||||||
|
// Send random pixels.
|
||||||
|
Random,
|
||||||
|
// Send image column by column.
|
||||||
|
Columns,
|
||||||
|
};
|
||||||
|
|
||||||
|
static Optional<FloodStrategy> parse_strategy(StringView name)
|
||||||
|
{
|
||||||
|
if (name.equals_ignoring_ascii_case("scanline"sv))
|
||||||
|
return FloodStrategy::Scanline;
|
||||||
|
if (name.equals_ignoring_ascii_case("random"sv))
|
||||||
|
return FloodStrategy::Random;
|
||||||
|
if (name.equals_ignoring_ascii_case("columns"sv))
|
||||||
|
return FloodStrategy::Columns;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// Client for the Pixelflut protocol
|
// Client for the Pixelflut protocol
|
||||||
// https://github.com/defnull/pixelflut#pixelflut-protocol
|
// https://github.com/defnull/pixelflut#pixelflut-protocol
|
||||||
class Client : public RefCounted<Client> {
|
class Client : public RefCounted<Client> {
|
||||||
public:
|
public:
|
||||||
static ErrorOr<NonnullRefPtr<Client>> create(StringView image_path, StringView server, size_t x, size_t y);
|
static ErrorOr<NonnullRefPtr<Client>> create(StringView image_path, StringView server, size_t x, size_t y, FloodStrategy strategy);
|
||||||
|
|
||||||
ErrorOr<void> run();
|
ErrorOr<void> run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Client(NonnullOwnPtr<Core::BufferedTCPSocket>, NonnullRefPtr<Gfx::Bitmap>, Gfx::IntSize, Gfx::IntPoint);
|
Client(NonnullOwnPtr<Core::BufferedTCPSocket>, NonnullRefPtr<Gfx::Bitmap>, Gfx::IntSize, Gfx::IntPoint, FloodStrategy);
|
||||||
|
|
||||||
ErrorOr<void> send_current_pixel();
|
ErrorOr<void> send_current_pixel();
|
||||||
void next_pixel();
|
void next_scanline_pixel();
|
||||||
|
void next_random_pixel();
|
||||||
|
void next_column_pixel();
|
||||||
|
|
||||||
NonnullOwnPtr<Core::BufferedTCPSocket> m_socket;
|
NonnullOwnPtr<Core::BufferedTCPSocket> m_socket;
|
||||||
|
|
||||||
|
@ -37,9 +61,10 @@ private:
|
||||||
Gfx::IntPoint const m_image_offset;
|
Gfx::IntPoint const m_image_offset;
|
||||||
|
|
||||||
Gfx::IntPoint m_current_point { 0, 0 };
|
Gfx::IntPoint m_current_point { 0, 0 };
|
||||||
|
FloodStrategy m_strategy;
|
||||||
};
|
};
|
||||||
|
|
||||||
ErrorOr<NonnullRefPtr<Client>> Client::create(StringView image_path, StringView server, size_t x, size_t y)
|
ErrorOr<NonnullRefPtr<Client>> Client::create(StringView image_path, StringView server, size_t x, size_t y, FloodStrategy strategy)
|
||||||
{
|
{
|
||||||
// Extract hostname and port and connect to server.
|
// Extract hostname and port and connect to server.
|
||||||
auto parts = server.split_view(':');
|
auto parts = server.split_view(':');
|
||||||
|
@ -88,20 +113,27 @@ ErrorOr<NonnullRefPtr<Client>> Client::create(StringView image_path, StringView
|
||||||
image = TRY(image->scaled(fitting_scale, fitting_scale));
|
image = TRY(image->scaled(fitting_scale, fitting_scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Client(move(socket), move(image), canvas_size, Gfx::IntPoint { x, y })));
|
return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Client(move(socket), move(image), canvas_size, Gfx::IntPoint { x, y }, strategy)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Client::Client(NonnullOwnPtr<Core::BufferedTCPSocket> socket, NonnullRefPtr<Gfx::Bitmap> image, Gfx::IntSize canvas_size, Gfx::IntPoint image_offset)
|
Client::Client(NonnullOwnPtr<Core::BufferedTCPSocket> socket, NonnullRefPtr<Gfx::Bitmap> image, Gfx::IntSize canvas_size, Gfx::IntPoint image_offset, FloodStrategy strategy)
|
||||||
: m_socket(move(socket))
|
: m_socket(move(socket))
|
||||||
, m_image(move(image))
|
, m_image(move(image))
|
||||||
, m_canvas_size(canvas_size)
|
, m_canvas_size(canvas_size)
|
||||||
, m_image_offset(image_offset)
|
, m_image_offset(image_offset)
|
||||||
|
, m_strategy(strategy)
|
||||||
{
|
{
|
||||||
outln("Connected to server, image {}, canvas size {}", m_image, m_canvas_size);
|
outln("Connected to server, image {}, canvas size {}", m_image, m_canvas_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> Client::run()
|
ErrorOr<void> Client::run()
|
||||||
{
|
{
|
||||||
|
Function<void(void)> next_pixel = [this] { this->next_scanline_pixel(); };
|
||||||
|
if (m_strategy == FloodStrategy::Columns)
|
||||||
|
next_pixel = [this] { this->next_column_pixel(); };
|
||||||
|
else if (m_strategy == FloodStrategy::Random)
|
||||||
|
next_pixel = [this] { this->next_random_pixel(); };
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
TRY(send_current_pixel());
|
TRY(send_current_pixel());
|
||||||
next_pixel();
|
next_pixel();
|
||||||
|
@ -127,7 +159,7 @@ ErrorOr<void> Client::send_current_pixel()
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::next_pixel()
|
void Client::next_scanline_pixel()
|
||||||
{
|
{
|
||||||
m_current_point.set_x(m_current_point.x() + 1);
|
m_current_point.set_x(m_current_point.x() + 1);
|
||||||
if (m_current_point.x() >= m_image->width()) {
|
if (m_current_point.x() >= m_image->width()) {
|
||||||
|
@ -140,6 +172,27 @@ void Client::next_pixel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::next_column_pixel()
|
||||||
|
{
|
||||||
|
m_current_point.set_y(m_current_point.y() + 1);
|
||||||
|
if (m_current_point.y() >= m_image->height()) {
|
||||||
|
m_current_point.set_y(0);
|
||||||
|
m_current_point.set_x(m_current_point.x() + 1);
|
||||||
|
if (m_current_point.x() >= m_image->width()) {
|
||||||
|
m_current_point.set_x(0);
|
||||||
|
m_current_point.set_y(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::next_random_pixel()
|
||||||
|
{
|
||||||
|
m_current_point = {
|
||||||
|
AK::random_int(0, m_image->width() - 1),
|
||||||
|
AK::random_int(0, m_image->height() - 1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
Core::EventLoop loop;
|
Core::EventLoop loop;
|
||||||
|
@ -148,11 +201,13 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
size_t x;
|
size_t x;
|
||||||
size_t y;
|
size_t y;
|
||||||
StringView server;
|
StringView server;
|
||||||
|
StringView strategy_string { "scanline"sv };
|
||||||
|
|
||||||
Core::ArgsParser args_parser;
|
Core::ArgsParser args_parser;
|
||||||
args_parser.add_option(image_path, "Image to send to server", "image", 'i', "IMAGE");
|
args_parser.add_option(image_path, "Image to send to server", "image", 'i', "IMAGE");
|
||||||
args_parser.add_option(x, "Target x coordinate of the image on the server", "x", 'x', "X");
|
args_parser.add_option(x, "Target x coordinate of the image on the server", "x", 'x', "X");
|
||||||
args_parser.add_option(y, "Target y coordinate of the image on the server", "y", 'y', "Y");
|
args_parser.add_option(y, "Target y coordinate of the image on the server", "y", 'y', "Y");
|
||||||
|
args_parser.add_option(strategy_string, "Pixel flooding strategy (scanline, random or column; default: scanline)", "strategy", 'm', "STRATEGY");
|
||||||
args_parser.add_positional_argument(server, "Pixelflut server (hostname:port)", "server");
|
args_parser.add_positional_argument(server, "Pixelflut server (hostname:port)", "server");
|
||||||
args_parser.parse(arguments);
|
args_parser.parse(arguments);
|
||||||
|
|
||||||
|
@ -161,7 +216,13 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto client = TRY(Client::create(image_path, server, x, y));
|
auto maybe_strategy = parse_strategy(strategy_string);
|
||||||
|
if (!maybe_strategy.has_value()) {
|
||||||
|
warnln("Error: Strategy {} invalid", strategy_string);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto client = TRY(Client::create(image_path, server, x, y, maybe_strategy.release_value()));
|
||||||
|
|
||||||
TRY(client->run());
|
TRY(client->run());
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue