bad-apple/encoder/src/main.rs

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
}
}