pub fn encode_rle(pixels: &[u8], width: u16, height: u16) -> Option<Vec<u8>> {
let w = width as usize;
let h = height as usize;
let total = w * h;
if pixels.len() != total {
return None;
}
if total == 0 {
return Some(Vec::new());
}
let mut buf = Vec::new();
for row in pixels.chunks(w) {
let mut col = 0;
while col < row.len() {
let color = row[col];
let mut run = 1usize;
while col + run < row.len() && row[col + run] == color {
run += 1;
}
let mut remaining = run;
while remaining > 0 {
let chunk = remaining.min(16383);
remaining -= chunk;
if color == 0 {
if chunk <= 63 {
buf.push(0x00);
buf.push(chunk as u8);
} else {
buf.push(0x00);
buf.push(0x40 | ((chunk >> 8) as u8));
buf.push((chunk & 0xFF) as u8);
}
} else {
if chunk == 1 {
buf.push(color);
} else if chunk == 2 {
buf.push(color);
buf.push(color);
} else if chunk <= 63 {
buf.push(0x00);
buf.push(0x80 | (chunk as u8));
buf.push(color);
} else {
buf.push(0x00);
buf.push(0xC0 | ((chunk >> 8) as u8));
buf.push((chunk & 0xFF) as u8);
buf.push(color);
}
}
}
col += run;
}
buf.push(0x00);
buf.push(0x00);
}
Some(buf)
}
pub fn decode_rle(rle_data: &[u8], width: u16, height: u16) -> Option<Vec<u8>> {
let w = width as usize;
let h = height as usize;
let total = w * h;
if total == 0 {
return Some(Vec::new());
}
let mut pixels = Vec::with_capacity(total);
let mut i = 0;
let len = rle_data.len();
let mut col = 0;
while i < len && pixels.len() < total {
let byte = rle_data[i];
i += 1;
if byte != 0x00 {
pixels.push(byte);
col += 1;
} else {
if i >= len {
break;
}
let flag = rle_data[i];
i += 1;
if flag == 0x00 {
let remaining_row = w.saturating_sub(col);
let room = total - pixels.len();
let run = remaining_row.min(room);
pixels.resize(pixels.len() + run, 0);
col = 0;
} else {
let top2 = flag & 0xC0;
let (run, color) = match top2 {
0x00 => {
((flag & 0x3F) as usize, 0u8)
}
0x40 => {
if i >= len {
return None;
}
let run = (((flag & 0x3F) as usize) << 8) | (rle_data[i] as usize);
i += 1;
(run, 0u8)
}
0x80 => {
if i >= len {
return None;
}
let run = (flag & 0x3F) as usize;
let color = rle_data[i];
i += 1;
(run, color)
}
0xC0 => {
if i + 1 >= len {
return None;
}
let run = (((flag & 0x3F) as usize) << 8) | (rle_data[i] as usize);
let color = rle_data[i + 1];
i += 2;
(run, color)
}
_ => unreachable!(),
};
let room = total - pixels.len();
let run = run.min(room);
pixels.resize(pixels.len() + run, color);
col += run;
}
}
}
if pixels.len() < total {
pixels.resize(total, 0);
}
if pixels.len() == total {
Some(pixels)
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_image() {
let result = decode_rle(&[], 0, 0);
assert_eq!(result, Some(vec![]));
}
#[test]
fn single_pixel_nonzero() {
let data = [0x05];
let result = decode_rle(&data, 1, 1).unwrap();
assert_eq!(result, vec![5]);
}
#[test]
fn single_pixel_color_zero_via_run() {
let data = [0x00, 0x01];
let result = decode_rle(&data, 1, 1).unwrap();
assert_eq!(result, vec![0]);
}
#[test]
fn short_color_zero_run() {
let data = [0x00, 0x03];
let result = decode_rle(&data, 3, 1).unwrap();
assert_eq!(result, vec![0, 0, 0]);
}
#[test]
fn long_color_zero_run() {
let data = [0x00, 0x41, 0x00];
let result = decode_rle(&data, 256, 1).unwrap();
assert_eq!(result.len(), 256);
assert!(result.iter().all(|&p| p == 0));
}
#[test]
fn short_color_c_run() {
let data = [0x00, 0x83, 0xFF];
let result = decode_rle(&data, 3, 1).unwrap();
assert_eq!(result, vec![255, 255, 255]);
}
#[test]
fn long_color_c_run() {
let data = [0x00, 0xC1, 0x00, 0x07];
let result = decode_rle(&data, 256, 1).unwrap();
assert_eq!(result.len(), 256);
assert!(result.iter().all(|&p| p == 7));
}
#[test]
fn end_of_line_padding() {
let data = [
0x01, 0x02, 0x00, 0x00, 0x03, 0x04, 0x05, 0x06, ];
let result = decode_rle(&data, 4, 2).unwrap();
assert_eq!(result, vec![1, 2, 0, 0, 3, 4, 5, 6]);
}
#[test]
fn mixed_runs_multirow() {
let data = [
0x0A, 0x00, 0x02, 0x00, 0x00, 0x00, 0x83, 0x14, ];
let result = decode_rle(&data, 3, 2).unwrap();
assert_eq!(result, vec![10, 0, 0, 20, 20, 20]);
}
#[test]
fn truncated_long_zero_run() {
let data = [0x00, 0x41]; assert!(decode_rle(&data, 256, 1).is_none());
}
#[test]
fn truncated_short_color_run() {
let data = [0x00, 0x83]; assert!(decode_rle(&data, 3, 1).is_none());
}
#[test]
fn truncated_long_color_run() {
let data = [0x00, 0xC1, 0x00]; assert!(decode_rle(&data, 256, 1).is_none());
}
#[test]
fn rle_data_pads_short_output() {
let data = [0x01]; let result = decode_rle(&data, 3, 1).unwrap();
assert_eq!(result, vec![1, 0, 0]);
}
#[test]
fn max_run_length() {
let data = [0x00, 0xFF, 0xFF, 0x2A];
let result = decode_rle(&data, 16383, 1).unwrap();
assert_eq!(result.len(), 16383);
assert!(result.iter().all(|&p| p == 42));
}
#[test]
fn color_index_255() {
let data = [0xFF];
let result = decode_rle(&data, 1, 1).unwrap();
assert_eq!(result, vec![255]);
}
#[test]
fn encode_empty_image() {
assert_eq!(encode_rle(&[], 0, 0), Some(vec![]));
}
#[test]
fn encode_wrong_length_returns_none() {
assert_eq!(encode_rle(&[0, 0, 0], 2, 1), None);
}
#[test]
fn encode_single_nonzero_pixel() {
let pixels = vec![42];
let rle = encode_rle(&pixels, 1, 1).unwrap();
let decoded = decode_rle(&rle, 1, 1).unwrap();
assert_eq!(decoded, pixels);
}
#[test]
fn encode_single_zero_pixel() {
let pixels = vec![0];
let rle = encode_rle(&pixels, 1, 1).unwrap();
let decoded = decode_rle(&rle, 1, 1).unwrap();
assert_eq!(decoded, pixels);
}
#[test]
fn encode_all_zeros() {
let pixels = vec![0; 100];
let rle = encode_rle(&pixels, 100, 1).unwrap();
let decoded = decode_rle(&rle, 100, 1).unwrap();
assert_eq!(decoded, pixels);
}
#[test]
fn encode_nonzero_run_of_2() {
let pixels = vec![5, 5];
let rle = encode_rle(&pixels, 2, 1).unwrap();
let decoded = decode_rle(&rle, 2, 1).unwrap();
assert_eq!(decoded, pixels);
assert_eq!(rle, vec![5, 5, 0x00, 0x00]);
}
#[test]
fn encode_nonzero_run_of_3() {
let pixels = vec![7, 7, 7];
let rle = encode_rle(&pixels, 3, 1).unwrap();
let decoded = decode_rle(&rle, 3, 1).unwrap();
assert_eq!(decoded, pixels);
}
#[test]
fn encode_long_nonzero_run() {
let pixels = vec![42; 256];
let rle = encode_rle(&pixels, 256, 1).unwrap();
let decoded = decode_rle(&rle, 256, 1).unwrap();
assert_eq!(decoded, pixels);
}
#[test]
fn encode_mixed_multirow() {
let pixels = vec![10, 0, 0, 20, 20, 20];
let rle = encode_rle(&pixels, 3, 2).unwrap();
let decoded = decode_rle(&rle, 3, 2).unwrap();
assert_eq!(decoded, pixels);
}
#[test]
fn encode_run_exceeding_16383() {
let pixels = vec![0; 20000];
let rle = encode_rle(&pixels, 20000, 1).unwrap();
let decoded = decode_rle(&rle, 20000, 1).unwrap();
assert_eq!(decoded, pixels);
}
#[test]
fn encode_nonzero_run_exceeding_16383() {
let pixels = vec![99; 20000];
let rle = encode_rle(&pixels, 20000, 1).unwrap();
let decoded = decode_rle(&rle, 20000, 1).unwrap();
assert_eq!(decoded, pixels);
}
#[test]
fn encode_alternating_colors() {
let pixels: Vec<u8> = (0..10).map(|i| if i % 2 == 0 { 1 } else { 2 }).collect();
let rle = encode_rle(&pixels, 10, 1).unwrap();
let decoded = decode_rle(&rle, 10, 1).unwrap();
assert_eq!(decoded, pixels);
}
#[test]
fn encode_long_zero_run_64() {
let pixels = vec![0; 64];
let rle = encode_rle(&pixels, 64, 1).unwrap();
let decoded = decode_rle(&rle, 64, 1).unwrap();
assert_eq!(decoded, pixels);
}
#[test]
fn encode_max_run_16383() {
let pixels = vec![42; 16383];
let rle = encode_rle(&pixels, 16383, 1).unwrap();
let decoded = decode_rle(&rle, 16383, 1).unwrap();
assert_eq!(decoded, pixels);
}
}