use super::deflate;
use super::render::Image;
pub(crate) fn encode(image: &Image) -> Vec<u8> {
let mut raw = Vec::with_capacity(image.height * (1 + image.width * 4));
for y in 0..image.height {
raw.push(0);
for x in 0..image.width {
match image.pixels[y * image.width + x] {
Some((r, g, b)) => raw.extend_from_slice(&[r, g, b, 255]),
None => raw.extend_from_slice(&[0, 0, 0, 0]),
}
}
}
let mut png = Vec::new();
png.extend_from_slice(&[0x89, b'P', b'N', b'G', 0x0D, 0x0A, 0x1A, 0x0A]);
let mut ihdr = Vec::with_capacity(13);
ihdr.extend_from_slice(&(image.width as u32).to_be_bytes());
ihdr.extend_from_slice(&(image.height as u32).to_be_bytes());
ihdr.extend_from_slice(&[8, 6, 0, 0, 0]);
chunk(&mut png, b"IHDR", &ihdr);
chunk(&mut png, b"IDAT", &deflate::zlib_compress(&raw));
chunk(&mut png, b"IEND", &[]);
png
}
fn chunk(png: &mut Vec<u8>, kind: &[u8; 4], data: &[u8]) {
png.extend_from_slice(&(data.len() as u32).to_be_bytes());
png.extend_from_slice(kind);
png.extend_from_slice(data);
let crc = crc32(kind.iter().chain(data).copied());
png.extend_from_slice(&crc.to_be_bytes());
}
const CRC_TABLE: [u32; 256] = crc_table();
const fn crc_table() -> [u32; 256] {
let mut table = [0u32; 256];
let mut n = 0;
while n < 256 {
let mut c = n as u32;
let mut k = 0;
while k < 8 {
c = if c & 1 != 0 {
0xEDB8_8320 ^ (c >> 1)
} else {
c >> 1
};
k += 1;
}
table[n] = c;
n += 1;
}
table
}
fn crc32(bytes: impl Iterator<Item = u8>) -> u32 {
let mut crc = 0xFFFF_FFFFu32;
for byte in bytes {
crc = CRC_TABLE[((crc ^ u32::from(byte)) & 0xFF) as usize] ^ (crc >> 8);
}
crc ^ 0xFFFF_FFFF
}
#[cfg(test)]
#[path = "tests/png_tests.rs"]
mod tests;