155 lines
3.9 KiB
Rust
155 lines
3.9 KiB
Rust
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<Encoding, u32> = 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<u8> {
|
|
let mut out = Vec::new();
|
|
let encodings: Vec<FrameEncoder> = 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<u8>,
|
|
}
|
|
|
|
impl EncodedFrame {
|
|
fn into_bytes(mut self) -> Vec<u8> {
|
|
self.data.insert(0, self.encoding as u8);
|
|
self.data
|
|
}
|
|
}
|