80 findings, not the 14 the first run showed -- clippy stops at the first failing compilation unit, so `--keep-going` is what makes the list complete. 60 were machine-applicable (`cargo clippy --fix`). The rest by hand: * five descending `sort_by` -> `sort_by_key(Reverse(..))` * `chunks_exact(4)` on both sides of four zips, so the compared items stay `[u8; 4]` rather than one array against one slice * three `type` aliases for the census maps and the captured-quad tuple * `&PathBuf` -> `&Path` in two disc tests * two range loops; one of them keeps `#[allow(needless_range_loop)]` with the reason -- the index is into a map's value, which changes each iteration * the module doc list in `invert_capture` re-indented to markdown's rules * `blit`'s eight arguments get `#[allow(too_many_arguments)]`, not a struct One dead `let off = b.len();` in a `ratc` test is dropped rather than renamed. The sibling test at :162 is the one that asserts an offset; if this one was meant to as well, that is a test change and not a lint fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
126 lines
4.9 KiB
Rust
126 lines
4.9 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, None, Some(&with));
|
|
let c = ui_layout::compose_with_order(&b, &by, opts, None, Some(&without));
|
|
let n = a
|
|
.rgba
|
|
.as_chunks::<4>()
|
|
.0
|
|
.iter()
|
|
.zip(c.rgba.as_chunks::<4>().0.iter())
|
|
.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
|
|
.as_chunks::<4>()
|
|
.0
|
|
.iter()
|
|
.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}");
|
|
}
|