use crate::constants::{COVER_HEIGHT, COVER_LEN, COVER_WIDTH};
use crate::error::PinkError;
const PNG_SIGNATURE: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
fn crc32(data: &[u8]) -> u32 {
let mut crc = 0xFFFF_FFFFu32;
for &byte in data {
crc ^= byte as u32;
for _ in 0..8 {
crc = if crc & 1 != 0 {
(crc >> 1) ^ 0xEDB8_8320
} else {
crc >> 1
};
}
}
!crc
}
fn adler32(data: &[u8]) -> u32 {
let mut a = 1u32;
let mut b = 0u32;
for &byte in data {
a = (a + byte as u32) % 65521;
b = (b + a) % 65521;
}
(b << 16) | a
}
fn write_chunk(out: &mut Vec<u8>, chunk_type: &[u8; 4], data: &[u8]) {
out.extend_from_slice(&(data.len() as u32).to_be_bytes());
out.extend_from_slice(chunk_type);
out.extend_from_slice(data);
let mut crc_data = chunk_type.to_vec();
crc_data.extend_from_slice(data);
out.extend_from_slice(&crc32(&crc_data).to_be_bytes());
}
pub fn encode_png(rgba: &[u8]) -> Vec<u8> {
debug_assert_eq!(rgba.len(), COVER_LEN);
let mut out = Vec::new();
out.extend_from_slice(&PNG_SIGNATURE);
let mut ihdr = Vec::with_capacity(13);
ihdr.extend_from_slice(&(COVER_WIDTH as u32).to_be_bytes());
ihdr.extend_from_slice(&(COVER_HEIGHT as u32).to_be_bytes());
ihdr.push(8); ihdr.push(6); ihdr.push(0); ihdr.push(0); ihdr.push(0); write_chunk(&mut out, b"IHDR", &ihdr);
let mut raw = Vec::with_capacity(COVER_HEIGHT * (1 + COVER_WIDTH * 4));
for y in 0..COVER_HEIGHT {
raw.push(0); let start = y * COVER_WIDTH * 4;
raw.extend_from_slice(&rgba[start..start + COVER_WIDTH * 4]);
}
let mut zlib = vec![0x78, 0x01]; let mut pos = 0;
while pos < raw.len() {
let remaining = raw.len() - pos;
let block_size = remaining.min(65535);
let is_last = pos + block_size >= raw.len();
zlib.push(if is_last { 1 } else { 0 });
zlib.extend_from_slice(&(block_size as u16).to_le_bytes());
zlib.extend_from_slice(&(!(block_size as u16)).to_le_bytes());
zlib.extend_from_slice(&raw[pos..pos + block_size]);
pos += block_size;
}
zlib.extend_from_slice(&adler32(&raw).to_be_bytes());
write_chunk(&mut out, b"IDAT", &zlib);
write_chunk(&mut out, b"IEND", &[]);
out
}
pub fn find_png_end(data: &[u8]) -> Result<usize, PinkError> {
if data.len() < 8 || data[0..8] != PNG_SIGNATURE {
return Err(PinkError::InvalidFormat);
}
let mut pos = 8;
while pos + 12 <= data.len() {
let len =
u32::from_be_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]) as usize;
let chunk_type = &data[pos + 4..pos + 8];
let chunk_end = pos + 12 + len;
if chunk_end > data.len() {
return Err(PinkError::InvalidFormat);
}
if chunk_type == b"IEND" {
return Ok(chunk_end);
}
pos = chunk_end;
}
Err(PinkError::InvalidFormat)
}
pub fn encode_pnk(frame: &[u8]) -> Vec<u8> {
let cover = &frame[crate::constants::HEADER_LEN..crate::constants::HEADER_LEN + COVER_LEN];
let mut out = encode_png(cover);
out.extend_from_slice(frame);
out
}
pub fn decode_pnk(data: &[u8]) -> Result<&[u8], PinkError> {
let png_end = find_png_end(data)?;
if png_end >= data.len() {
return Err(PinkError::InvalidFormat);
}
Ok(&data[png_end..])
}