Skip to content

Commit

Permalink
imgcmp: Add a --write-diff-image option
Browse files Browse the repository at this point in the history
This writes a bitmap that has pixels that are identical between
the two input images blended with 0.5 alpha against white, and
differing pixels are highlighted in red.

Use like so:

    imgcmp --write-diff-image diff.webp image1.png image2.webp
  • Loading branch information
nico committed Jan 10, 2025
1 parent 5863954 commit 95fc058
Showing 1 changed file with 46 additions and 0 deletions.
46 changes: 46 additions & 0 deletions Userland/Utilities/imgcmp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include <LibCore/MappedFile.h>
#include <LibCore/MimeData.h>
#include <LibGfx/ImageFormats/ImageDecoder.h>
#include <LibGfx/ImageFormats/PNGWriter.h>
#include <LibGfx/ImageFormats/WebPWriter.h>

static ErrorOr<RefPtr<Gfx::Bitmap>> load_image(StringView path)
{
Expand All @@ -20,10 +22,49 @@ static ErrorOr<RefPtr<Gfx::Bitmap>> load_image(StringView path)
return TRY(decoder->frame(0)).image;
}

static ErrorOr<void> save_image(NonnullRefPtr<Gfx::Bitmap> bitmap, StringView out_path)
{
if (!out_path.ends_with(".png"sv, CaseSensitivity::CaseInsensitive) && !out_path.ends_with(".webp"sv, CaseSensitivity::CaseInsensitive))
return Error::from_string_view("can only save to .png and .webp files"sv);

auto output_stream = TRY(Core::File::open(out_path, Core::File::OpenMode::Write));
auto buffered_stream = TRY(Core::OutputBufferedFile::create(move(output_stream)));

if (out_path.ends_with(".png"sv, CaseSensitivity::CaseInsensitive))
return Gfx::PNGWriter::encode(*buffered_stream, *bitmap);

VERIFY(out_path.ends_with(".webp"sv, CaseSensitivity::CaseInsensitive));
return Gfx::WebPWriter::encode(*buffered_stream, *bitmap);
}

static ErrorOr<NonnullRefPtr<Gfx::Bitmap>> make_diff_image(NonnullRefPtr<Gfx::Bitmap> first_image, NonnullRefPtr<Gfx::Bitmap> second_image)
{
VERIFY(first_image->size() == second_image->size());

auto diff_image = TRY(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, first_image->size()));

for (int y = 0; y < first_image->height(); ++y) {
for (int x = 0; x < first_image->width(); ++x) {
auto first_pixel = first_image->get_pixel<Gfx::StorageFormat::BGRA8888>(x, y);
auto second_pixel = second_image->get_pixel<Gfx::StorageFormat::BGRA8888>(x, y);
if (first_pixel == second_pixel) {
diff_image->set_pixel(x, y, first_pixel.interpolate(Gfx::Color::White, 0.5f));
} else {
diff_image->set_pixel(x, y, Gfx::Color::Red);
}
}
}

return diff_image;
}

ErrorOr<int> serenity_main(Main::Arguments arguments)
{
Core::ArgsParser args_parser;

StringView write_diff_image_path;
args_parser.add_option(write_diff_image_path, "Write image that highlights differing pixels", "write-diff-image", {}, "FILE");

StringView first_image_path;
args_parser.add_positional_argument(first_image_path, "Path to first input image", "FILE1");

Expand All @@ -40,6 +81,11 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
return 1;
}

if (!write_diff_image_path.is_empty()) {
auto diff_image = TRY(make_diff_image(*first_image, *second_image));
TRY(save_image(diff_image, write_diff_image_path));
}

for (int y = 0; y < first_image->physical_height(); ++y) {
for (int x = 0; x < first_image->physical_width(); ++x) {
auto first_pixel = first_image->get_pixel(x, y);
Expand Down

0 comments on commit 95fc058

Please sign in to comment.