Files
ddidderr ce9373aa7a feat: visual parity with the original Ghostty (pixel-diff pipeline)
Adds scripts/visual-parity.sh (`just visual-parity`): launches the
installed Ghostty 1.3.1 and the Rust build in an isolated headless
nested weston (GL renderer, Mesa forced, weston-screenshooter via
--debug), with identical isolated config, a deterministic test page
(ASCII, ligatures, all SGR styles and underline kinds, 16/256/truecolor,
box/block/braille/powerline/nerd icons, CJK, emoji, wide chars,
cursor) and a tabs+splits scenario driven through Xwayland, then diffs
the captures (terminal area and chrome separately, heatmaps).

Fixes found by comparing against the original:
- A pane stayed blank after a nested split: the GTK render path called
  draw_frame(false) where Ghostty uses drawFrame(true) (a re-realized
  GLArea has a new swap chain that nothing else redraws).
- Sprite glyph antialiasing now ports z2d's default 4x4 multisample
  rasterizer instead of tiny-skia's analytic coverage (pixels differing
  from Ghostty's reference sprite sheets: 5.76% -> 1.20%, none off by
  more than 96 levels); the sheet test tolerance is tightened to 0.1%.

Result vs Ghostty 1.3.1: test page 0.020% of pixels differ (196 px at
curly-underline ends and diagonal box strokes, max delta 86 levels);
tabs+splits and headerbar/tab bar are bit-identical. Details in
docs/rewrite/notes/visual-parity-VP.md.

Test Plan:
- just visual-parity (needs weston, weston-screenshooter, xdotool)
- cargo test -p ghostty-font (sprite sheet comparison)
2026-10-12 13:52:22 +02:00

12 lines
563 B
Python

#!/usr/bin/env python3
"""zoom.py DIR x,y,w,h scale OUT : side-by-side ref | rust | diff crop, upscaled (nearest)."""
import sys
from PIL import Image
d, box, sc, out = sys.argv[1], [int(v) for v in sys.argv[2].split(",")], int(sys.argv[3]), sys.argv[4]
x, y, w, h = box
ims = [Image.open(f"{d}/{n}.png").convert("RGB").crop((x, y, x + w, y + h)).resize((w * sc, h * sc), Image.NEAREST) for n in ("ref", "rust", "diff")]
o = Image.new("RGB", (w * sc, h * sc * 3 + 8), (255, 0, 255))
for i, im in enumerate(ims):
o.paste(im, (0, i * (h * sc + 4)))
o.save(out)