pub fn write_png_header(
buffer: &mut Vec<u8>,
width: u32,
height: u32,
color_depth: BitDepth,
color_mode: ColorType,
) -> Range<usize> {
let before = buffer.len();
buffer.extend_from_slice(b"\x89PNG\r\n\x1A\n");
write_png_chunk(buffer, b"IHDR", &{
let mut data = Vec::new();
data.extend_from_slice(&u32::from(width).to_be_bytes());
data.extend_from_slice(&u32::from(height).to_be_bytes());
data.extend_from_slice(&u8::from(color_depth).to_be_bytes());
data.extend_from_slice(&u8::from(color_mode).to_be_bytes());
data.extend_from_slice(&u8::from(0_u8).to_be_bytes());
data.extend_from_slice(&u8::from(0_u8).to_be_bytes());
data.extend_from_slice(&u8::from(0_u8).to_be_bytes());
data
});
let after = buffer.len();
before..after
}
pub fn write_png_palette(buffer: &mut Vec<u8>, palette: &[u8]) -> Range<usize> {
write_png_chunk(buffer, b"PLTE", palette)
}
pub fn write_png_body(buffer: &mut Vec<u8>, data: &[u8]) -> Range<usize> {
let mut deflated = Vec::new();
write_non_deflated(&mut deflated, data);
write_png_chunk(buffer, b"IDAT", &deflated)
}
pub fn write_non_deflated(buffer: &mut Vec<u8>, data: &[u8]) -> Range<usize> {
let chunks = data.chunks(0xFFFF);
let cmf = 0b_0111_1000;
buffer.push(cmf);
let mut flg: u8 = 0b_00_0_00000;
flg |= 0b_11111 - ((((cmf as u16) << 8) | (flg as u16)) % 0b_11111) as u8;
buffer.push(flg);
let mut before = None;
let mut after = None;
let count = chunks.len();
for (index, chunk) in chunks.enumerate() {
let is_last_chunk = index + 1 >= count;
buffer.push(is_last_chunk.into());
buffer.extend_from_slice(&u16::try_from(chunk.len()).unwrap().to_le_bytes());
buffer.extend_from_slice(&u16::try_from(chunk.len()).unwrap().not().to_le_bytes());
before.get_or_insert(buffer.len());
buffer.extend_from_slice(chunk);
after = Some(buffer.len());
}
buffer.extend_from_slice(&adler32(data).to_le_bytes());
let after = after.unwrap_or(buffer.len());
let before = before.unwrap_or(after);
before..after
}
pub fn write_non_png_chunk(buffer: &mut Vec<u8>, data: &[u8]) -> Range<usize> {
write_png_chunk(buffer, b"pkPK", data)
}
pub fn write_png_footer(buffer: &mut Vec<u8>) -> Range<usize> {
write_png_chunk(buffer, b"IEND", b"")
}
pub fn write_png_chunk(buffer: &mut Vec<u8>, chunk_type: &[u8; 4], data: &[u8]) -> Range<usize> {
let before = buffer.len();
buffer.extend_from_slice(
&u32::try_from(data.len())
.expect("png chunk larger than 2GiB")
.to_be_bytes(),
);
buffer.extend_from_slice(chunk_type);
buffer.extend_from_slice(data);
buffer.extend_from_slice(
&crc32(
&[chunk_type.as_slice(), data]
.into_iter()
.flatten()
.copied()
.collect::<Vec<_>>(),
)
.to_be_bytes(),
);
let after = buffer.len();
before..after
}
pub fn write_png(
buffer: &mut Vec<u8>,
data: &[u8],
width: u32,
height: u32,
bit_depth: BitDepth,
color_mode: ColorType,
palette: Option<&[u8]>,
) {
write_png_header(buffer, width, height, bit_depth, color_mode);
if let Some(palette) = palette {
write_png_palette(buffer, palette);
}
let mut filtered_data = Vec::new();
let bits_per_pixel = bit_depth.bits_per_sample() * color_mode.samples_per_pixel();
let bits_per_line = width * bits_per_pixel as u32;
let bytes_per_line = (bits_per_line + 7) / 8;
for (i, byte) in data.iter().enumerate() {
if i % (bytes_per_line as usize) == 0 {
filtered_data.push(0x00);
}
filtered_data.push(*byte);
}
write_png_body(buffer, &filtered_data);
write_png_footer(buffer);
}