holger-rs/xtask/src/logo.rs
//! The one picture on holger.rs's front page, and how it gets there.
//!
//! The source is a 1024x1536 RGBA PNG the owner drew; the page wants a WebP,
//! because that is what the front page's left half is built to carry and
//! because 2.6 MB of PNG on the one page a stranger sees first is 2.6 MB spent
//! before they have read a word.
//!
//! **Pure Rust, both halves.** `image` decodes and resamples; `image-webp`
//! encodes. `cwebp` is not invoked, is not required, and is not installed —
//! the law forbids shelling out where a Rust library exists, and here two do.
//! The `webp` crate on crates.io would also work and is NOT used: it binds
//! Google's C libwebp through FFI, which the law puts last.
//!
//! **Lossless, not lossy.** `image-webp` encodes VP8L only. That is not a
//! compromise accepted for lack of an encoder — it is the right format for
//! this picture: a logo is flat colour and hard edges, which is what VP8L's
//! predictor transform is for, and a lossy pass would put ringing on exactly
//! the edges that make it a logo. The measured sizes are printed by `logo
//! plan`, so the trade is a number and not an opinion.
use std::io::Write as _;
use std::path::{Path, PathBuf};
/// Where the picture ends up, relative to the repository root.
///
/// It sits beside the page that names it, and the page names it ONCE, in a
/// custom property — the same discipline gunnar's login page keeps, so
/// replacing the picture is one line and not a hunt.
pub const PUBLISHED: &str = "holger-front/assets/holger.webp";
/// The width the page is served, and it is MEASURED rather than picked.
///
/// The source is 1024x1536 (portrait, 2:3) and the encoder is lossless, so the
/// width IS the file size. The three that were tried, from the same source:
///
/// ```text
/// 720 x 1080 950 578 bytes 35 % of the PNG
/// 640 x 960 739 018 bytes 27 %
/// 574 x 861 578 646 bytes 21 %
/// ```
///
/// 574 it is — 40 % off the 720 render for a picture that is `background-size:
/// contain` in a half-viewport column, where the extra rows buy nothing a
/// reader can resolve. It is also exactly the width `gunnar_silver.webp` is
/// served at, which is not a coincidence worth breaking: the two fronts in
/// this estate are the same page with different pictures.
///
/// It is NOT upscaled for retina. A 2x copy of a LOSSLESS encode is three
/// quarters of the file again, on the one page somebody looks at for four
/// seconds before they click something.
pub const WIDTH: u32 = 574;
pub struct Converted {
pub source: PathBuf,
pub source_bytes: u64,
pub source_dims: (u32, u32),
pub out: PathBuf,
pub out_bytes: u64,
pub out_dims: (u32, u32),
}
/// Read `source`, resample to [`WIDTH`], encode lossless WebP, write `out`.
///
/// Writes nothing until the whole encode has succeeded: a half-written picture
/// at the published path is a broken page, and it is the kind of broken that
/// looks like a deploy problem for an hour.
pub fn convert(source: &Path, out: &Path) -> Result<Converted, String> {
let source_bytes = std::fs::metadata(source)
.map_err(|e| {
format!(
"the logo source is not readable at {}: {e}\n \
This is the file the owner drew. It is not in the repository \
and is not supposed to be — pass its path with `--from`.",
source.display()
)
})?
.len();
let img = image::open(source).map_err(|e| {
format!(
"{} is not an image this build can read: {e}",
source.display()
)
})?;
let source_dims = (img.width(), img.height());
// Lanczos3, not nearest and not triangle. Downscaling by ~30 % is where a
// cheap filter shows: nearest aliases every diagonal in a logo into a
// staircase, and the staircase is then encoded LOSSLESSLY, so the artefact
// is preserved perfectly forever.
let height = ((source_dims.1 as u64 * WIDTH as u64) / source_dims.0 as u64) as u32;
let scaled = img.resize_exact(WIDTH, height, image::imageops::FilterType::Lanczos3);
let rgba = scaled.to_rgba8();
let mut buf: Vec<u8> = Vec::new();
image_webp::WebPEncoder::new(&mut buf)
.encode(rgba.as_raw(), WIDTH, height, image_webp::ColorType::Rgba8)
.map_err(|e| format!("the WebP encode failed: {e}"))?;
// A self-check before anything is written: the encoder answered Ok, and
// what it produced really is a RIFF/WEBP container. Cheap, and it turns a
// silent format change in a dependency into a refusal here rather than a
// broken left half on the front page.
if !is_webp(&buf) {
return Err("the encoder produced bytes that are not a WebP container".to_string());
}
if let Some(dir) = out.parent() {
std::fs::create_dir_all(dir)
.map_err(|e| format!("could not make {}: {e}", dir.display()))?;
}
let mut f = std::fs::File::create(out)
.map_err(|e| format!("could not write {}: {e}", out.display()))?;
f.write_all(&buf)
.map_err(|e| format!("could not write {}: {e}", out.display()))?;
f.sync_all()
.map_err(|e| format!("could not flush {}: {e}", out.display()))?;
Ok(Converted {
source: source.to_path_buf(),
source_bytes,
source_dims,
out: out.to_path_buf(),
out_bytes: buf.len() as u64,
out_dims: (WIDTH, height),
})
}
/// The WebP magic, so a test can assert the bytes are a WebP and not merely a
/// file that got written. `RIFF....WEBP`.
pub fn is_webp(bytes: &[u8]) -> bool {
bytes.len() > 12 && &bytes[0..4] == b"RIFF" && &bytes[8..12] == b"WEBP"
}
#[cfg(test)]
mod tests {
use super::*;
/// The encoder is the pure-Rust one, and what it produces is a real WebP.
/// A round trip on a tiny synthetic image, so the test costs nothing and
/// needs no asset on disk.
#[test]
fn the_rust_encoder_produces_a_real_webp() {
let w = 8u32;
let h = 6u32;
let px = vec![0x2bu8; (w * h * 4) as usize];
let mut buf = Vec::new();
image_webp::WebPEncoder::new(&mut buf)
.encode(&px, w, h, image_webp::ColorType::Rgba8)
.expect("the pure-Rust VP8L encoder");
assert!(is_webp(&buf), "what came out was not a WebP");
}
/// **The aspect ratio survives.** The source is portrait 2:3 and the left
/// half of the page is a portrait column; a conversion that quietly
/// squashed it would still produce a valid WebP and a wrong page.
#[test]
fn the_published_height_follows_the_source_ratio() {
for (sw, sh) in [(1024u64, 1536u64), (574, 1024), (900, 900)] {
let h = ((sh * WIDTH as u64) / sw) as u32;
let got = h as f64 / WIDTH as f64;
let want = sh as f64 / sw as f64;
assert!(
(got - want).abs() < 0.01,
"{sw}x{sh} came out at {got}, not {want}"
);
}
}
/// The published path is named once, and it is under the front's own
/// assets — not loose at the repository root and not beside the xtask that
/// happens to write it.
#[test]
fn the_picture_is_published_into_the_fronts_assets() {
assert!(PUBLISHED.starts_with("holger-front/assets/"), "{PUBLISHED}");
assert!(PUBLISHED.ends_with(".webp"), "{PUBLISHED}");
}
}