Files
Sylpheed/crates/sylpheed-formats/examples/forced_backdrop_pixel_cost.rs
sylph-decoder aa9b7ef340 re: the forced-backdrop rule's pixel cost -- 38 screens go black without it
Follows the necessity census. 'The order moves' is a property of the sort; the
tie-break work already found reorders costing zero pixels, so the picture
moving is a separate claim. Rendered each of the 62 deciding builds twice and
diffed.

  38 .prm deciders: changed_px == ink_px in ALL 38. Without the rule the
                    primitive sorts last, paints over everything, and the
                    screen composites to pure black. The port's original
                    contradiction argument, measured on 38 builds across seven
                    archives instead of argued on two.
  24 .tbm deciders: zero -- and that is MY INSTRUMENT, not a finding.

The control asked whether the composite had ink; it always does. The question
was whether the reordered ELEMENT has ink, and compose draws no pixels at all
for a .tbm. So those 24 zeros measure our renderer's blindness by construction.
tie_break_pixel_cost.rs already had the per-element ink_mask this needed.
Reported rather than quietly patched: a control that cannot fail is the shape
this corpus keeps paying for.

Also corrects two things the port agent caught:

  - 'Two renderers, same answer' was true of the six GP_TITLE instances and not
    of the other 74. The port's re-run of my probe is my code executed twice;
    its independent leg was removing its own exporter post-pass, which covers
    GP_TITLE only. The disc-wide 62 has one witness and the page now says so.
  - forced_backdrop_necessity.rs defaulted to GP_TITLE with no argument, so a
    bare run printed 6 instances in the same format as 80. It now walks every
    dat/*.pak and reports the archive count.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-30 07:04:05 +00:00

120 lines
4.8 KiB
Rust

//! What does `forced_backdrop` cost IN PIXELS on the screens it decides?
//!
//! `forced_backdrop_necessity.rs` answers "does the derived ORDER move", which is
//! a property of the sort. The port agent then pointed out — correctly — that its
//! re-run of that probe was **my code executed twice**, not a second witness, so
//! the disc-wide 62 has one measurement behind it and only `GP_TITLE` has two.
//!
//! This does not fix that (it is still this crate), but it moves the question to a
//! **different layer**: render each deciding build twice, once in the order
//! `compose` derives and once with the `forced_backdrop` fallback removed, and
//! count the pixels that differ. "The order moved" and "the picture moved" are not
//! the same claim, and the second is the one anybody cares about — the tie-break
//! work already found overlapping reorders that cost exactly zero pixels.
//!
//! Each entry carries its own CONTROL: the pixel count of the composite itself.
//! If a build renders empty, its zero means the instrument saw nothing, not that
//! the rule is free.
//!
//! cargo run -p sylpheed-formats --example forced_backdrop_pixel_cost -- [pak...]
//!
//! With no argument it walks **every `dat/*.pak`** — the necessity probe defaulted
//! to `GP_TITLE`, which made a bare run report a thirteenth of the census and read
//! like the whole thing.
use std::path::PathBuf;
use sylpheed_formats::{pak::PakArchive, ui_layout};
use ui_layout::ComposeOptions;
fn order_without_rule(build: &ui_layout::UiBuild, bundle: &[u8]) -> Vec<usize> {
let mut idx: Vec<usize> = (0..build.elements.len()).collect();
idx.sort_by_key(|&i| {
let el = &build.elements[i];
(
ui_layout::sprite_layer_key(build, bundle, el)
.or_else(|| ui_layout::implied_layer_key(&el.name))
.unwrap_or(u32::MAX),
i,
)
});
idx
}
fn main() {
let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC"));
let mut paks: Vec<PathBuf> = std::env::args().skip(1).map(PathBuf::from).collect();
if paks.is_empty() {
let mut all: Vec<PathBuf> = std::fs::read_dir(root.join("dat"))
.expect("dat/")
.filter_map(|e| e.ok().map(|e| e.path()))
.filter(|p| p.extension().is_some_and(|x| x == "pak"))
.collect();
all.sort();
paks = all;
}
eprintln!("# scanning {} archive(s)", paks.len());
let opts = ComposeOptions {
include_primitives: true,
backdrop: [0, 0, 0, 255],
..Default::default()
};
println!("# archive entry element changed_px total_px ink_px(control) pct");
let (mut decided, mut zero_cost, mut blind) = (0usize, 0usize, 0usize);
for pak in &paks {
let Ok(ar) = PakArchive::open(pak) else { continue };
let name = pak.file_name().unwrap().to_string_lossy().to_string();
for (i, e) in ar.entries().iter().enumerate() {
let Ok(by) = ar.read(e) else { continue };
let Some(b) = ui_layout::parse_build(&by) else {
continue;
};
let with = ui_layout::derived_paint_order(&b, &by);
let without = order_without_rule(&b, &by);
if with == without {
continue;
}
let forced: Vec<&str> = b
.elements
.iter()
.filter(|el| ui_layout::forced_backdrop(&b, el))
.map(|el| el.name.as_str())
.collect();
let a = ui_layout::compose_with_order(&b, &by, opts.clone(), None, Some(&with));
let c = ui_layout::compose_with_order(&b, &by, opts.clone(), None, Some(&without));
let n = a
.rgba
.chunks_exact(4)
.zip(c.rgba.chunks_exact(4))
.filter(|(x, y)| x != y)
.count();
// Control: does this build put any ink down at all, against the bare
// backdrop? A build that renders to nothing cannot show a reorder.
let ink = a
.rgba
.chunks_exact(4)
.filter(|p| p[..3] != [0, 0, 0])
.count();
let total = a.rgba.len() / 4;
decided += 1;
if ink == 0 {
blind += 1;
} else if n == 0 {
zero_cost += 1;
}
println!(
" {name} {i} {} {n} {total} {ink} {:.2}%",
forced.join(","),
100.0 * n as f64 / total as f64
);
}
}
println!("\n# builds whose ORDER the rule decides: {decided}");
println!("# of those, costing ZERO pixels: {zero_cost}");
println!("# of those, BLIND (build renders no ink, control fails): {blind}");
}