From c4780f4ee1e3dd29d3503c9a5efdba97e06dc22e Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 2 Feb 2024 19:41:08 -0500 Subject: [PATCH] image: Add an --invert-cmyk option This is useful for working with CMYK jpegs extracted from PDFs by mutool. CMYK channels for jpegs in PDFs are inverted compared to CMYK channels in standalone jpegs (!), and mutool doesn't compensate for that. You can now do: mutool extract ~/Downloads/0000/0000711.pdf 461 Followed by: Build/lagom/bin/image -o out.png \ --invert-cmyk \ --assign-color-profile \ Build/lagom/Root/res/icc/Adobe/CMYK/USWebCoatedSWOP.icc \ --convert-to-color-profile serenity-sRGB.icc \ cmyk.jpg Doesn't exactly roll off the keyboard, but at least it's possible. (We should probably add an implicit default CMYK color profile if the input file doesn't have one, and assume conversion to sRGB when saving to a format that can only store RGB.) --- Userland/Utilities/image.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Userland/Utilities/image.cpp b/Userland/Utilities/image.cpp index 250c5d7d16..0a15abcc72 100644 --- a/Userland/Utilities/image.cpp +++ b/Userland/Utilities/image.cpp @@ -42,6 +42,21 @@ static ErrorOr load_image(RefPtr const& decoder, return LoadedImage { internal_format, move(bitmap), TRY(decoder->icc_data()) }; } +static ErrorOr invert_cmyk(LoadedImage& image) +{ + if (!image.bitmap.has>()) + return Error::from_string_view("Can't --invert-cmyk with RGB bitmaps"sv); + auto& frame = image.bitmap.get>(); + + for (auto& pixel : *frame) { + pixel.c = ~pixel.c; + pixel.m = ~pixel.m; + pixel.y = ~pixel.y; + pixel.k = ~pixel.k; + } + return {}; +} + static ErrorOr move_alpha_to_rgb(LoadedImage& image) { if (!image.bitmap.has>()) @@ -173,6 +188,7 @@ struct Options { StringView out_path; bool no_output = false; int frame_index = 0; + bool invert_cmyk = false; bool move_alpha_to_rgb = false; bool strip_alpha = false; StringView assign_color_profile_path; @@ -190,6 +206,7 @@ static ErrorOr parse_options(Main::Arguments arguments) args_parser.add_option(options.out_path, "Path to output image file", "output", 'o', "FILE"); args_parser.add_option(options.no_output, "Do not write output (only useful for benchmarking image decoding)", "no-output", {}); args_parser.add_option(options.frame_index, "Which frame of a multi-frame input image (0-based)", "frame-index", {}, "INDEX"); + args_parser.add_option(options.invert_cmyk, "Invert CMYK channels", "invert-cmyk", {}); args_parser.add_option(options.move_alpha_to_rgb, "Copy alpha channel to rgb, clear alpha", "move-alpha-to-rgb", {}); args_parser.add_option(options.strip_alpha, "Remove alpha channel", "strip-alpha", {}); args_parser.add_option(options.assign_color_profile_path, "Load color profile from file and assign it to output image", "assign-color-profile", {}, "FILE"); @@ -216,6 +233,9 @@ ErrorOr serenity_main(Main::Arguments arguments) LoadedImage image = TRY(load_image(*decoder, options.frame_index)); + if (options.invert_cmyk) + TRY(invert_cmyk(image)); + if (options.move_alpha_to_rgb) TRY(move_alpha_to_rgb(image));