Skip to content

feat: add unified screenshot diff with layout-shift compensation#6

Open
jsfez wants to merge 2 commits into
mainfrom
feat/unified-screenshot-diff
Open

feat: add unified screenshot diff with layout-shift compensation#6
jsfez wants to merge 2 commits into
mainfrom
feat/unified-screenshot-diff

Conversation

@jsfez

@jsfez jsfez commented Jul 4, 2026

Copy link
Copy Markdown

Summary

Adds diffScreenshots, a single-pass screenshot comparator designed to replace odiff-bin in the Argos backend (companion PR in argos-ci/argos), plus computeRowAlignment exposing the row alignment on its own.

  • Pixel diff in Rust: pixelmatch algorithm (YIQ color distance, alpha blending over white, antialiasing detection). Both Argos thresholds (base 0.15 / color-sensitive 0.0225) are evaluated in a single loop over in-memory images — the previous pipeline spawned odiff twice and decoded each PNG four times.
  • Layout-shift compensation built in: row alignment via patience diff on quantized (5 bits/channel) row hashes; when a trustworthy vertical shift is found (min-run, coverage, segment-count and residual-ratio gates), the base image is realigned by an in-memory row map. Inserted sections are painted solid red on the mask, deletions get a 4px seam marker, and both are reported (insertedRows/deletedRows) so the caller can score them explicitly.
  • Parallelized with rayon: image decode, diff rows and row hashing. Benchmark on Argos' big-images fixture: 66ms vs 200ms for the two parallel odiff spawns it replaces (×3).
  • Masks are only encoded/written when they have content.

odiff parity

Validated against the full Argos fixture corpus (24 cases): the base-threshold pass is bit-identical to odiff on identical inputs; the color-sensitive pass diverges by +0.1–2% relative (OCaml vs Rust float precision at a very low threshold), with identical gate verdicts everywhere.

Tests

  • 12 Rust unit tests (cargo test): alignment (insertion, deletion, repeated rows, min-run folding, width mismatch) and diff (color math, dual threshold, transparent padding, row-map realignment, end-to-end layout shift).
  • 12 vitest tests including 6 new diffScreenshots specs on synthetic PNGs (masks content asserted pixel by pixel) and the pre-existing fingerprint spec, unchanged.

Release note

Version bumped to 0.3.0 — the Argos backend PR depends on this version being published to npm.

🤖 Generated with Claude Code

Add diffScreenshots, a single-pass screenshot comparator replacing
odiff in the Argos pipeline:

- pixelmatch algorithm in Rust (YIQ color distance, alpha blending
  over white, antialiasing detection), both diff thresholds evaluated
  in one loop over in-memory images
- integrated layout-shift compensation: row alignment (patience diff
  on quantized row hashes, exposed separately as computeRowAlignment),
  in-memory row remapping instead of image compositing, inserted
  sections painted on the mask, deletion seam markers
- parallelized with rayon (decode, diff rows, row hashing): x3 faster
  than the two parallel odiff processes it replaces
- masks are only encoded when they have content

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
diff_pass had 9 arguments, tripping clippy::too_many_arguments. Bundle
the canvas geometry and thresholds into a PassConfig struct.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant