`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
27 lines
1.2 KiB
Rust
27 lines
1.2 KiB
Rust
// Leave-one-out over the elements that touch a band, dumping raw RGBA each time.
|
|
use sylpheed_formats::{pak, ui_layout};
|
|
fn main(){
|
|
let mut a=std::env::args().skip(1);
|
|
let pk=a.next().unwrap();
|
|
let bi:usize=a.next().unwrap().parse().unwrap();
|
|
let dir=a.next().unwrap();
|
|
let ar=pak::PakArchive::open(pk).unwrap();
|
|
let by=ar.read(&ar.entries()[bi]).unwrap();
|
|
let b=ui_layout::parse_build(&by).unwrap();
|
|
let o=ui_layout::ComposeOptions{ backdrop:[0,0,0,255], ..Default::default() };
|
|
let n=b.elements.len();
|
|
let base=ui_layout::compose(&b,&by,o.clone(),None);
|
|
std::fs::write(format!("{dir}/base.raw"),&base.rgba).unwrap();
|
|
println!("canvas {}x{} elements {n}", base.width, base.height);
|
|
for i in 0..n {
|
|
let e=&b.elements[i];
|
|
let Some(kf)=e.rest() else { continue };
|
|
// only bother with elements whose rest pose can touch the band
|
|
let mut v=vec![true;n]; v[i]=false;
|
|
let c=ui_layout::compose(&b,&by,o.clone(),Some(&v));
|
|
std::fs::write(format!("{dir}/wo-{i}.raw"),&c.rgba).unwrap();
|
|
println!("{i}\t{}\t{}\t({},{})\ta={}", e.name,
|
|
e.sprite.clone().unwrap_or_default(), kf.x, kf.y, kf.fade>>24);
|
|
}
|
|
}
|