fit_mode='fit' only guaranteed the row-justified block filled canvas
width, never upscaling to also fill height when the natural block was
shorter than the canvas (e.g. 2 refs left a top/bottom bar). Add
fit_mode='cover': same row-justified layout, but when the natural block
underfills the canvas it scales up to fill height and center-crops the
resulting width overflow instead, and when it overfills height it crops
that overflow directly -- either way every pixel of the canvas is
covered, cropping only the minimum shared/symmetric amount.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replace per-panel independent letterboxing with a row-justified layout:
every row fills the canvas width exactly (each image scaled uniformly,
no distortion), rows are only shrunk together (never upscaled past
natural fill-width size) if they'd collectively overflow the canvas
height. Net effect: at most one shared pair of side/top-bottom bars
instead of padding scattered inside every individual panel.
Ported the identical algorithm into build_multiref_sheet.py (the
training-side script) so inference-time sheets match training-time
sheets pixel-for-pixel when fit_mode='fit' is used -- verified with a
byte-for-byte comparison test. fit_mode='crop' (default) is unchanged.
crop (default, unchanged): zoom+center-crop to fill each panel, matches
the training data. fit: scale each reference to fit entirely inside its
panel with a single uniform scale factor (no distortion), letterboxed
with background padding for any leftover space -- keeps every pixel
of references with extreme aspect ratios instead of cropping them.
Same grid convention as the multi-ref LTX-2 LoRA training pipeline
(build_multiref_sheet.py): deterministic layout by ref count (1x1, 2x1,
3x1, 2x2, 3-top+2-bottom), 512x512 panels, centered onto a fixed
1536x1024 canvas regardless of how many refs are plugged in. Panel
order matches the image0/image1/... indexing used in training.