`Element::rest()` picks each element's last hold keyframe independently of every other element, so a composite built from it is not the screen at any moment in time -- it is a per-element maximum. For a transient that is exactly wrong: a two-frame flash's last hold IS the flash peak, so it burns forever. GP_TITLE build 4 is the case. `ptlogo_back2eff1`..`eff5` are five staggered two-frame flashes -- one light sweep drawn as five frames, all extinguished by t110 -- that `rest()` draws simultaneously and permanently. Five stacked white glows saturate the light arc behind the logo. The disc names the right instant: the midpoint of the longest interval containing no keyframe of any element. `UiBuild::settle_time()` and `settle_window()`; `screen render --settle` applies it and prints the window, whose width is how much the midpoint is worth. Predicted t=198 from [160,236] BEFORE scoring. Against the console capture, the arc band goes 33.22 -> 11.79 and pixels at the clipping level 8581 -> 1452, where the console has 1459 -- an unfitted statistic. Whole frame 14.07 -> 12.06. Controls at t=100 and t=358 are far worse, and a hand-picked visibility list reaches the identical numbers. `ComposeOptions::at` now poses every element rather than leaves only, which is why the earlier rotation pose scan was flat: it moved the sweeps and never touched the top-level flashes. `at = None` is byte-identical (cmp), the pre-rotation tag renders identically at rest, and the 13 paint-order tests plus the keyframe/focus/opt-link disc tests are green. Also fixes the diagnostic that caused a wrong finding to be sent to the port agent: `not drawn` listed bare names, and a kind-0x4 ghost carries its template's name, so four ghosts printed as `ptlogo1.t32`/`ptlogo2.t32` and read as "the logo is missing". It now prints index, name and reason. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd
32 lines
1.6 KiB
Rust
32 lines
1.6 KiB
Rust
// The settled screen, computed from the keyframe times alone.
|
|
//
|
|
// `rest()` picks each element's last HOLD keyframe independently, which is right
|
|
// for an element that ends settled and wrong for a transient: a 2-frame flash
|
|
// holds at its PEAK, so `rest()` leaves it burning forever. The settled screen is
|
|
// instead one INSTANT that every element is posed at, and the instant to pick is
|
|
// inside the longest interval during which no element has a keyframe at all.
|
|
use sylpheed_formats::{pak, ui_layout};
|
|
fn main(){
|
|
let mut a=std::env::args().skip(1);
|
|
let pk=a.next().unwrap();
|
|
let ar=pak::PakArchive::open(&pk).unwrap();
|
|
let only:Option<usize>=a.next().and_then(|s|s.parse().ok());
|
|
for (i,e) in ar.entries().iter().enumerate() {
|
|
if let Some(o)=only { if o!=i { continue } }
|
|
let Ok(by)=ar.read(e) else { continue };
|
|
let Some(b)=ui_layout::parse_build(&by) else { continue };
|
|
if b.elements.len()<2 { continue }
|
|
let mut ts:Vec<u32>=b.elements.iter().flat_map(|el|
|
|
el.keyframes.iter().filter_map(|k|k.time)).collect();
|
|
if ts.len()<2 { continue }
|
|
ts.sort_unstable(); ts.dedup();
|
|
// longest gap between consecutive keyframe times
|
|
let (mut best,mut lo,mut hi)=(0u32,0u32,0u32);
|
|
for w in ts.windows(2) {
|
|
if w[1]-w[0] > best { best=w[1]-w[0]; lo=w[0]; hi=w[1]; }
|
|
}
|
|
println!("{:>4} {:<28} times {:>3} span {:>4} settle window [{lo},{hi}] = {best} units ({:.2}s) -> t={}",
|
|
i, format!("{:08x}",e.name_hash), ts.len(), ts.last().unwrap(), best as f64/60.0, lo+best/2);
|
|
}
|
|
}
|