diff --git a/encoder/src/dec.rs b/encoder/src/dec.rs index 90f7a73..3e73c27 100644 --- a/encoder/src/dec.rs +++ b/encoder/src/dec.rs @@ -1,9 +1,30 @@ use crate::*; +pub fn bg_strips_horizontal(_prev_frame: &Frame, encoded: &[u8], reader: &mut usize) -> Frame { + *reader += 1; + let bg = encoded[0] >> 7; + let fg = 1 - bg; + let mut frame = [[bg; HEIGHT]; WIDTH]; + let count = (encoded[0] & 0x7f) as usize; + *reader += count * 2; + for i in 0..count { + let upper = (encoded[i * 2 + 1] as u16) << 8; + let lower = encoded[i * 2 + 2] as u16; + let y_x_w: u16 = upper | lower; + let y = (y_x_w >> 11) as usize; + let x_start = ((y_x_w >> 5) & 0x3f) as usize; + let w = (y_x_w & 0x1f) as usize; + for x in x_start..(x_start + w) { + frame[x][y] = fg; + } + } + frame +} + pub fn rle_diff_horizontal(prev_frame: &Frame, encoded: &[u8], reader: &mut usize) -> Frame { let (runs, decoded) = rle_255_decode_until(encoded, FRAME_SIZE); *reader += runs; - let mut frame = prev_frame.clone(); + let mut frame = *prev_frame; let mut i = 0; for y in 0..HEIGHT { for x in 0..WIDTH { @@ -17,11 +38,15 @@ pub fn rle_diff_horizontal(prev_frame: &Frame, encoded: &[u8], reader: &mut usiz pub fn rle_diff_vertical(prev_frame: &Frame, encoded: &[u8], reader: &mut usize) -> Frame { let (runs, decoded) = rle_255_decode_until(encoded, FRAME_SIZE); *reader += runs; - let mut frame = prev_frame.clone(); + let mut frame = *prev_frame; + let mut dbg_frame = FRAME_0; let mut i = 0; for x in 0..WIDTH { for y in 0..HEIGHT { frame[x][y] ^= decoded[i]; + frame[x][y] &= 1; + dbg_frame[x][y] ^= decoded[i]; + dbg_frame[x][y] &= 1; i += 1; } } diff --git a/encoder/src/enc.rs b/encoder/src/enc.rs index 6aec066..e7e0f92 100644 --- a/encoder/src/enc.rs +++ b/encoder/src/enc.rs @@ -1,5 +1,58 @@ use crate::*; +pub fn bg_strips_horizontal(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame { + let bg = most_common_pixel(frame); + fn pack_strip(x: usize, y: usize, width: usize) -> [u8; 2] { + // Y is 0..31 so will only need 5 bits + // x is 0..42 so needs 6 bits + // 5 bits remain for width + // y: 1111100000000000 + let mut y_x_w: u16 = (y as u16) << 11; + // x: 0000011111100000 + y_x_w |= (x as u16) << 5; + // w: 0000000000011111 + y_x_w |= width as u16; + [(y_x_w >> 8) as u8, (y_x_w as u8)] + } + + let mut strips = Vec::new(); + 'outer: for y in 0..HEIGHT { + let mut strip_start = 0; + let mut in_strip = false; + for x in 0..WIDTH { + let pixel = frame[x][y]; + if !in_strip && pixel != bg { + in_strip = true; + strip_start = x; + } + if in_strip { + if pixel == bg { + strips.push((strip_start, y, x - strip_start)); + in_strip = false; + } else if x - strip_start == 31 { + strips.push((strip_start, y, x - strip_start)); + strip_start = x; + } + if strips.len() == 127 { + break 'outer; + } + } + } + if in_strip { + strips.push((strip_start, y, WIDTH - strip_start)); + } + } + let mut frame_bytes = Vec::with_capacity(1 + strips.len() * 2); + frame_bytes.push(bg << 7 | (strips.len() as u8)); + for (x, y, width) in strips { + frame_bytes.extend_from_slice(&pack_strip(x, y, width)); + } + EncodedFrame { + encoding: Encoding::BGStripsH, + data: frame_bytes, + } +} + pub fn rle_diff_horizontal(prev_frame: &Frame, frame: &Frame) -> EncodedFrame { let mut pixels = Vec::new(); for y in 0..HEIGHT { diff --git a/encoder/src/main.rs b/encoder/src/main.rs index b8c46ff..daadb9c 100644 --- a/encoder/src/main.rs +++ b/encoder/src/main.rs @@ -7,7 +7,7 @@ mod util; pub use util::*; const INSPECT_ENC: bool = false; -const INSPECT_DEC: bool = true; +const INSPECT_DEC: bool = false; fn main() { let frames = get_all_frames("../video/frames/"); @@ -25,7 +25,10 @@ fn main() { let decoder = get_matching_decoder(frame_type); last_frame = decoder(&last_frame, &encoded[reader..], &mut reader); if INSPECT_DEC { - println!("\n{frame_type:?}"); + 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; @@ -52,11 +55,12 @@ fn encode(frames: &[Frame]) -> Vec { enc::rle_vertical, enc::rle_diff_horizontal, enc::rle_diff_vertical, + enc::bg_strips_horizontal, ]; let max_error = 0; let mut last_frame = FRAME_0; - for frame in frames { + for (i, frame) in frames.iter().enumerate() { let mut options = Vec::new(); for encode in &encodings { let encoded = encode(&last_frame, frame); @@ -65,6 +69,11 @@ fn encode(frames: &[Frame]) -> Vec { 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()); @@ -81,7 +90,7 @@ fn encode(frames: &[Frame]) -> Vec { } let best_encoding = best_encoding.into_bytes(); out.extend_from_slice(&best_encoding); - last_frame = frame.clone(); + last_frame = *frame; } out } @@ -96,15 +105,15 @@ enum Encoding { RLEDiffHorizontal, RLEDiffVertical, BGStripsH, - BGStripsV, - QuadTree, - DrawCommands, - CellDiff8H, - CellDiff8V, - CellDiff4HH, - CellDiff4HV, - CellDiff4VH, - CellDiff4VV, + // BGStripsV, + // QuadTree, + // DrawCommands, + // CellDiff8H, + // CellDiff8V, + // CellDiff4HH, + // CellDiff4HV, + // CellDiff4VH, + // CellDiff4VV, } fn get_matching_decoder(encoding: Encoding) -> FrameDecoder { @@ -115,22 +124,23 @@ fn get_matching_decoder(encoding: Encoding) -> FrameDecoder { Encoding::RLEVertical => dec::rle_vertical, Encoding::RLEDiffHorizontal => dec::rle_diff_horizontal, Encoding::RLEDiffVertical => dec::rle_diff_vertical, - Encoding::BGStripsH => todo!(), - Encoding::BGStripsV => todo!(), - Encoding::QuadTree => todo!(), - Encoding::DrawCommands => todo!(), - Encoding::CellDiff8H => todo!(), - Encoding::CellDiff8V => todo!(), - Encoding::CellDiff4HH => todo!(), - Encoding::CellDiff4HV => todo!(), - Encoding::CellDiff4VH => todo!(), - Encoding::CellDiff4VV => todo!(), + Encoding::BGStripsH => dec::bg_strips_horizontal, + // Encoding::BGStripsV => todo!(), + // Encoding::QuadTree => todo!(), + // Encoding::DrawCommands => todo!(), + // Encoding::CellDiff8H => todo!(), + // 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, diff --git a/encoder/src/util.rs b/encoder/src/util.rs index f68526f..0c81fe3 100644 --- a/encoder/src/util.rs +++ b/encoder/src/util.rs @@ -1,6 +1,6 @@ use std::{ fs::{self, File}, - io::BufReader, + io::{stdin, BufReader}, }; use image::{self, DynamicImage, GenericImageView, ImageFormat, Rgba}; @@ -14,7 +14,7 @@ pub const FRAME_0: Frame = [[0; HEIGHT]; WIDTH]; pub const FRAME_1: Frame = [[1; HEIGHT]; WIDTH]; pub fn wait_for_input() { - std::io::stdin().read_line(&mut String::new()).unwrap(); + stdin().read_line(&mut String::new()).unwrap(); } fn convert_pixel(rgba: Rgba) -> u8 { @@ -55,6 +55,23 @@ pub fn frame_error(real: &Frame, decoded: &Frame) -> usize { error } +pub fn most_common_pixel(frame: &Frame) -> u8 { + let mut white_pixels = 0; + let mut black_pixels = 0; + for col in frame { + for &p in col { + let p = p as u32; + white_pixels += p; + black_pixels += 1 - p; + } + } + if white_pixels > black_pixels { + 1 + } else { + 0 + } +} + fn render_pixel_pair(img: &Frame, x: usize, y: usize) { let char = match (img[x][y * 2], img[x][y * 2 + 1]) { (0, 0) => " ", @@ -77,14 +94,15 @@ pub fn render_image(img: &Frame) { pub fn render_images(left: &Frame, right: &Frame) { for y in 0..(HEIGHT / 2) { + print!("|"); for x in 0..WIDTH { render_pixel_pair(left, x, y); } - print!(" "); + print!("|"); for x in 0..WIDTH { render_pixel_pair(right, x, y); } - println!(); + println!("|"); } } @@ -95,7 +113,7 @@ pub fn rle_255_encode(raw: &[u8]) -> Vec { for &val in raw { if val != last_val || run == 255 { encoded.push(run); - if run == 255 { + if run == 255 && val == last_val { encoded.push(0); } run = 1;