use rusty_jpeg::encode::{ColorType, Encoder, SamplingFactor};
fn fbm(x: usize, y: usize, amplitude: f32) -> f32 {
fn hash(xi: i32, yi: i32) -> f32 {
let mut h = (xi as u32).wrapping_mul(0x9E37_79B9) ^ (yi as u32).wrapping_mul(0x85EB_CA6B);
h ^= h >> 15;
h = h.wrapping_mul(0x2545_F491);
h ^= h >> 13;
(h >> 8) as f32 / 8_388_608.0 - 1.0
}
fn value_noise(fx: f32, fy: f32) -> f32 {
let (x0, y0) = (fx.floor() as i32, fy.floor() as i32);
let (tx, ty) = (fx - x0 as f32, fy - y0 as f32);
let (sx, sy) = (tx * tx * (3.0 - 2.0 * tx), ty * ty * (3.0 - 2.0 * ty));
let a = hash(x0, y0) + (hash(x0 + 1, y0) - hash(x0, y0)) * sx;
let b = hash(x0, y0 + 1) + (hash(x0 + 1, y0 + 1) - hash(x0, y0 + 1)) * sx;
a + (b - a) * sy
}
let (mut sum, mut amp, mut freq) = (0.0, amplitude, 1.0 / 64.0);
for _ in 0..6 {
sum += amp * value_noise(x as f32 * freq, y as f32 * freq);
amp *= 0.5;
freq *= 2.0;
}
sum
}
fn main() {
let mut args = std::env::args().skip(1);
let out = args
.next()
.expect("usage: make_fixture <out.jpg> [w] [h] [q]");
let w: usize = args.next().and_then(|v| v.parse().ok()).unwrap_or(1920);
let h: usize = args.next().and_then(|v| v.parse().ok()).unwrap_or(1080);
let quality: u8 = args.next().and_then(|v| v.parse().ok()).unwrap_or(90);
let interleaved: bool = args.next().map(|v| v == "interleaved").unwrap_or(false);
let sampling_arg = args.next().unwrap_or_else(|| "420".into());
let mut rgb = vec![0u8; w * h * 3];
for j in 0..h {
for i in 0..w {
let (fi, fj) = (i as f32 / w as f32, j as f32 / h as f32);
let base = 96.0 + 90.0 * (fi * 2.3 + fj * 1.7).sin() * (fj * 1.9).cos();
let lum = (base + fbm(i, j, 70.0)).clamp(0.0, 255.0);
let o = (j * w + i) * 3;
rgb[o] = (lum + 26.0 * (fi * 3.1).sin()).clamp(0.0, 255.0) as u8;
rgb[o + 1] = lum as u8;
rgb[o + 2] = (lum + 22.0 * (fj * 2.7).cos()).clamp(0.0, 255.0) as u8;
}
}
let mut jpeg = Vec::new();
let mut enc = Encoder::new(&mut jpeg, quality);
enc.set_sampling_factor(match sampling_arg.as_str() {
"444" => SamplingFactor::R_4_4_4,
"422" => SamplingFactor::R_4_2_2,
_ => SamplingFactor::R_4_2_0,
});
enc.set_optimized_huffman_tables(true);
if std::env::var("RUSTY_JPEG_TRELLIS").is_ok() {
enc.set_trellis(true);
}
if interleaved {
enc.set_streaming_optimize(true);
}
enc.encode(&rgb, w as u16, h as u16, ColorType::Rgb)
.expect("encode");
let ratio = (w * h * 3 / 2) as f64 / jpeg.len() as f64;
std::fs::write(&out, &jpeg).expect("write");
println!(
"{out}: {w}x{h} q{quality}, {} B, {ratio:.1}x compression",
jpeg.len()
);
}