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
120 lines
4.8 KiB
Rust
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}");
|
|
}
|