use enum_map::{Enum, EnumMap}; use num_enum::TryFromPrimitive; mod dec; mod enc; mod util; pub use util::*; const INSPECT_ENC: bool = false; const INSPECT_DEC: bool = false; const MAX_ERROR: usize = 0; fn main() { let frames = get_all_frames("../video/frames/"); let encoded = encode(&frames); // todo print average bytes per frame for each encoding type let mut stats: EnumMap = EnumMap::default(); let mut reader = 0; let mut last_frame = FRAME_0; let mut frame_index = 0; while reader < encoded.len() { let frame_type: Encoding = encoded[reader].try_into().unwrap(); stats[frame_type] += 1; reader += 1; let decoder = get_matching_decoder(frame_type); last_frame = decoder(&last_frame, &encoded[reader..], &mut reader); if INSPECT_DEC { println!( "\n{frame_type:?}, error: {}", frame_error(&frames[frame_index], &last_frame) ); render_images(&frames[frame_index], &last_frame); wait_for_input(); frame_index += 1; } } for (encoding, frames) in stats.iter() { println!("{encoding:?} - {frames} frames"); } println!(); println!( "{} frames, total {} bytes (~{} bpf)", frames.len(), encoded.len(), encoded.len() / frames.len() ); } fn encode(frames: &[Frame]) -> Vec { let mut out = Vec::new(); let encodings: Vec = vec![ enc::fill_white, enc::fill_black, enc::rle_horizontal, enc::rle_vertical, enc::rle_diff_horizontal, enc::rle_diff_vertical, enc::bg_strips_horizontal, enc::cell_diff_8_horizontal, ]; let mut last_frame = FRAME_0; for (i, frame) in frames.iter().enumerate() { let mut options = Vec::new(); for encode in &encodings { let encoded = encode(&last_frame, frame); let decode = get_matching_decoder(encoded.encoding); let decoded = decode(&last_frame, &encoded.data, &mut 0); let error = frame_error(frame, &decoded); if error <= MAX_ERROR { options.push(encoded); } else { // dbg!(&encoded); // println!("{:?}, error: {error}, frame: {i}", encoded.encoding); // render_images(&frame, &decoded); // panic!("loss in compression"); } } options.sort_by_key(|b| b.data.len()); let best_encoding = options.into_iter().next().unwrap(); if INSPECT_ENC { println!(); println!( "{:?}, {} bytes", best_encoding.encoding, best_encoding.data.len() + 1 ); render_image(frame); wait_for_input(); } let best_encoding = best_encoding.into_bytes(); out.extend_from_slice(&best_encoding); last_frame = *frame; } out } #[derive(Debug, TryFromPrimitive, Enum, Copy, Clone)] #[repr(u8)] enum Encoding { FillWhite, FillBlack, RLEHorizontal, RLEVertical, RLEDiffHorizontal, RLEDiffVertical, BGStripsH, // BGStripsV, // QuadTree, // DrawCommands, CellDiff8H, // CellDiff8V, // CellDiff4HH, // CellDiff4HV, // CellDiff4VH, // CellDiff4VV, } fn get_matching_decoder(encoding: Encoding) -> FrameDecoder { match encoding { Encoding::FillWhite => dec::fill_white, Encoding::FillBlack => dec::fill_black, Encoding::RLEHorizontal => dec::rle_horizontal, Encoding::RLEVertical => dec::rle_vertical, Encoding::RLEDiffHorizontal => dec::rle_diff_horizontal, Encoding::RLEDiffVertical => dec::rle_diff_vertical, Encoding::BGStripsH => dec::bg_strips_horizontal, // Encoding::BGStripsV => todo!(), // Encoding::QuadTree => todo!(), // Encoding::DrawCommands => todo!(), Encoding::CellDiff8H => dec::cell_diff_8_horizontal, // Encoding::CellDiff8V => todo!(), // Encoding::CellDiff4HH => todo!(), // Encoding::CellDiff4HV => todo!(), // Encoding::CellDiff4VH => todo!(), // Encoding::CellDiff4VV => todo!(), } } type FrameEncoder = fn(previous_frame: &Frame, new_frame: &Frame) -> EncodedFrame; type FrameDecoder = fn(previous_frame: &Frame, encoded_bytes: &[u8], reader: &mut usize) -> Frame; #[derive(Debug)] struct EncodedFrame { encoding: Encoding, data: Vec, } impl EncodedFrame { fn into_bytes(mut self) -> Vec { self.data.insert(0, self.encoding as u8); self.data } }