feat(image): add image diff command (ImageMagick compare equivalent) #18

Merged
David merged 1 commit from feat/image-diff into main 2026-05-14 13:17:06 +02:00
Owner

New subcommand: monkey image diff <A> <B> <OUT>. Pixel-level comparison modelled after ImageMagick compare / pixelmatch.

The flag surface is two orthogonal axes:

--bg <transparent|white|black|a|b>     [default: black]
--highlight <rgb|red|green|blue|a|b>   [default: rgb]
--threshold <0.0-1.0>                  [default: 0.0]
  • --bg fills pixels where A and B match. a / b overlay the diff on top of one of the originals; transparent produces RGBA output.
  • --highlight paints pixels where A and B differ. rgb emits per-channel |A - B| (the classic IM difference look); the colour names paint a solid block; a / b pass through the original pixel from either side.
  • --threshold is per-channel: a pixel counts as a match when max(|dr|, |dg|, |db|, |da|) <= threshold * 255.

Validation

  • Mismatched dimensions error before any pixel work.
  • --bg transparent requires a .png output; non-PNG sinks fail fast.
  • Every other combination renders opaque RGB and accepts whatever image::save writes.

Tests

Five unit tests in src/image/diff.rs cover dimension mismatch, transparent-+-non-PNG rejection, matched-pixel background, highlight painting, and threshold suppression (44 total tests passing, 2 ignored).

End-to-end smoke test against magick-generated 60x40 inputs:

  • image diff a.png b.png out.png -> rgb mode, black bg, changed pixel reads (223,223,159) (≈ white minus the #202060 background that disappeared).
  • image diff --bg a --highlight red a.png b.png out.png -> changed pixel is pure red (255,0,0).
  • image diff --bg transparent --highlight red ... out.png -> matched pixels have alpha 0.
  • --bg transparent ... out.jpg -> errors with "requires a .png output".
  • Mismatched sizes (60x40 vs 60x41) -> errors with "dimensions differ".

Local cargo fmt --check, cargo clippy --all-targets -- -D warnings, cargo build --all-targets, cargo test all pass.

Test plan

  • Confirm Forgejo Check workflow passes for this branch.
  • Run image diff against two screenshots (one with a small UI change) and visually confirm the diff highlights the change.
  • Run image diff --bg a --highlight red old.png new.png overlay.png and confirm the result looks like ImageMagick compare.
New subcommand: `monkey image diff <A> <B> <OUT>`. Pixel-level comparison modelled after ImageMagick `compare` / `pixelmatch`. The flag surface is two orthogonal axes: ``` --bg <transparent|white|black|a|b> [default: black] --highlight <rgb|red|green|blue|a|b> [default: rgb] --threshold <0.0-1.0> [default: 0.0] ``` - `--bg` fills pixels where A and B match. `a` / `b` overlay the diff on top of one of the originals; `transparent` produces RGBA output. - `--highlight` paints pixels where A and B differ. `rgb` emits per-channel `|A - B|` (the classic IM difference look); the colour names paint a solid block; `a` / `b` pass through the original pixel from either side. - `--threshold` is per-channel: a pixel counts as a match when `max(|dr|, |dg|, |db|, |da|) <= threshold * 255`. ## Validation - Mismatched dimensions error before any pixel work. - `--bg transparent` requires a `.png` output; non-PNG sinks fail fast. - Every other combination renders opaque RGB and accepts whatever `image::save` writes. ## Tests Five unit tests in `src/image/diff.rs` cover dimension mismatch, transparent-+-non-PNG rejection, matched-pixel background, highlight painting, and threshold suppression (44 total tests passing, 2 ignored). End-to-end smoke test against magick-generated 60x40 inputs: - `image diff a.png b.png out.png` -> rgb mode, black bg, changed pixel reads `(223,223,159)` (≈ white minus the `#202060` background that disappeared). - `image diff --bg a --highlight red a.png b.png out.png` -> changed pixel is pure red `(255,0,0)`. - `image diff --bg transparent --highlight red ... out.png` -> matched pixels have alpha 0. - `--bg transparent ... out.jpg` -> errors with "requires a .png output". - Mismatched sizes (60x40 vs 60x41) -> errors with "dimensions differ". Local `cargo fmt --check`, `cargo clippy --all-targets -- -D warnings`, `cargo build --all-targets`, `cargo test` all pass. ## Test plan - [ ] Confirm Forgejo `Check` workflow passes for this branch. - [ ] Run `image diff` against two screenshots (one with a small UI change) and visually confirm the diff highlights the change. - [ ] Run `image diff --bg a --highlight red old.png new.png overlay.png` and confirm the result looks like ImageMagick `compare`.
feat(image): add image diff command (ImageMagick compare equivalent)
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 18s
Create release / Create release from merged PR (pull_request) Has been skipped
d573e4a119
Compute a per-pixel difference between two images. Modelled on two orthogonal axes: `--bg <transparent|white|black|a|b>` controls what fills pixels where A and B match, `--highlight <rgb|red|green|blue|a|b>` controls what fills pixels where they differ. The `rgb` highlight emits per-channel `|A - B|`, matching ImageMagick `compare` in difference mode; the colour highlights (`red`, `green`, `blue`) and `a` / `b` overlays cover the common "paint the diff on top of the original" workflow that `pixelmatch` and `compare` default to. `--threshold` is a 0.0-1.0 per-channel tolerance: pixels whose max channel delta is at or below `threshold * 255` count as a match, defaulting to `0.0` so any difference shows. Inputs must share dimensions; mismatched sizes error out before any pixel work. `--bg transparent` produces RGBA output and requires a `.png` sink; every other combination emits opaque RGB and writes through whatever `image::save` supports. Five unit tests cover dimension mismatch, transparent + non-PNG rejection, matched-pixel background, highlight painting, and threshold suppression. End-to-end smoke test against a magick-generated input pair confirmed the rgb / red / transparent paths produce the expected pixel values and that the two error cases (size mismatch, transparent + .jpg) fail cleanly.
David merged commit ec52263387 into main 2026-05-14 13:17:06 +02:00
David deleted branch feat/image-diff 2026-05-14 13:17:06 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
pandoras-box/monkey!18
No description provided.