458 lines
11 KiB
Rust
458 lines
11 KiB
Rust
use crate::*;
|
|
|
|
pub fn cell_diff_4_vertical(prev_frame: &Frame, frame: &Frame, loss: usize) -> EncodedFrame {
|
|
let loss = loss / 4;
|
|
const CELLS_X: usize = WIDTH / 4;
|
|
const CELLS_Y: usize = HEIGHT / 4;
|
|
|
|
let mut modified_cells = [[0; CELLS_Y]; CELLS_X];
|
|
|
|
for cellx in 0..CELLS_X {
|
|
for celly in 0..CELLS_Y {
|
|
let mut changed = 0;
|
|
for dx in 0..4 {
|
|
for dy in 0..4 {
|
|
let x = cellx * 4 + dx;
|
|
let y = celly * 4 + dy;
|
|
let pixel = frame[x][y];
|
|
|
|
if pixel != prev_frame[x][y] {
|
|
changed += 1;
|
|
}
|
|
}
|
|
}
|
|
if changed > loss {
|
|
modified_cells[cellx][celly] = 1;
|
|
}
|
|
}
|
|
}
|
|
let mut changed_pixels = Vec::new();
|
|
for x in 0..WIDTH {
|
|
for y in 0..HEIGHT {
|
|
let cellx = x / 4;
|
|
let celly = y / 4;
|
|
|
|
if modified_cells[cellx][celly] != 0 {
|
|
changed_pixels.push(frame[x][y]);
|
|
}
|
|
}
|
|
}
|
|
let mut modified_cells_flat = Vec::new();
|
|
for x in 0..CELLS_X {
|
|
for y in 0..CELLS_Y {
|
|
modified_cells_flat.push(modified_cells[x][y]);
|
|
}
|
|
}
|
|
let mut data = Vec::new();
|
|
data.extend_from_slice(&pack_nybbles(rle_encode(&modified_cells_flat, 15)));
|
|
data.extend_from_slice(&rle_255_encode(&changed_pixels));
|
|
|
|
EncodedFrame {
|
|
encoding: Encoding::CellDiff4VV,
|
|
data,
|
|
}
|
|
}
|
|
|
|
pub fn cell_diff_8_vertical_big(prev_frame: &Frame, frame: &Frame, loss: usize) -> EncodedFrame {
|
|
let loss = loss / 8;
|
|
const CELLS_X: usize = WIDTH / 8;
|
|
const CELLS_Y: usize = HEIGHT / 8;
|
|
|
|
let mut modified_cells = [[0; CELLS_Y]; CELLS_X];
|
|
|
|
for cellx in 0..CELLS_X {
|
|
for celly in 0..CELLS_Y {
|
|
let mut changed = 0;
|
|
for dx in 0..8 {
|
|
for dy in 0..8 {
|
|
let x = cellx * 8 + dx;
|
|
let y = celly * 8 + dy;
|
|
let pixel = frame[x][y];
|
|
|
|
if pixel != prev_frame[x][y] {
|
|
changed += 1;
|
|
}
|
|
}
|
|
}
|
|
if changed > loss {
|
|
modified_cells[cellx][celly] = 1;
|
|
}
|
|
}
|
|
}
|
|
let mut changed_pixels = Vec::new();
|
|
for x in 0..WIDTH {
|
|
for y in 0..HEIGHT {
|
|
let cellx = x / 8;
|
|
let celly = y / 8;
|
|
|
|
if modified_cells[cellx][celly] != 0 {
|
|
changed_pixels.push(frame[x][y]);
|
|
}
|
|
}
|
|
}
|
|
let mut modified_cells_flat = Vec::new();
|
|
for x in 0..CELLS_X {
|
|
for y in 0..CELLS_Y {
|
|
modified_cells_flat.push(modified_cells[x][y]);
|
|
}
|
|
}
|
|
let mut data = Vec::new();
|
|
data.extend_from_slice(&pack_nybbles(rle_encode(&modified_cells_flat, 15)));
|
|
data.extend_from_slice(&rle_255_encode(&changed_pixels));
|
|
|
|
EncodedFrame {
|
|
encoding: Encoding::CellDiff8VBig,
|
|
data,
|
|
}
|
|
}
|
|
|
|
pub fn cell_diff_8_vertical(prev_frame: &Frame, frame: &Frame, loss: usize) -> EncodedFrame {
|
|
const CELLS_X: usize = WIDTH / 8;
|
|
const CELLS_Y: usize = HEIGHT / 8;
|
|
|
|
let mut bitmap: u32 = 0;
|
|
|
|
for cellx in 0..CELLS_X {
|
|
for celly in 0..CELLS_Y {
|
|
let mut changed = 0;
|
|
for dx in 0..8 {
|
|
for dy in 0..8 {
|
|
let x = cellx * 8 + dx;
|
|
let y = celly * 8 + dy;
|
|
let pixel = frame[x][y];
|
|
|
|
if pixel != prev_frame[x][y] {
|
|
changed += 1;
|
|
}
|
|
}
|
|
}
|
|
if changed > loss {
|
|
bitmap |= 1 << (cellx + CELLS_X * celly);
|
|
}
|
|
}
|
|
}
|
|
let mut changed_pixels = Vec::new();
|
|
for x in 0..WIDTH {
|
|
for y in 0..HEIGHT {
|
|
let cellx = x / 8;
|
|
let celly = y / 8;
|
|
let bit = 1 << (cellx + CELLS_X * celly);
|
|
if (bitmap & bit) != 0 {
|
|
changed_pixels.push(frame[x][y]);
|
|
}
|
|
}
|
|
}
|
|
let mut data = vec![(bitmap >> 16) as u8, (bitmap >> 8) as u8, bitmap as u8];
|
|
data.extend_from_slice(&rle_255_encode(&changed_pixels));
|
|
EncodedFrame {
|
|
encoding: Encoding::CellDiff8V,
|
|
data,
|
|
}
|
|
}
|
|
|
|
// meant for 42x32 mode
|
|
pub fn bg_strips_horizontal_16(_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::BGStripsH16,
|
|
data: frame_bytes,
|
|
}
|
|
}
|
|
|
|
// meant for 320x240 mode
|
|
pub fn bg_strips_horizontal_24(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
|
|
let bg = most_common_pixel(frame);
|
|
fn pack_strip(x: usize, y: usize, width: usize) -> [u8; 3] {
|
|
// Y is 0..240 so will need 8 bits
|
|
// x is 0..320 so needs 9 bits
|
|
// 7 bits remain for width
|
|
let x_width = ((x >> 1) & 0x80) as u8 | (width as u8);
|
|
[y as u8, (x & 0xff) as u8, x_width]
|
|
}
|
|
|
|
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 == 127 {
|
|
strips.push((strip_start, y, x - strip_start));
|
|
strip_start = x;
|
|
}
|
|
// if strips.len() == MAX_STRIPS {
|
|
// break 'outer;
|
|
// }
|
|
}
|
|
}
|
|
if in_strip {
|
|
strips.push((strip_start, y, WIDTH - strip_start));
|
|
}
|
|
}
|
|
let mut frame_bytes = Vec::with_capacity(2 + strips.len() * 2);
|
|
frame_bytes.push(bg << 7 | ((strips.len() >> 8) as u8));
|
|
frame_bytes.push(strips.len() as u8);
|
|
for (x, y, width) in strips {
|
|
frame_bytes.extend_from_slice(&pack_strip(x, y, width));
|
|
}
|
|
EncodedFrame {
|
|
encoding: Encoding::BGStripsH24,
|
|
data: frame_bytes,
|
|
}
|
|
}
|
|
|
|
pub fn rle_diff_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] ^ prev_frame[x][y]);
|
|
}
|
|
}
|
|
EncodedFrame {
|
|
encoding: Encoding::RLEDiffHorizontal,
|
|
data: rle_255_encode(&pixels),
|
|
}
|
|
}
|
|
|
|
pub fn rle_diff_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] ^ prev_frame[x][y]);
|
|
}
|
|
}
|
|
EncodedFrame {
|
|
encoding: Encoding::RLEDiffVertical,
|
|
data: rle_255_encode(&pixels),
|
|
}
|
|
}
|
|
|
|
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,
|
|
data: rle_255_encode(&pixels),
|
|
}
|
|
}
|
|
|
|
/// requires WIDTH and HEIGHT to be multiples of 16
|
|
/// two-level tree with 4x4 branches per level
|
|
pub fn tree_16(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
|
|
let mut top_node: u16 = 0;
|
|
let mut second_nodes = Vec::new();
|
|
let mut included_pixels = Vec::new();
|
|
// todo allow using white as bg
|
|
let fg = 1;
|
|
for cx in 0..4 {
|
|
for cy in 0..4 {
|
|
let mut any_filled_here = false;
|
|
'a: for px in 0..(WIDTH / 4) {
|
|
let x = cx * (WIDTH / 4) + px;
|
|
for py in 0..(HEIGHT / 4) {
|
|
let y = cy * (HEIGHT / 4) + py;
|
|
if frame[x][y] == fg{
|
|
any_filled_here = true;
|
|
break 'a;
|
|
}
|
|
}
|
|
}
|
|
if any_filled_here {
|
|
top_node |= 1 << (cy * 4 + cx);
|
|
let mut second_node: u16 = 0;
|
|
for ccx in 0..4 {
|
|
for ccy in 0..4 {
|
|
let mut any_filled_here = false;
|
|
let mut node_pixels = Vec::new();
|
|
for px in 0..(WIDTH / 16) {
|
|
for py in 0..(HEIGHT / 16) {
|
|
let x = cx * (WIDTH / 4) + ccx * (WIDTH / 16) + px;
|
|
let y = cy * (HEIGHT / 4) + ccy * (HEIGHT / 16) + py;
|
|
if frame[x][y] == fg {
|
|
node_pixels.push(fg);
|
|
any_filled_here = true;
|
|
}else{
|
|
node_pixels.push(1-fg);
|
|
}
|
|
}
|
|
}
|
|
if any_filled_here {
|
|
second_node |= 1 << (ccy * 4 + ccx);
|
|
included_pixels.extend_from_slice(&node_pixels);
|
|
}
|
|
}
|
|
}
|
|
second_nodes.push(second_node);
|
|
}
|
|
}
|
|
}
|
|
let mut data = Vec::new();
|
|
data.push((top_node >>8) as u8);
|
|
data.push(top_node 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));
|
|
|
|
EncodedFrame {
|
|
encoding: Encoding::Tree16,
|
|
data,
|
|
}
|
|
}
|
|
|
|
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,
|
|
data: rle_255_encode(&pixels),
|
|
}
|
|
}
|
|
|
|
pub fn rle_vertical_ext(_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 < 256 {
|
|
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);
|
|
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::RLEVerticalExt,
|
|
data,
|
|
}
|
|
}
|
|
|
|
pub fn rle_vertical_16(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
|
|
let mut data = Vec::new();
|
|
let mut last_pixel = 0;
|
|
let mut run: u16 = 0;
|
|
for x in 0..WIDTH {
|
|
for y in 0..HEIGHT {
|
|
let pixel = frame[x][y];
|
|
if pixel != last_pixel || run == 0xffff {
|
|
data.push((run >> 8) as u8);
|
|
data.push((run & 0xff) as u8);
|
|
if run == 0xffff && pixel == last_pixel {
|
|
// inserting dummy run because we ran out of max len
|
|
data.push(0);
|
|
data.push(0);
|
|
}
|
|
run = 1;
|
|
} else {
|
|
run += 1;
|
|
}
|
|
last_pixel = pixel;
|
|
}
|
|
}
|
|
data.push((run >> 8) as u8);
|
|
data.push((run & 0xff) as u8);
|
|
EncodedFrame {
|
|
encoding: Encoding::RLEVertical16,
|
|
data,
|
|
}
|
|
}
|
|
|
|
pub fn fill_white(_prev_frame: &Frame, _frame: &Frame, _loss: usize) -> EncodedFrame {
|
|
EncodedFrame {
|
|
encoding: Encoding::FillWhite,
|
|
data: Vec::new(),
|
|
}
|
|
}
|
|
|
|
pub fn fill_black(_prev_frame: &Frame, _frame: &Frame, _loss: usize) -> EncodedFrame {
|
|
EncodedFrame {
|
|
encoding: Encoding::FillBlack,
|
|
data: Vec::new(),
|
|
}
|
|
}
|