re(ui): scale 0 means collapsed, not "unset" -- stop drawing it full size

blit() and fill_quad() both opened with

    let sx_pct = if kf.scale_x == 0 { 100 } else { kf.scale_x };

so an element whose pose is collapsed to nothing rendered at FULL SIZE.
I first described this as "a 1-pixel sliver", reading the .max(1) in the
size arithmetic two lines below -- the guard above it meant .max(1) never
saw a zero. Read the whole function, not the lines you went looking for.

Control run before the change: if 0 meant "unset", something on the disc
would use it throughout. Nothing does.

  elements with a keyframe group                    15 493
  at least one keyframe at scale 0                   2 166
  EVERY keyframe at scale 0                              0
  zero is a transient the element grows out of       1 762

ptlogo_eff3.t32 runs 0% -> 200%. An "unset" marker no element ever uses
throughout is not a marker.

Both functions now return without drawing when either scale is 0.

Reach: 24 of 24 renders byte-identical across GP_TITLE (all 16 builds),
GP_PAUSE_MENU and GP_OPTIONS -- additive on every screen the port needs.
195 elements have a guessed rest pose at scale 0 and 126 of those have a
non-zero alpha, so the old code painted them; all 126 are in
GP_READY_ROOM.pak, which S1 already declared a no-go. They are
tactical-map sprites at sx=0/sy=100, mid-horizontal-wipe, that the
coercion drew full-width.

So: a real correctness fix with no visible effect on the menu port.
Suite green, 122 passed / 0 failed across 3 suites at commit time.
This commit is contained in:
Sylpheed RE agent
2026-08-28 23:38:37 +00:00
parent c2c69b02be
commit b973d8cde3
7 changed files with 187 additions and 4 deletions

View File

@@ -982,8 +982,11 @@ fn fill_quad(
if sw == 0 || sh == 0 {
return false;
}
let sx_pct = if kf.scale_x == 0 { 100 } else { kf.scale_x };
let sy_pct = if kf.scale_y == 0 { 100 } else { kf.scale_y };
// `scale = 0` is COLLAPSED TO NOTHING, not "unset" — see `blit`.
let (sx_pct, sy_pct) = (kf.scale_x, kf.scale_y);
if sx_pct == 0 || sy_pct == 0 {
return false;
}
let dw = (sw * sx_pct / 100).max(1);
let dh = (sh * sy_pct / 100).max(1);
let ox = kf.x - (pivot_x as i32 * (sx_pct as i32 - 100)) / 100;
@@ -1040,8 +1043,16 @@ fn blit(
if sw == 0 || sh == 0 {
return;
}
let sx_pct = if kf.scale_x == 0 { 100 } else { kf.scale_x };
let sy_pct = if kf.scale_y == 0 { 100 } else { kf.scale_y };
// `scale = 0` means COLLAPSED TO NOTHING, not "unset". This used to coerce
// 0 → 100 %, which drew a fully-collapsed element at full size. The disc
// settles it: of 15 493 elements with a keyframe group, **2 166 have at
// least one zero-scale keyframe and not one has zero on every keyframe** —
// and 1 762 of them grow back out of it (`ptlogo_eff3.t32` runs 0 % → 200 %).
// An "unset" marker that no element ever uses throughout is not a marker.
let (sx_pct, sy_pct) = (kf.scale_x, kf.scale_y);
if sx_pct == 0 || sy_pct == 0 {
return;
}
let dw = (sw * sx_pct / 100).max(1);
let dh = (sh * sy_pct / 100).max(1);
// Keep the pivot point fixed as the element scales.