diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index 3cc6a424ef..f683d435c5 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -510,6 +510,9 @@ if (BUILD_LAGOM) add_executable(icc ../../Userland/Utilities/icc.cpp) target_link_libraries(icc LibCore LibGfx LibMain) + add_executable(image ../../Userland/Utilities/image.cpp) + target_link_libraries(image LibCore LibGfx LibMain) + add_executable(ttfdisasm ../../Userland/Utilities/ttfdisasm.cpp) target_link_libraries(ttfdisasm LibGfx LibMain) diff --git a/Userland/Utilities/CMakeLists.txt b/Userland/Utilities/CMakeLists.txt index ec1928cf68..530a5811ac 100644 --- a/Userland/Utilities/CMakeLists.txt +++ b/Userland/Utilities/CMakeLists.txt @@ -95,6 +95,7 @@ target_link_libraries(gunzip PRIVATE LibCompress) target_link_libraries(gzip PRIVATE LibCompress) target_link_libraries(headless-browser PRIVATE LibCrypto LibGemini LibGfx LibHTTP LibTLS LibWeb LibWebSocket LibIPC LibJS) target_link_libraries(icc PRIVATE LibGfx LibVideo) +target_link_libraries(image PRIVATE LibGfx) target_link_libraries(image2bin PRIVATE LibGfx) target_link_libraries(jail-attach PRIVATE LibCore LibMain) target_link_libraries(jail-create PRIVATE LibCore LibMain) diff --git a/Userland/Utilities/image.cpp b/Userland/Utilities/image.cpp new file mode 100644 index 0000000000..fa105303b9 --- /dev/null +++ b/Userland/Utilities/image.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +ErrorOr serenity_main(Main::Arguments arguments) +{ + Core::ArgsParser args_parser; + + StringView in_path; + args_parser.add_positional_argument(in_path, "Path to input image file", "FILE"); + + StringView out_path; + args_parser.add_option(out_path, "Path to output image file", "output", 'o', "FILE"); + + args_parser.parse(arguments); + + if (out_path.is_empty()) { + warnln("-o is required"); + return 1; + } + + auto file = TRY(Core::MappedFile::map(in_path)); + auto decoder = Gfx::ImageDecoder::try_create_for_raw_bytes(file->bytes()); + + // This uses ImageDecoder instead of Bitmap::load_from_file() to have more control + // over selecting a frame, access color profile data, and so on in the future. + auto frame = TRY(decoder->frame(0)).image; + + ByteBuffer bytes; + if (out_path.ends_with(".bmp"sv, CaseSensitivity::CaseInsensitive)) { + bytes = Gfx::BMPWriter().dump(frame); + } else if (out_path.ends_with(".png"sv, CaseSensitivity::CaseInsensitive)) { + bytes = TRY(Gfx::PNGWriter::encode(*frame)); + } else if (out_path.ends_with(".qoi"sv, CaseSensitivity::CaseInsensitive)) { + bytes = Gfx::QOIWriter::encode(*frame); + } else { + warnln("can only write .bmp, .png, and .qoi"); + return 1; + } + + auto output_stream = TRY(Core::File::open(out_path, Core::File::OpenMode::Write)); + TRY(output_stream->write_entire_buffer(bytes)); + + return 0; +}