Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

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}");
    }
}