Files
Sylpheed/crates/sylpheed-formats/examples/frame_alpha_census.rs
MechaCat02 ccd49ac31f fix(lint): clear the clippy gate across examples and tests
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>
2026-09-12 16:42:41 +02:00

84 lines
3.2 KiB
Rust

//! The main menu's sprites, by their ALPHA channel — is `ptframe1`/`ptframe2`'s
//! "no fully-opaque pixel" a property of the artwork, and does any alpha value
//! look like a scale the game expands (e.g. 0..128) rather than 0..255?
//!
//! The port measures both frames as rendering too DARK against the capture, with
//! the shortfall correlating with the BACKGROUND. Two different causes predict
//! that: a background-scaling blend selected in code, or an alpha that is too
//! LOW in our decode. This example tests the second, which is on the disc.
//!
//! cargo run -p sylpheed-formats --example frame_alpha_census
use std::path::PathBuf;
use sylpheed_formats::{pak::PakArchive, t8ad, ui_layout};
fn census(name: &str, img: &t8ad::T8adImage) {
let n = (img.width * img.height) as usize;
let mut hist = [0usize; 256];
for p in 0..n {
hist[img.rgba[p * 4 + 3] as usize] += 1;
}
let zero = hist[0];
let full = hist[255];
let max = (0..256).rev().find(|&a| hist[a] > 0).unwrap_or(0);
let nonzero = n - zero;
// the top five alpha values that actually occur, by population
let mut top: Vec<(usize, usize)> = (1..256)
.map(|a| (hist[a], a))
.filter(|&(c, _)| c > 0)
.collect();
top.sort_unstable_by_key(|a| std::cmp::Reverse(a.0));
let top5: Vec<String> = top
.iter()
.take(5)
.map(|&(c, a)| format!("{a}x{c}"))
.collect();
println!(
"{name:<16} {}x{:<4} px={n:<8} a=0:{:5.1}% a=255:{:5.1}% max={max:<3} \
partial(1..254)/nonzero={:5.1}% top:[{}]",
img.width,
img.height,
100.0 * zero as f64 / n as f64,
100.0 * full as f64 / n as f64,
if nonzero > 0 {
100.0 * (nonzero - full) as f64 / nonzero as f64
} else {
0.0
},
top5.join(" ")
);
}
fn main() {
let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC"));
let argv: Vec<String> = std::env::args().skip(1).collect();
let pak = argv
.iter()
.find(|a| a.parse::<usize>().is_err())
.cloned()
.unwrap_or_else(|| "GP_TITLE".to_string());
let ar = PakArchive::open(root.join(format!("dat/{pak}.pak"))).expect("pak");
// Builds default to the two the port ships and can be overridden, so the
// same census serves the title (4) and the `PRESS (A)` plate (2).
let args: Vec<usize> = argv.iter().filter_map(|a| a.parse().ok()).collect();
let builds: Vec<usize> = if args.is_empty() { vec![5, 6] } else { args };
for build in builds {
let Ok(by) = ar.read(&ar.entries()[build]) else {
continue;
};
let Some(b) = ui_layout::parse_build(&by) else {
continue;
};
println!("=== {pak} build {build} ===");
let mut names: Vec<&String> = b.sprites.keys().collect();
names.sort();
for n in names {
let (off, size) = b.sprites[n];
let s = &by[off..(off + size).min(by.len())];
match t8ad::parse(s) {
Some(img) => census(n, &img),
None => println!("{n:<16} (not a T8aD / failed to parse)"),
}
}
}
}