slightly better rle variant, implement the cell based one on the pico, 16fps fits and works

This commit is contained in:
Crispy 2025-06-17 20:29:49 +02:00
parent c5f7c245d2
commit f05d146af7
6 changed files with 123434 additions and 79213 deletions

View file

@ -55,49 +55,36 @@ pub fn cell_diff_8_vertical_big(prev_frame: &Frame, encoded: &[u8], reader: &mut
const CELLS_X: usize = WIDTH / 8;
const CELLS_Y: usize = HEIGHT / 8;
let mut modified_cells = [[false; CELLS_Y]; CELLS_X];
let (mut cell_runs, modified_cells_flat) =
rle_255_decode(&unpack_nybbles(encoded), CELLS_X * CELLS_Y);
if cell_runs % 2 == 1 {
cell_runs += 1;
}
*reader += cell_runs / 2;
let encoded = &encoded[(cell_runs / 2)..];
let mut changed_cell_count = 0;
let mut i = 0;
for cellx in 0..CELLS_X {
for celly in 0..CELLS_Y {
let cell = modified_cells_flat[i] == 1;
modified_cells[cellx][celly] = cell;
if cell {
changed_cell_count += 1;
}
i += 1;
}
}
let (cell_runs, modified_cells_flat) = rle_255_decode(&encoded, CELLS_X * CELLS_Y);
*reader += cell_runs;
let encoded = &encoded[cell_runs..];
let mut frame = *prev_frame;
let changed_pixel_count = changed_cell_count * 8 * 8;
let (runs, new_pixels) = rle_255_decode(encoded, changed_pixel_count);
*reader += runs;
let mut index = 0;
for x in 0..WIDTH {
for y in 0..HEIGHT {
let cellx = x / 8;
let celly = y / 8;
let is_changed = modified_cells[cellx][celly];
if is_changed {
frame[x][y] = new_pixels[index];
index += 1;
let mut x = 0;
let mut y = 0;
let mut color = 1;
let mut run = 0;
let mut i = 0;
while x < WIDTH {
let cx = x / 8;
let cy = y / 8;
if modified_cells_flat[cx * CELLS_Y + cy] != 0 {
while run == 0 {
run = encoded[i];
i += 1;
color = 1 - color;
}
run -= 1;
frame[x][y] = color;
}
y += 1;
if y == HEIGHT {
x += 1;
y = 0;
}
}
*reader += i;
frame
}
@ -307,6 +294,72 @@ pub fn rle_vertical_ext(_prev_frame: &Frame, encoded: &[u8], reader: &mut usize)
frame
}
pub fn rle_vertical_ext_2(_prev_frame: &Frame, encoded: &[u8], reader: &mut usize) -> Frame {
let mut x = 0;
let mut y = 0;
let mut color = 0;
let mut i = 0;
let mut frame = FRAME_0;
while x < WIDTH {
let byte = encoded[i];
i += 1;
let mut run = byte as u16;
if byte == 255 {
// 16 bit run length
let upper = (encoded[i] as u16) << 8;
let lower = encoded[i + 1] as u16;
i += 2;
run = upper | lower;
}
for _ in 0..run {
frame[x][y] = color;
y += 1;
if y == HEIGHT {
y = 0;
x += 1;
}
}
color = 1 - color;
}
*reader += i;
frame
}
pub fn rle_horizontal_ext_2(_prev_frame: &Frame, encoded: &[u8], reader: &mut usize) -> Frame {
let mut x = 0;
let mut y = 0;
let mut color = 0;
let mut i = 0;
let mut frame = FRAME_0;
while y < HEIGHT {
let byte = encoded[i];
i += 1;
let mut run = byte as u16;
if byte == 255 {
// 16 bit run length
let upper = (encoded[i] as u16) << 8;
let lower = encoded[i + 1] as u16;
i += 2;
run = upper | lower;
}
for _ in 0..run {
frame[x][y] = color;
x += 1;
if x == WIDTH {
x = 0;
y += 1;
}
}
color = 1 - color;
}
*reader += i;
frame
}
pub fn rle_vertical_var(_prev_frame: &Frame, encoded: &[u8], reader: &mut usize) -> Frame {
let mut x = 0;
let mut y = 0;

View file

@ -53,8 +53,8 @@ pub fn cell_diff_4_vertical(prev_frame: &Frame, frame: &Frame, loss: usize) -> E
}
}
pub fn cell_diff_8_vertical_big(prev_frame: &Frame, frame: &Frame, loss: usize) -> EncodedFrame {
let loss = loss / 8;
pub fn cell_diff_8_vertical_big(prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
// let loss = loss / 8;
const CELLS_X: usize = WIDTH / 8;
const CELLS_Y: usize = HEIGHT / 8;
@ -74,7 +74,7 @@ pub fn cell_diff_8_vertical_big(prev_frame: &Frame, frame: &Frame, loss: usize)
}
}
}
if changed > loss {
if changed > 0{
modified_cells[cellx][celly] = 1;
}
}
@ -97,7 +97,7 @@ pub fn cell_diff_8_vertical_big(prev_frame: &Frame, frame: &Frame, loss: usize)
}
}
let mut data = Vec::new();
data.extend_from_slice(&pack_nybbles(rle_encode(&modified_cells_flat, 15)));
data.extend_from_slice(&rle_255_encode(&modified_cells_flat));
data.extend_from_slice(&rle_255_encode(&changed_pixels));
EncodedFrame {
@ -298,7 +298,7 @@ pub fn rle_horizontal_ext(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
};
for y in 0..HEIGHT {
for x in 0..WIDTH {
for x in 0..WIDTH {
let pixel = frame[x][y];
if pixel != last_pixel || run == 0xffff {
push_run(&mut data, run);
@ -320,7 +320,6 @@ pub fn rle_horizontal_ext(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
}
}
pub fn rle_horizontal(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
let mut pixels = Vec::new();
for y in 0..HEIGHT {
@ -349,7 +348,7 @@ pub fn tree_16(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
let x = cx * (WIDTH / 4) + px;
for py in 0..(HEIGHT / 4) {
let y = cy * (HEIGHT / 4) + py;
if frame[x][y] == fg{
if frame[x][y] == fg {
any_filled_here = true;
break 'a;
}
@ -369,8 +368,8 @@ pub fn tree_16(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
if frame[x][y] == fg {
node_pixels.push(fg);
any_filled_here = true;
}else{
node_pixels.push(1-fg);
} else {
node_pixels.push(1 - fg);
}
}
}
@ -385,10 +384,10 @@ pub fn tree_16(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
}
}
let mut data = Vec::new();
data.push((top_node >>8) as u8);
data.push((top_node >> 8) as u8);
data.push(top_node as u8);
for n in second_nodes{
data.push((n>>8)as u8);
for n in second_nodes {
data.push((n >> 8) as u8);
data.push(n as u8);
}
data.extend_from_slice(&rle_255_encode(&included_pixels));
@ -422,8 +421,6 @@ pub fn rle_vertical_ext(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
data.push(run as u8);
} else {
// double zero to mark 16 bit run length
// if data.last() != Some(&0) {
// }
data.push(0);
data.push(0);
data.push((run >> 8) as u8);
@ -454,7 +451,81 @@ pub fn rle_vertical_ext(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
}
}
pub fn rle_vertical_ext_2(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
let mut data = Vec::new();
let mut last_pixel = 0;
let mut run: u16 = 0;
let push_run = |data: &mut Vec<u8>, run| {
if run < 255 {
data.push(run as u8);
} else {
data.push(255);
data.push((run >> 8) as u8);
data.push((run & 0xff) as u8);
}
};
for x in 0..WIDTH {
for y in 0..HEIGHT {
let pixel = frame[x][y];
if pixel != last_pixel || run == 0xffff {
push_run(&mut data, run);
if run == 0xffff && pixel == last_pixel {
// inserting dummy run because we ran out of max len
data.push(0);
}
run = 1;
} else {
run += 1;
}
last_pixel = pixel;
}
}
push_run(&mut data, run);
EncodedFrame {
encoding: Encoding::RLEVerticalExt2,
data,
}
}
pub fn rle_horizontal_ext_2(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
let mut data = Vec::new();
let mut last_pixel = 0;
let mut run: u16 = 0;
let push_run = |data: &mut Vec<u8>, run| {
if run < 255 {
data.push(run as u8);
} else {
data.push(255);
data.push((run >> 8) as u8);
data.push((run & 0xff) as u8);
}
};
for y in 0..HEIGHT {
for x in 0..WIDTH {
let pixel = frame[x][y];
if pixel != last_pixel || run == 0xffff {
push_run(&mut data, run);
if run == 0xffff && pixel == last_pixel {
// inserting dummy run because we ran out of max len
data.push(0);
}
run = 1;
} else {
run += 1;
}
last_pixel = pixel;
}
}
push_run(&mut data, run);
EncodedFrame {
encoding: Encoding::RLEHorizontalExt2,
data,
}
}
pub fn rle_vertical_var(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
// variable bit count
@ -469,12 +540,12 @@ pub fn rle_vertical_var(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
let push_run = |data: &mut Vec<u8>, run| {
if run < 0x7f {
data.push(run as u8);
}else if run < 0x3fff {
} else if run < 0x3fff {
data.push((run >> 8) as u8 | 0x80);
data.push(run as u8);
} else {
data.push((run >> 16) as u8 | 0xC0);
data.push((run >> 8 ) as u8);
data.push((run >> 8) as u8);
data.push(run as u8);
}
};
@ -482,7 +553,7 @@ pub fn rle_vertical_var(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
for x in 0..WIDTH {
for y in 0..HEIGHT {
let pixel = frame[x][y];
if pixel != last_pixel{
if pixel != last_pixel {
push_run(&mut data, run);
run = 1;
} else {

View file

@ -15,37 +15,42 @@ const MAX_LOSS: usize = 0; // highest "loss" value tried for all lossy encodings
const LOSSLESS_ENCODINGS: &[FrameEncoder] = &[
enc::rle_horizontal,
enc::rle_horizontal_ext,
// enc::rle_horizontal_ext,
enc::rle_horizontal_ext_2,
enc::rle_vertical,
enc::rle_vertical_ext,
enc::rle_vertical_var,
enc::rle_vertical_16,
enc::rle_diff_horizontal,
enc::rle_diff_vertical,
// enc::rle_vertical_ext,
enc::rle_vertical_ext_2,
// enc::rle_vertical_var, // not worth
// enc::rle_vertical_16, // not worth
// enc::rle_diff_horizontal,
// enc::rle_diff_vertical,
// enc::bg_strips_horizontal_16, // only works for the tiny display
enc::bg_strips_horizontal_24, // intended for the 240x320 display
// enc::tree_16,// turns out to be useless
enc::cell_diff_8_vertical_big,
];
const LOSSY_ENCODINGS: &[FrameEncoderLossy] = &[
enc::fill_white,
enc::fill_black,
// todo: adapt for big display
// enc::cell_diff_8_vertical,
enc::cell_diff_4_vertical,
enc::cell_diff_8_vertical_big,
// enc::cell_diff_4_vertical,
];
fn main() {
let frames = get_all_frames("../video/frames/");
let encoded = encode(&frames);
let mut stats: EnumMap<Encoding, u32> = EnumMap::default();
let mut stats: EnumMap<Encoding, (u32, Vec<usize>)> = 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;
stats[frame_type].0 += 1;
if stats[frame_type].1.len() < 5{
stats[frame_type].1.push(frame_index);
}
reader += 1;
let decoder = get_matching_decoder(frame_type);
last_frame = decoder(&last_frame, &encoded[reader..], &mut reader);
@ -56,12 +61,12 @@ fn main() {
);
render_images(&frames[frame_index], &last_frame);
wait_for_input();
frame_index += 1;
}
frame_index += 1;
}
for (encoding, &frames) in stats.iter() {
if frames > 0 {
println!("{encoding:?} - {frames} frames");
for (encoding, (frames, indexes)) in &stats {
if *frames > 0 {
println!("{encoding:?} - {frames} frames: {indexes:?}");
}
}
println!();
@ -73,13 +78,13 @@ fn main() {
);
let mut export_string = String::from("// Generated by the `encoder` rust app\n");
for (encoding, count) in stats {
for (encoding, &(count, _)) in &stats {
if count > 0 {
export_string += &format!("#define USE_{encoding:?}\n");
}
}
export_string += "\n\ntypedef enum Encoding {\n";
for (encoding, count) in stats {
for (encoding, (count, _)) in stats {
if count > 0 {
export_string += &format!("\tEncoding_{encoding:?} = {},\n", encoding as u8);
}
@ -127,7 +132,7 @@ fn encode(frames: &[Frame]) -> Vec<u8> {
}
}
for encode in LOSSY_ENCODINGS.iter() {
for loss in 0..MAX_LOSS {
for loss in 0..=MAX_LOSS {
let encoded = encode(&last_frame, frame, loss);
let decode = get_matching_decoder(encoded.encoding);
let decoded = decode(&last_frame, &encoded.data, &mut 0);
@ -169,8 +174,10 @@ enum Encoding {
FillWhite,
RLEHorizontal,
RLEHorizontalExt,
RLEHorizontalExt2,
RLEVertical,
RLEVerticalExt,
RLEVerticalExt2,
RLEVerticalVar,
RLEVertical16,
RLEDiffHorizontal,
@ -197,8 +204,10 @@ fn get_matching_decoder(encoding: Encoding) -> FrameDecoder {
Encoding::FillBlack => dec::fill_black,
Encoding::RLEHorizontal => dec::rle_horizontal,
Encoding::RLEHorizontalExt => dec::rle_horizontal_ext,
Encoding::RLEHorizontalExt2 => dec::rle_horizontal_ext_2,
Encoding::RLEVertical => dec::rle_vertical,
Encoding::RLEVerticalExt => dec::rle_vertical_ext,
Encoding::RLEVerticalExt2 => dec::rle_vertical_ext_2,
Encoding::RLEVerticalVar => dec::rle_vertical_var,
Encoding::RLEVertical16 => dec::rle_vertical_16,
Encoding::RLEDiffHorizontal => dec::rle_diff_horizontal,