This commit is contained in:
Crispy 2024-04-10 19:04:29 +02:00
commit eca5344c66
10 changed files with 445 additions and 0 deletions

35
encoder/src/dec.rs Normal file
View file

@ -0,0 +1,35 @@
use crate::*;
pub fn rle_horizontal(_prev_frame: &Frame, encoded: &[u8]) -> Frame {
let pixels = rle_255_decode(encoded);
let mut frame = FRAME_0;
let mut i = 0;
for y in 0..HEIGHT {
for x in 0..WIDTH {
frame[x][y] = pixels[i];
i += 1;
}
}
frame
}
pub fn rle_vertical(_prev_frame: &Frame, encoded: &[u8]) -> Frame {
let pixels = rle_255_decode(encoded);
let mut frame = FRAME_0;
let mut i = 0;
for x in 0..WIDTH {
for y in 0..HEIGHT {
frame[x][y] = pixels[i];
i += 1;
}
}
frame
}
pub fn fill_white(_prev_frame: &Frame, _encoded: &[u8]) -> Frame {
FRAME_1
}
pub fn fill_black(_prev_frame: &Frame, _encoded: &[u8]) -> Frame {
FRAME_0
}

45
encoder/src/enc.rs Normal file
View file

@ -0,0 +1,45 @@
use crate::*;
pub fn rle_horizontal(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
let mut pixels = Vec::new();
for y in 0..HEIGHT {
for x in 0..WIDTH {
pixels.push(frame[x][y]);
}
}
EncodedFrame {
encoding: Encoding::RLEHorizontal,
head_u4: 0,
data: rle_255_encode(&pixels),
}
}
pub fn rle_vertical(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
let mut pixels = Vec::new();
for x in 0..WIDTH {
for y in 0..HEIGHT {
pixels.push(frame[x][y]);
}
}
EncodedFrame {
encoding: Encoding::RLEVertical,
head_u4: 0,
data: rle_255_encode(&pixels),
}
}
pub fn fill_white(_prev_frame: &Frame, _frame: &Frame) -> EncodedFrame {
EncodedFrame {
encoding: Encoding::FillWhite,
head_u4: 0,
data: Vec::new(),
}
}
pub fn fill_black(_prev_frame: &Frame, _frame: &Frame) -> EncodedFrame {
EncodedFrame {
encoding: Encoding::FillBlack,
head_u4: 0,
data: Vec::new(),
}
}

90
encoder/src/main.rs Normal file
View file

@ -0,0 +1,90 @@
mod dec;
mod enc;
mod util;
pub use util::*;
const INTERACTIVE: bool = false;
fn main() {
let frames = get_all_frames("../video/frames/");
let encoded = encode(&frames);
println!("{} frames, total {} bytes", frames.len(), encoded.len());
//
}
fn encode(frames: &[Frame]) -> Vec<u8> {
let mut out = Vec::new();
let encodings: Vec<(FrameEncoder, FrameDecoder)> = vec![
(enc::fill_white, dec::fill_white),
(enc::fill_black, dec::fill_black),
(enc::rle_horizontal, dec::rle_horizontal),
(enc::rle_vertical, dec::rle_vertical),
];
let max_error = 0;
let mut last_frame = FRAME_0;
for frame in frames {
let mut options = Vec::new();
for (encode, decode) in &encodings {
let encoded = encode(&last_frame, frame);
let decoded = decode(&last_frame, &encoded.data);
let error = frame_error(frame, &decoded);
if error <= max_error {
options.push(encoded);
}
}
options.sort_by_key(|b| b.data.len());
let best_encoding = options.into_iter().next().unwrap();
if INTERACTIVE {
println!();
println!(
"{:?}, {} bytes",
best_encoding.encoding,
best_encoding.data.len() + 1
);
render_image(frame);
let mut a = String::new();
std::io::stdin().read_line(&mut a).unwrap();
}
let best_encoding = best_encoding.into_bytes();
out.extend_from_slice(&best_encoding);
last_frame = frame.clone();
}
out
}
#[derive(Debug)]
#[repr(u8)]
enum Encoding {
FillWhite,
FillBlack,
RLEHorizontal,
RLEVertical,
BGStrips,
CellDiff8Horizontal,
CellDiff8Vertical,
CellDiff4HH,
CellDiff4HV,
CellDiff4VH,
CellDiff4VV,
}
type FrameEncoder = fn(previous_frame: &Frame, new_frame: &Frame) -> EncodedFrame;
type FrameDecoder = fn(previous_frame: &Frame, encoded_bytes: &[u8]) -> Frame;
struct EncodedFrame {
encoding: Encoding,
head_u4: u8,
data: Vec<u8>,
}
impl EncodedFrame {
fn into_bytes(self) -> Vec<u8> {
let head = (self.encoding as u8) << 4;
let head = head | (self.head_u4 & 15);
let mut out = Vec::with_capacity(self.data.len() + 1);
out.push(head);
out.extend(self.data.into_iter());
out
}
}

