1pub struct Pfm {
2 pub width: u32,
3 pub height: u32,
4 pub pixels: Vec<[f32; 4]>,
5}
6
7pub fn read(bytes: &[u8]) -> Result<Pfm, String> {
8 let mut fields = Vec::new();
9 let mut at = 0;
10 while fields.len() < 4 {
11 while at < bytes.len() && bytes[at].is_ascii_whitespace() {
12 at += 1;
13 }
14 let start = at;
15 while at < bytes.len() && !bytes[at].is_ascii_whitespace() {
16 at += 1;
17 }
18 if start == at {
19 return Err("invalid PFM image".into());
20 }
21 fields.push(std::str::from_utf8(&bytes[start..at]).map_err(|_| "invalid PFM image")?);
22 }
23 at += 1;
24 let channels = match fields[0] {
25 "PF" => 3,
26 "Pf" => 1,
27 _ => return Err("invalid PFM image".into()),
28 };
29 let parse = |field: &str| field.parse::<u32>().map_err(|_| "invalid PFM image");
30 let width = parse(fields[1])?;
31 let height = parse(fields[2])?;
32 let scale: f32 = fields[3].parse().map_err(|_| "invalid PFM image")?;
33 if scale >= 0.0 {
34 return Err("big-endian PFM images are not supported".into());
35 }
36 let count = width as usize * height as usize * channels;
37 let data = bytes
38 .get(at..at + count * 4)
39 .ok_or("invalid PFM image: error reading pixel data")?;
40 let values: Vec<f32> = data
41 .chunks_exact(4)
42 .map(|value| f32::from_le_bytes(value.try_into().unwrap()) * scale.abs())
43 .collect();
44 let mut pixels = vec![[0.0, 0.0, 0.0, 1.0]; width as usize * height as usize];
45 for y in 0..height as usize {
46 let row = height as usize - 1 - y;
47 for x in 0..width as usize {
48 let source = &values[(y * width as usize + x) * channels..][..channels];
49 let pixel = &mut pixels[row * width as usize + x];
50 for c in 0..3 {
51 pixel[c] = source[c.min(channels - 1)];
52 }
53 }
54 }
55 Ok(Pfm {
56 width,
57 height,
58 pixels,
59 })
60}
61
62pub fn write(width: u32, height: u32, pixels: &[[f32; 4]]) -> Vec<u8> {
63 let mut bytes = format!("PF\n{width} {height}\n-1.0\n").into_bytes();
64 for y in (0..height as usize).rev() {
65 for pixel in &pixels[y * width as usize..][..width as usize] {
66 for value in &pixel[..3] {
67 bytes.extend_from_slice(&value.to_le_bytes());
68 }
69 }
70 }
71 bytes
72}