ferravex_graphics/
image.rs1use crate::Framebuffer;
2impl Framebuffer {
3 pub fn to_ppm(&self) -> Vec<u8> {
5 let mut output = format!("P6\n{} {}\n255\n", self.width, self.height).into_bytes();
6 output.reserve(self.pixels().len() * 3);
7 for pixel in self.pixels() {
8 output.extend_from_slice(&pixel[..3]);
9 }
10 output
11 }
12 pub fn to_png(&self) -> Vec<u8> {
15 let mut filtered = Vec::with_capacity(self.pixels().len() * 4 + self.height as usize);
16 for row in self.pixels().chunks_exact(self.width as usize) {
17 filtered.push(0);
18 for pixel in row {
19 filtered.extend_from_slice(pixel);
20 }
21 }
22 let mut zlib = vec![0x78, 0x01];
23 let block_count = filtered.len().div_ceil(65535);
24 for (index, block) in filtered.chunks(65535).enumerate() {
25 zlib.push(u8::from(index + 1 == block_count));
26 let length = block.len() as u16;
27 zlib.extend_from_slice(&length.to_le_bytes());
28 zlib.extend_from_slice(&(!length).to_le_bytes());
29 zlib.extend_from_slice(block);
30 }
31 zlib.extend_from_slice(&adler32(&filtered).to_be_bytes());
32 let mut output = b"\x89PNG\r\n\x1a\n".to_vec();
33 let mut header = Vec::with_capacity(13);
34 header.extend_from_slice(&self.width.to_be_bytes());
35 header.extend_from_slice(&self.height.to_be_bytes());
36 header.extend([8, 6, 0, 0, 0]);
37 chunk(&mut output, b"IHDR", &header);
38 chunk(&mut output, b"IDAT", &zlib);
39 chunk(&mut output, b"IEND", &[]);
40 output
41 }
42 #[cfg(not(target_arch = "wasm32"))]
43 pub fn save_png(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
44 std::fs::write(path, self.to_png())
45 }
46 #[cfg(not(target_arch = "wasm32"))]
47 pub fn save_ppm(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
48 std::fs::write(path, self.to_ppm())
49 }
50}
51fn adler32(bytes: &[u8]) -> u32 {
52 let mut a = 1_u32;
53 let mut b = 0_u32;
54 for byte in bytes {
55 a = (a + u32::from(*byte)) % 65521;
56 b = (b + a) % 65521;
57 }
58 (b << 16) | a
59}
60fn crc32(bytes: impl Iterator<Item = u8>) -> u32 {
61 let mut crc = !0_u32;
62 for byte in bytes {
63 crc ^= u32::from(byte);
64 for _ in 0..8 {
65 crc = (crc >> 1) ^ if crc & 1 != 0 { 0xedb88320 } else { 0 };
66 }
67 }
68 !crc
69}
70fn chunk(output: &mut Vec<u8>, kind: &[u8; 4], bytes: &[u8]) {
71 output.extend_from_slice(&(bytes.len() as u32).to_be_bytes());
72 output.extend_from_slice(kind);
73 output.extend_from_slice(bytes);
74 output.extend_from_slice(&crc32(kind.iter().chain(bytes).copied()).to_be_bytes());
75}
76#[cfg(test)]
77mod tests {
78 use super::*;
79 #[test]
80 fn checksum_known_vectors() {
81 assert_eq!(crc32(b"123456789".iter().copied()), 0xcbf43926);
82 assert_eq!(adler32(b"Wikipedia"), 0x11e60398);
83 }
84}