diff --git a/Userland/Utilities/pixelflut.cpp b/Userland/Utilities/pixelflut.cpp index a2b4eb2ae5..ee12868a61 100644 --- a/Userland/Utilities/pixelflut.cpp +++ b/Userland/Utilities/pixelflut.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -16,19 +17,42 @@ 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 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 // https://github.com/defnull/pixelflut#pixelflut-protocol class Client : public RefCounted { public: - static ErrorOr> create(StringView image_path, StringView server, size_t x, size_t y); + static ErrorOr> create(StringView image_path, StringView server, size_t x, size_t y, FloodStrategy strategy); ErrorOr run(); private: - Client(NonnullOwnPtr, NonnullRefPtr, Gfx::IntSize, Gfx::IntPoint); + Client(NonnullOwnPtr, NonnullRefPtr, Gfx::IntSize, Gfx::IntPoint, FloodStrategy); ErrorOr send_current_pixel(); - void next_pixel(); + void next_scanline_pixel(); + void next_random_pixel(); + void next_column_pixel(); NonnullOwnPtr m_socket; @@ -37,9 +61,10 @@ private: Gfx::IntPoint const m_image_offset; Gfx::IntPoint m_current_point { 0, 0 }; + FloodStrategy m_strategy; }; -ErrorOr> Client::create(StringView image_path, StringView server, size_t x, size_t y) +ErrorOr> Client::create(StringView image_path, StringView server, size_t x, size_t y, FloodStrategy strategy) { // Extract hostname and port and connect to server. auto parts = server.split_view(':'); @@ -88,20 +113,27 @@ ErrorOr> Client::create(StringView image_path, StringView 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 socket, NonnullRefPtr image, Gfx::IntSize canvas_size, Gfx::IntPoint image_offset) +Client::Client(NonnullOwnPtr socket, NonnullRefPtr image, Gfx::IntSize canvas_size, Gfx::IntPoint image_offset, FloodStrategy strategy) : m_socket(move(socket)) , m_image(move(image)) , m_canvas_size(canvas_size) , m_image_offset(image_offset) + , m_strategy(strategy) { outln("Connected to server, image {}, canvas size {}", m_image, m_canvas_size); } ErrorOr Client::run() { + Function 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) { TRY(send_current_pixel()); next_pixel(); @@ -127,7 +159,7 @@ ErrorOr Client::send_current_pixel() return {}; } -void Client::next_pixel() +void Client::next_scanline_pixel() { m_current_point.set_x(m_current_point.x() + 1); 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 serenity_main(Main::Arguments arguments) { Core::EventLoop loop; @@ -148,11 +201,13 @@ ErrorOr serenity_main(Main::Arguments arguments) size_t x; size_t y; StringView server; + StringView strategy_string { "scanline"sv }; Core::ArgsParser args_parser; 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(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.parse(arguments); @@ -161,7 +216,13 @@ ErrorOr serenity_main(Main::Arguments arguments) 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());