fn crc32(data: &[u8], mut crc: u32) -> u32 {
for &b in data {
crc ^= b as u32;
for _ in 0..8 {
crc = if crc & 1 != 0 {
0xEDB8_8320 ^ (crc >> 1)
} else {
crc >> 1
};
}
}
crc
}
fn chunk(out: &mut Vec<u8>, kind: &[u8; 4], data: &[u8]) {
out.extend_from_slice(&(data.len() as u32).to_be_bytes());
out.extend_from_slice(kind);
out.extend_from_slice(data);
let c = crc32(data, crc32(kind, 0xFFFF_FFFF)) ^ 0xFFFF_FFFF;
out.extend_from_slice(&c.to_be_bytes());
}
pub fn encode_rgba(width: u32, height: u32, rgba: &[u8]) -> Vec<u8> {
let mut raw = Vec::with_capacity((width as usize * 4 + 1) * height as usize);
for row in rgba.chunks_exact(width as usize * 4).take(height as usize) {
raw.push(0);
raw.extend_from_slice(row);
}
let mut z = vec![0x78, 0x01];
let mut blocks = raw.chunks(65535).peekable();
if blocks.peek().is_none() {
z.extend_from_slice(&[1, 0, 0, 0xFF, 0xFF]);
}
while let Some(b) = blocks.next() {
z.push(u8::from(blocks.peek().is_none()));
let n = b.len() as u16;
z.extend_from_slice(&n.to_le_bytes());
z.extend_from_slice(&(!n).to_le_bytes());
z.extend_from_slice(b);
}
let (mut a, mut bb) = (1u32, 0u32);
for &x in &raw {
a = (a + x as u32) % 65521;
bb = (bb + a) % 65521;
}
z.extend_from_slice(&((bb << 16) | a).to_be_bytes());
let mut out = b"\x89PNG\r\n\x1a\n".to_vec();
let mut ihdr = Vec::with_capacity(13);
ihdr.extend_from_slice(&width.to_be_bytes());
ihdr.extend_from_slice(&height.to_be_bytes());
ihdr.extend_from_slice(&[8, 6, 0, 0, 0]);
chunk(&mut out, b"IHDR", &ihdr);
chunk(&mut out, b"IDAT", &z);
chunk(&mut out, b"IEND", &[]);
out
}
impl crate::crt::FrameView<'_> {
pub fn to_png(&self) -> Vec<u8> {
encode_rgba(self.width, self.height, self.rgba)
}
}