main
Caleb Add the Rust source, flow definition, assets and install scripts cf9809b
//! Overlays drawn onto evidence images, so every measurement can be checked by eye.
use image::{Rgb, RgbImage};

pub fn px(img: &mut RgbImage, x: i64, y: i64, c: [u8; 3]) {
    if x >= 0 && y >= 0 && (x as u32) < img.width() && (y as u32) < img.height() { img.put_pixel(x as u32, y as u32, Rgb(c)); }
}

pub fn line(img: &mut RgbImage, a: (f64, f64), b: (f64, f64), c: [u8; 3], th: i64) {
    let n = ((b.0 - a.0).abs().max((b.1 - a.1).abs()) as i64).max(1);
    for i in 0..=n {
        let t = i as f64 / n as f64;
        let (x, y) = ((a.0 + (b.0 - a.0) * t) as i64, (a.1 + (b.1 - a.1) * t) as i64);
        for dy in -th / 2..=th / 2 { for dx in -th / 2..=th / 2 { px(img, x + dx, y + dy, c); } }
    }
}

pub fn poly(img: &mut RgbImage, p: &[(f64, f64)], c: [u8; 3], th: i64) {
    for i in 0..p.len() { line(img, p[i], p[(i + 1) % p.len()], c, th); }
}

pub fn circle(img: &mut RgbImage, ctr: (f64, f64), r: f64, c: [u8; 3], th: i64) {
    let n = ((r * 6.3) as usize).max(16);
    let pts: Vec<(f64, f64)> = (0..n).map(|i| { let a = i as f64 / n as f64 * std::f64::consts::TAU; (ctr.0 + r * a.cos(), ctr.1 + r * a.sin()) }).collect();
    poly(img, &pts, c, th);
}

/// Tint the pixels of a mask.
pub fn tint(img: &mut RgbImage, m: &crate::raster::Mask, c: [u8; 3], a: f32) {
    for (i, p) in img.pixels_mut().enumerate() {
        if m.d[i] { for k in 0..3 { p[k] = (p[k] as f32 * (1.0 - a) + c[k] as f32 * a) as u8; } }
    }
}

/// A tiny 3x5 digit font for labels (ids are letters + digits; letters map to a block).
const DIG: [[u8; 5]; 10] = [[7, 5, 5, 5, 7], [2, 6, 2, 2, 7], [7, 1, 7, 4, 7], [7, 1, 7, 1, 7], [5, 5, 7, 1, 1], [7, 4, 7, 1, 7], [7, 4, 7, 5, 7], [7, 1, 1, 1, 1], [7, 5, 7, 5, 7], [7, 5, 7, 1, 7]];

pub fn label(img: &mut RgbImage, at: (f64, f64), text: &str, c: [u8; 3], scale: i64) {
    let (mut x, y) = (at.0 as i64, at.1 as i64);
    for ch in text.chars() {
        let g = ch.to_digit(10).map(|d| DIG[d as usize]).unwrap_or([7, 7, 7, 7, 7]);
        if ch.is_ascii_digit() || ch.is_ascii_alphabetic() {
            for (r, bits) in g.iter().enumerate() { for col in 0..3 {
                if bits >> (2 - col) & 1 == 1 && (ch.is_ascii_digit() || r == 0 || r == 4 || col == 0) {
                    for dy in 0..scale { for dx in 0..scale { px(img, x + col * scale + dx, y + r as i64 * scale + dy, c); } }
                }
            } }
        }
        x += 4 * scale;
    }
}