115
encoder/src/util.rs Normal file
View file

@ -0,0 +1,115 @@
use std::{
fs::{self, File},
io::BufReader,
};
use image::{self, DynamicImage, GenericImageView, ImageFormat, Rgba};
pub const WIDTH: usize = 40;
pub const HEIGHT: usize = 32;
pub const SIZE: usize = WIDTH * HEIGHT;
pub type Frame = [[u8; HEIGHT]; WIDTH];
pub const FRAME_0: Frame = [[0; HEIGHT]; WIDTH];
pub const FRAME_1: Frame = [[1; HEIGHT]; WIDTH];
fn convert_pixel(rgba: Rgba<u8>) -> u8 {
(rgba.0[0] > 128) as u8
}
pub fn convert_image(image: &DynamicImage) -> Frame {
let mut frame = FRAME_0;
for x in 0..WIDTH {
for y in 0..HEIGHT {
frame[x][y] = convert_pixel(image.get_pixel(x as u32, y as u32));
}
}
frame
}
pub fn get_all_frames(path: &str) -> Vec<Frame> {
let frame_count = fs::read_dir(path).unwrap().count();
let mut frames = Vec::new();
for i in 0..frame_count {
let path = format!("{}frame_{:04}.png", path, i + 1);
let file = BufReader::new(File::open(path).unwrap());
let image = image::load(file, ImageFormat::Png).unwrap();
frames.push(convert_image(&image));
}
frames
}
pub fn frame_error(real: &Frame, decoded: &Frame) -> usize {
let mut error = 0;
for x in 0..WIDTH {
for y in 0..HEIGHT {
if real[x][y] != decoded[x][y] {
error += 1;
}
}
}
error
}
fn render_pixel_pair(img: &Frame, x: usize, y: usize) {
let char = match (img[x][y * 2], img[x][y * 2 + 1]) {
(0, 0) => " ",
(0, 1) => "",
(1, 0) => "",
(1, 1) => "",
_ => panic!("image contained nonbinary bytes"),
};
print!("{}", char);
}
pub fn render_image(img: &Frame) {
for y in 0..(HEIGHT / 2) {
for x in 0..WIDTH {
render_pixel_pair(img, x, y);
}
println!();
}
}
pub fn render_images(left: &Frame, right: &Frame) {
for y in 0..(HEIGHT / 2) {
for x in 0..WIDTH {
render_pixel_pair(left, x, y);
print!(" ");
render_pixel_pair(right, x, y);
}
println!();
}
}
pub fn rle_255_encode(raw: &[u8]) -> Vec<u8> {
let mut encoded = Vec::new();
let mut last_val = 0;
let mut run = 0;
for &val in raw {
if val != last_val || run == 255 {
encoded.push(run);
if run == 255 {
encoded.push(0);
}
run = 1;
} else {
run += 1;
}
last_val = val;
}
encoded.push(run);
encoded
}
pub fn rle_255_decode(encoded: &[u8]) -> Vec<u8> {
let mut raw = Vec::new();
let mut val = 0;
for &run in encoded {
for _ in 0..run {
raw.push(val);
}
val = 1 - val;
}
raw
}