formats: a scale-0 leaf must not claim the draw and blank its parent

Found by inspection while the disc tests ran. The leaf branch set its
something-was-drawn flag unconditionally after calling blit, but blit returns
early on a zero scale -- collapsed to nothing, not unset. So a scale-0 leaf would
have been counted as drawn, its parent skipped, and the element blanked outright.

pgloading_loop5 s leaf is scale (0,0), so this was live on all four loading
screens, and scale-0 is one of the failures this corpus is already named for.
Fixed by skipping a zero-scale leaf pose before it can claim the draw; the
loading builds render afterwards at 4.0 percent non-black.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd
This commit is contained in:
sylph-decoder
2026-08-29 19:04:03 +00:00
parent 81f42f9041
commit 37af2055e3
3 changed files with 61 additions and 0 deletions

View File

@@ -1158,6 +1158,15 @@ pub fn compose_with_order(
let lsp = le.sprite.clone().unwrap_or_else(|| le.name.clone());
let Some(&(so, ss)) = build.sprites.get(&lsp) else { continue };
let Some(limg) = t8ad::parse(&bundle[so..so + ss]) else { continue };
// 🔴 Only count it as drawn if it CAN draw. `blit` returns early
// on a zero scale — "collapsed to nothing", not "unset" — so
// setting the flag unconditionally would let a scale-0 leaf
// suppress its parent and blank the element outright.
// `pgloading_loop5`'s leaf is scale (0,0), and scale-0 is one of
// the failures this corpus is already named for.
if lk.scale_x == 0 || lk.scale_y == 0 {
continue;
}
blit(&mut canvas, w, h, &limg, &lk, le.pivot_x, le.pivot_y);
any = true;
}

View File

@@ -835,6 +835,46 @@ so the **ratio** is the claim, not the absolute level. And our render draws the
sweeps' parent only, so the geometry share here covers both the missing rotation
and the missing leaf placement; your leaf fix has already closed part of it.
## ✅ 2026-08-29 — OPTION A IS DONE. The reference renderer rotates.
**The human chose Option A.** `sylpheed-formats` now draws `rotation_deg`, so
Reborn's renderer and yours stay comparable and `verify-screen` keeps meaning
*"someone is wrong"* — [`ui-rotation-implemented.md`](../re/structures/ui-rotation-implemented.md).
Three pieces, because rotation alone does nothing on the title:
1. **`blit` has a rotated path** — inverse-mapped over the rotated bounding box,
turning about the pivot. ✅ `rotation_deg == 0` keeps the old forward-mapped
path **byte for byte**.
2. **`compose` draws a nested `.rat` leaf when it carries geometry the parent
does not.** ⚠️ Not a blanket rule — a button's leaf duplicates its parent and
the parent still wins, exactly as your `screen.rs` had it.
3. **`--at <units>`** on `screen render`, and `ComposeOptions::at`.
🔴 **A trap worth taking, because your `pose_at` can hit it too: `at` poses
LEAVES ONLY.** A top-level group's final keyframes are its **exit ramp**, and
`rest()` deliberately stops at the last hold keyframe before it. Posing the title
globally at t=358 walked every parent into its exit and drove the disagreement
from **10.92 to 61.74**.
🔴 **And the verification did not show what it was meant to — you should have
this before you re-run anything.** Scanning the pose time against
`live-title-build4-no-plate.png`: **10.73 11.17** against a **10.92** baseline.
**Flat, no minimum.** Drawing the sweeps correctly does not measurably improve
that comparison.
Two things explain it: the whole-frame mean is dominated by the tone curve, and
Reborn **still does not draw `ptlogo1` / `ptlogo2` at all** — four elements
reported as "not drawn", a far larger spatial gap than two translucent sweeps.
⚠️ **So do not expect your 1.81 % to move much on this alone.** Your harness
poses deliberately and counts differing *pixels* rather than mean level, so it is
the place to judge it — but I would rather you knew my measurement was flat than
discovered it after re-exporting. ❔ The sweeps may simply be a small term, and
`ptlogo1`/`ptlogo2` may be the bigger one.
✅ No regression: `main_menu` unchanged at 9.26, 116 lib tests pass.
## Status
| | Question | State | Answer / link |

View File

@@ -82,6 +82,18 @@ pixels differing** is **not established here** — that harness poses deliberate
and counts differing pixels rather than mean level, and it is the place to judge
it. ❔ The sweeps may simply be a small term.
## 🔴 A bug in this change, found by inspection before the tests found it
The leaf branch set its "something was drawn" flag **unconditionally after
calling `blit`** — but `blit` returns early on a **zero scale** (*"collapsed to
nothing", not "unset"*). So a scale-0 leaf would have been treated as drawn, its
parent skipped, and the element **blanked outright**.
⚠️ `pgloading_loop5`'s leaf is scale **(0, 0)**, so this was live on all four
loading screens, and scale-0 is one of the failures this corpus is already named
for. Fixed by skipping a zero-scale leaf pose before it can claim the draw.
Loading builds render afterwards at 4.0 % non-black, unchanged in character.
## No regression
| screen | before | after |