render.rs
2.8 kB · rust · 95 lines
1use super::field::Field;2use mrlycore::errors::{value_error, Result};3use mrlycore::ramp::Colorizer;45/// Quantizes a field into colored levels and encodes PNG bytes, or an error at scale zero.6pub fn render(7 field: &Field,8 colorizer: &Colorizer,9 levels: usize,10 symmetric: bool,11 invert: bool,12 scale: usize,13) -> Result<Vec<u8>> {14 if scale < 1 {15 return value_error("scale must be at least 1.");16 }17 let levels = levels.max(2);18 let size = field.size;19 let norm = field.normalized(symmetric);20 let max_val = levels - 1;21 let mut rgba = vec![[0u8; 4]; size * size];22 for (i, &v) in norm.iter().enumerate() {23 let t = if invert { 1.0 - v } else { v };24 let bucket = ((t * max_val as f32).round() as usize).min(max_val);25 let c = colorizer.color(bucket + 1, levels);26 rgba[i] = [c.r, c.g, c.b, 255];27 }28 mrlycore::io::png(&rgba, size, size, scale)29}3031#[cfg(test)]32mod tests {33 use super::*;34 use crate::moire::{stack, Combine, Lattice, Spec};35 #[test]36 fn renders_png_bytes() {37 let f = stack(38 Spec::new(7, 2, 2),39 &[1, 3, 5],40 Combine::Sum,41 1,42 Lattice::Square,43 32,44 &[],45 )46 .unwrap();47 let png = render(&f, &Colorizer::fire(), 64, false, false, 2).unwrap();48 assert_eq!(&png[1..4], b"PNG");49 }50 #[test]51 fn png_pixels_stay_pinned() {52 let f = stack(53 Spec::new(7, 2, 2),54 &[1, 3, 5],55 Combine::Sum,56 1,57 Lattice::Square,58 32,59 &[],60 )61 .unwrap();62 let cases = [63 (64 render(&f, &Colorizer::fire(), 64, false, false, 2).unwrap(),65 64,66 3_252_192,67 [255, 229, 145, 255],68 [212, 73, 33, 255],69 ),70 (71 render(&f, &Colorizer::heat(), 32, true, true, 3).unwrap(),72 96,73 9_073_080,74 [255, 255, 255, 255],75 [214, 214, 214, 255],76 ),77 (78 render(&f, &Colorizer::diverge(), 8, true, false, 1).unwrap(),79 32,80 684_184,81 [255, 61, 64, 255],82 [255, 119, 121, 255],83 ),84 ];85 for (bytes, side, sum, corner, centre) in &cases {86 let (w, h, pixels) = mrlycore::unpng(bytes).unwrap();87 assert_eq!((w, h), (*side, *side));88 let bytes: u64 = pixels.iter().flatten().map(|&b| u64::from(b)).sum();89 assert_eq!(bytes, *sum);90 assert_eq!(pixels[0], *corner);91 assert_eq!(pixels[side * side - 1], *corner);92 assert_eq!(pixels[(side / 2) * side + side / 2], *centre);93 }94 }95}