//! An OPAQUE full-screen primitive cannot paint on top of elements that are //! visible at the same time — the screen would be blank. //! //! That is a constraint read off the file, not a preference. For each keyless //! primitive this computes the interval over which it is opaque, and the interval //! over which any OTHER element is visible, and reports the overlap. //! //! The falsifier: `pteff00.prm` is MEASURED painting last on the title and the //! main menu. If any of its instances is opaque while content is up, the //! constraint is wrong and this whole line is dead. use sylpheed_formats::{pak, ratc, ui_layout}; /// Interval(s) where alpha >= `thr`, sampled at every half unit. fn opaque_span(el: &ui_layout::Element, thr: u32, tmax: u32) -> Vec<(f64, f64)> { let mut out = Vec::new(); let mut cur: Option = None; let mut t = 0.0; while t <= tmax as f64 { let a = el.pose_at(t as u32).map(|k| k.fade >> 24).unwrap_or(0); if a >= thr { if cur.is_none() { cur = Some(t) } } else if let Some(s) = cur.take() { out.push((s, t)); } t += 0.5; } if let Some(s) = cur { out.push((s, tmax as f64)) } out } fn main() { let root = std::env::var("SYLPHEED_DISC").expect("set SYLPHEED_DISC to the extracted disc root"); let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")) .expect("dat/") .flatten() .map(|e| e.path()) .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")) .collect(); paks.sort(); let want = [ "pteff00.prm", "pgloading_eff00.prm", "palogo_eff0.prm", "pfbase.tbm", "pteff02.prm", "pzeff00.prm", "pceff00.prm", "pdeff00.prm", ]; println!( "{:>22} {:>5} {:>16} {:>18} overlap", "primitive", "entry", "opaque while", "content visible" ); for p in &paks { let Ok(ar) = pak::PakArchive::open(p) else { continue; }; for (ei, e) in ar.entries().iter().enumerate() { let Ok(by) = ar.read(e) else { continue }; if !ratc::is_ratc(&by) { continue; } let Some(b) = ui_layout::parse_build(&by) else { continue; }; let tmax = b .elements .iter() .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) .max() .unwrap_or(0); if tmax == 0 { continue; } for el in &b.elements { if !want.contains(&el.name.as_str()) { continue; } if ui_layout::sprite_layer_key(&b, &by, el).is_some() { continue; } let op = opaque_span(el, 255, tmax); if op.is_empty() { continue; } // when is any OTHER element visible? let mut cmin = f64::MAX; let mut cmax = f64::MIN; for o in &b.elements { if o.index == el.index { continue; } for (s, t) in opaque_span(o, 1, tmax) { cmin = cmin.min(s); cmax = cmax.max(t) } } if cmin > cmax { continue; } // overlap of the primitive's opaque span with the content span let ov: f64 = op .iter() .map(|&(s, t)| (t.min(cmax) - s.max(cmin)).max(0.0)) .sum(); let opd: String = op .iter() .map(|&(s, t)| format!("{s:.0}-{t:.0}")) .collect::>() .join(","); let flag = if ov > 2.0 { " 🔴 CANNOT BE ON TOP" } else { "" }; println!( "{:>22} {:>5} {:>16} {:>18} {ov:6.1}{flag}", el.name, format!( "{}:{}", p.file_name() .unwrap() .to_string_lossy() .trim_end_matches(".pak") .trim_start_matches("GP_"), ei ), opd, format!("{cmin:.0}-{cmax:.0}") ); } } } }