add horizontal strip encoding type

This commit is contained in:
Crispy 2024-04-11 23:28:49 +02:00
parent 3af9be328c
commit 1f61cebd50
4 changed files with 136 additions and 30 deletions

View file

@ -1,9 +1,30 @@
use crate::*; 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 { pub fn rle_diff_horizontal(prev_frame: &Frame, encoded: &[u8], reader: &mut usize) -> Frame {
let (runs, decoded) = rle_255_decode_until(encoded, FRAME_SIZE); let (runs, decoded) = rle_255_decode_until(encoded, FRAME_SIZE);
*reader += runs; *reader += runs;
let mut frame = prev_frame.clone(); let mut frame = *prev_frame;
let mut i = 0; let mut i = 0;
for y in 0..HEIGHT { for y in 0..HEIGHT {
for x in 0..WIDTH { 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 { pub fn rle_diff_vertical(prev_frame: &Frame, encoded: &[u8], reader: &mut usize) -> Frame {
let (runs, decoded) = rle_255_decode_until(encoded, FRAME_SIZE); let (runs, decoded) = rle_255_decode_until(encoded, FRAME_SIZE);
*reader += runs; *reader += runs;
let mut frame = prev_frame.clone(); let mut frame = *prev_frame;
let mut dbg_frame = FRAME_0;
let mut i = 0; let mut i = 0;
for x in 0..WIDTH { for x in 0..WIDTH {
for y in 0..HEIGHT { for y in 0..HEIGHT {
frame[x][y] ^= decoded[i]; frame[x][y] ^= decoded[i];
frame[x][y] &= 1;
dbg_frame[x][y] ^= decoded[i];
dbg_frame[x][y] &= 1;
i += 1; i += 1;
} }
} }

View file

@ -1,5 +1,58 @@
use crate::*; 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 { pub fn rle_diff_horizontal(prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
let mut pixels = Vec::new(); let mut pixels = Vec::new();
for y in 0..HEIGHT { for y in 0..HEIGHT {

View file

@ -7,7 +7,7 @@ mod util;
pub use util::*; pub use util::*;
const INSPECT_ENC: bool = false; const INSPECT_ENC: bool = false;
const INSPECT_DEC: bool = true; const INSPECT_DEC: bool = false;
fn main() { fn main() {
let frames = get_all_frames("../video/frames/"); let frames = get_all_frames("../video/frames/");
@ -25,7 +25,10 @@ fn main() {
let decoder = get_matching_decoder(frame_type); let decoder = get_matching_decoder(frame_type);
last_frame = decoder(&last_frame, &encoded[reader..], &mut reader); last_frame = decoder(&last_frame, &encoded[reader..], &mut reader);
if INSPECT_DEC { 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); render_images(&frames[frame_index], &last_frame);
wait_for_input(); wait_for_input();
frame_index += 1; frame_index += 1;
@ -52,11 +55,12 @@ fn encode(frames: &[Frame]) -> Vec<u8> {
enc::rle_vertical, enc::rle_vertical,
enc::rle_diff_horizontal, enc::rle_diff_horizontal,
enc::rle_diff_vertical, enc::rle_diff_vertical,
enc::bg_strips_horizontal,
]; ];
let max_error = 0; let max_error = 0;
let mut last_frame = FRAME_0; let mut last_frame = FRAME_0;
for frame in frames { for (i, frame) in frames.iter().enumerate() {
let mut options = Vec::new(); let mut options = Vec::new();
for encode in &encodings { for encode in &encodings {
let encoded = encode(&last_frame, frame); let encoded = encode(&last_frame, frame);
@ -65,6 +69,11 @@ fn encode(frames: &[Frame]) -> Vec<u8> {
let error = frame_error(frame, &decoded); let error = frame_error(frame, &decoded);
if error <= max_error { if error <= max_error {
options.push(encoded); 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()); options.sort_by_key(|b| b.data.len());
@ -81,7 +90,7 @@ fn encode(frames: &[Frame]) -> Vec<u8> {
} }
let best_encoding = best_encoding.into_bytes(); let best_encoding = best_encoding.into_bytes();
out.extend_from_slice(&best_encoding); out.extend_from_slice(&best_encoding);
last_frame = frame.clone(); last_frame = *frame;
} }
out out
} }
@ -96,15 +105,15 @@ enum Encoding {
RLEDiffHorizontal, RLEDiffHorizontal,
RLEDiffVertical, RLEDiffVertical,
BGStripsH, BGStripsH,
BGStripsV, // BGStripsV,
QuadTree, // QuadTree,
DrawCommands, // DrawCommands,
CellDiff8H, // CellDiff8H,
CellDiff8V, // CellDiff8V,
CellDiff4HH, // CellDiff4HH,
CellDiff4HV, // CellDiff4HV,
CellDiff4VH, // CellDiff4VH,
CellDiff4VV, // CellDiff4VV,
} }
fn get_matching_decoder(encoding: Encoding) -> FrameDecoder { fn get_matching_decoder(encoding: Encoding) -> FrameDecoder {
@ -115,22 +124,23 @@ fn get_matching_decoder(encoding: Encoding) -> FrameDecoder {
Encoding::RLEVertical => dec::rle_vertical, Encoding::RLEVertical => dec::rle_vertical,
Encoding::RLEDiffHorizontal => dec::rle_diff_horizontal, Encoding::RLEDiffHorizontal => dec::rle_diff_horizontal,
Encoding::RLEDiffVertical => dec::rle_diff_vertical, Encoding::RLEDiffVertical => dec::rle_diff_vertical,
Encoding::BGStripsH => todo!(), Encoding::BGStripsH => dec::bg_strips_horizontal,
Encoding::BGStripsV => todo!(), // Encoding::BGStripsV => todo!(),
Encoding::QuadTree => todo!(), // Encoding::QuadTree => todo!(),
Encoding::DrawCommands => todo!(), // Encoding::DrawCommands => todo!(),
Encoding::CellDiff8H => todo!(), // Encoding::CellDiff8H => todo!(),
Encoding::CellDiff8V => todo!(), // Encoding::CellDiff8V => todo!(),
Encoding::CellDiff4HH => todo!(), // Encoding::CellDiff4HH => todo!(),
Encoding::CellDiff4HV => todo!(), // Encoding::CellDiff4HV => todo!(),
Encoding::CellDiff4VH => todo!(), // Encoding::CellDiff4VH => todo!(),
Encoding::CellDiff4VV => todo!(), // Encoding::CellDiff4VV => todo!(),
} }
} }
type FrameEncoder = fn(previous_frame: &Frame, new_frame: &Frame) -> EncodedFrame; type FrameEncoder = fn(previous_frame: &Frame, new_frame: &Frame) -> EncodedFrame;
type FrameDecoder = fn(previous_frame: &Frame, encoded_bytes: &[u8], reader: &mut usize) -> Frame; type FrameDecoder = fn(previous_frame: &Frame, encoded_bytes: &[u8], reader: &mut usize) -> Frame;
#[derive(Debug)]
struct EncodedFrame { struct EncodedFrame {
encoding: Encoding, encoding: Encoding,
data: Vec<u8>, data: Vec<u8>,

View file

@ -1,6 +1,6 @@
use std::{ use std::{
fs::{self, File}, fs::{self, File},
io::BufReader, io::{stdin, BufReader},
}; };
use image::{self, DynamicImage, GenericImageView, ImageFormat, Rgba}; 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 const FRAME_1: Frame = [[1; HEIGHT]; WIDTH];
pub fn wait_for_input() { 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>) -> u8 { fn convert_pixel(rgba: Rgba<u8>) -> u8 {
@ -55,6 +55,23 @@ pub fn frame_error(real: &Frame, decoded: &Frame) -> usize {
error 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) { fn render_pixel_pair(img: &Frame, x: usize, y: usize) {
let char = match (img[x][y * 2], img[x][y * 2 + 1]) { let char = match (img[x][y * 2], img[x][y * 2 + 1]) {
(0, 0) => " ", (0, 0) => " ",
@ -77,14 +94,15 @@ pub fn render_image(img: &Frame) {
pub fn render_images(left: &Frame, right: &Frame) { pub fn render_images(left: &Frame, right: &Frame) {
for y in 0..(HEIGHT / 2) { for y in 0..(HEIGHT / 2) {
print!("|");
for x in 0..WIDTH { for x in 0..WIDTH {
render_pixel_pair(left, x, y); render_pixel_pair(left, x, y);
} }
print!(" "); print!("|");
for x in 0..WIDTH { for x in 0..WIDTH {
render_pixel_pair(right, x, y); render_pixel_pair(right, x, y);
} }
println!(); println!("|");
} }
} }
@ -95,7 +113,7 @@ pub fn rle_255_encode(raw: &[u8]) -> Vec<u8> {
for &val in raw { for &val in raw {
if val != last_val || run == 255 { if val != last_val || run == 255 {
encoded.push(run); encoded.push(run);
if run == 255 { if run == 255 && val == last_val {
encoded.push(0); encoded.push(0);
} }
run = 1; run = 1;