Compare commits
2 commits
eca5344c66
...
1f61cebd50
Author | SHA1 | Date | |
---|---|---|---|
1f61cebd50 | |||
3af9be328c |
6 changed files with 424 additions and 58 deletions
155
encoder/Cargo.lock
generated
155
encoder/Cargo.lock
generated
|
@ -38,13 +38,6 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "compressor"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"image",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -54,6 +47,41 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoder"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"enum-map",
|
||||||
|
"image",
|
||||||
|
"num_enum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum-map"
|
||||||
|
version = "2.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9"
|
||||||
|
dependencies = [
|
||||||
|
"enum-map-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum-map-derive"
|
||||||
|
version = "0.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fdeflate"
|
name = "fdeflate"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
|
@ -73,6 +101,12 @@ dependencies = [
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "image"
|
name = "image"
|
||||||
version = "0.25.1"
|
version = "0.25.1"
|
||||||
|
@ -85,6 +119,22 @@ dependencies = [
|
||||||
"png",
|
"png",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.7.2"
|
version = "0.7.2"
|
||||||
|
@ -104,6 +154,27 @@ dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_enum"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
|
||||||
|
dependencies = [
|
||||||
|
"num_enum_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_enum_derive"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-crate",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "png"
|
name = "png"
|
||||||
version = "0.17.13"
|
version = "0.17.13"
|
||||||
|
@ -117,8 +188,78 @@ dependencies = [
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-crate"
|
||||||
|
version = "3.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
|
||||||
|
dependencies = [
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simd-adler32"
|
name = "simd-adler32"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.21.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.5.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
|
@ -6,4 +6,6 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
enum-map = "2.7.3"
|
||||||
image = { version = "0.25.1", default-features = false, features = ["png"] }
|
image = { version = "0.25.1", default-features = false, features = ["png"] }
|
||||||
|
num_enum = "0.7.2"
|
||||||
|
|
|
@ -1,7 +1,61 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub fn rle_horizontal(_prev_frame: &Frame, encoded: &[u8]) -> Frame {
|
pub fn bg_strips_horizontal(_prev_frame: &Frame, encoded: &[u8], reader: &mut usize) -> Frame {
|
||||||
let pixels = rle_255_decode(encoded);
|
*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;
|
||||||
|
let mut i = 0;
|
||||||
|
for y in 0..HEIGHT {
|
||||||
|
for x in 0..WIDTH {
|
||||||
|
frame[x][y] ^= decoded[i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
*reader += runs;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rle_horizontal(_prev_frame: &Frame, encoded: &[u8], reader: &mut usize) -> Frame {
|
||||||
|
let (runs, pixels) = rle_255_decode_until(encoded, FRAME_SIZE);
|
||||||
|
*reader += runs;
|
||||||
let mut frame = FRAME_0;
|
let mut frame = FRAME_0;
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
for y in 0..HEIGHT {
|
for y in 0..HEIGHT {
|
||||||
|
@ -13,8 +67,9 @@ pub fn rle_horizontal(_prev_frame: &Frame, encoded: &[u8]) -> Frame {
|
||||||
frame
|
frame
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rle_vertical(_prev_frame: &Frame, encoded: &[u8]) -> Frame {
|
pub fn rle_vertical(_prev_frame: &Frame, encoded: &[u8], reader: &mut usize) -> Frame {
|
||||||
let pixels = rle_255_decode(encoded);
|
let (runs, pixels) = rle_255_decode_until(encoded, FRAME_SIZE);
|
||||||
|
*reader += runs;
|
||||||
let mut frame = FRAME_0;
|
let mut frame = FRAME_0;
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
for x in 0..WIDTH {
|
for x in 0..WIDTH {
|
||||||
|
@ -26,10 +81,10 @@ pub fn rle_vertical(_prev_frame: &Frame, encoded: &[u8]) -> Frame {
|
||||||
frame
|
frame
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill_white(_prev_frame: &Frame, _encoded: &[u8]) -> Frame {
|
pub fn fill_white(_prev_frame: &Frame, _encoded: &[u8], _reader: &mut usize) -> Frame {
|
||||||
FRAME_1
|
FRAME_1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill_black(_prev_frame: &Frame, _encoded: &[u8]) -> Frame {
|
pub fn fill_black(_prev_frame: &Frame, _encoded: &[u8], _reader: &mut usize) -> Frame {
|
||||||
FRAME_0
|
FRAME_0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,84 @@
|
||||||
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 {
|
||||||
|
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 {
|
pub fn rle_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 {
|
||||||
|
@ -9,7 +88,6 @@ pub fn rle_horizontal(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
|
||||||
}
|
}
|
||||||
EncodedFrame {
|
EncodedFrame {
|
||||||
encoding: Encoding::RLEHorizontal,
|
encoding: Encoding::RLEHorizontal,
|
||||||
head_u4: 0,
|
|
||||||
data: rle_255_encode(&pixels),
|
data: rle_255_encode(&pixels),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +101,6 @@ pub fn rle_vertical(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
|
||||||
}
|
}
|
||||||
EncodedFrame {
|
EncodedFrame {
|
||||||
encoding: Encoding::RLEVertical,
|
encoding: Encoding::RLEVertical,
|
||||||
head_u4: 0,
|
|
||||||
data: rle_255_encode(&pixels),
|
data: rle_255_encode(&pixels),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +108,6 @@ pub fn rle_vertical(_prev_frame: &Frame, frame: &Frame) -> EncodedFrame {
|
||||||
pub fn fill_white(_prev_frame: &Frame, _frame: &Frame) -> EncodedFrame {
|
pub fn fill_white(_prev_frame: &Frame, _frame: &Frame) -> EncodedFrame {
|
||||||
EncodedFrame {
|
EncodedFrame {
|
||||||
encoding: Encoding::FillWhite,
|
encoding: Encoding::FillWhite,
|
||||||
head_u4: 0,
|
|
||||||
data: Vec::new(),
|
data: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +115,6 @@ pub fn fill_white(_prev_frame: &Frame, _frame: &Frame) -> EncodedFrame {
|
||||||
pub fn fill_black(_prev_frame: &Frame, _frame: &Frame) -> EncodedFrame {
|
pub fn fill_black(_prev_frame: &Frame, _frame: &Frame) -> EncodedFrame {
|
||||||
EncodedFrame {
|
EncodedFrame {
|
||||||
encoding: Encoding::FillBlack,
|
encoding: Encoding::FillBlack,
|
||||||
head_u4: 0,
|
|
||||||
data: Vec::new(),
|
data: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +1,84 @@
|
||||||
|
use enum_map::{Enum, EnumMap};
|
||||||
|
use num_enum::TryFromPrimitive;
|
||||||
|
|
||||||
mod dec;
|
mod dec;
|
||||||
mod enc;
|
mod enc;
|
||||||
mod util;
|
mod util;
|
||||||
pub use util::*;
|
pub use util::*;
|
||||||
|
|
||||||
const INTERACTIVE: bool = false;
|
const INSPECT_ENC: bool = false;
|
||||||
|
const INSPECT_DEC: bool = false;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let frames = get_all_frames("../video/frames/");
|
let frames = get_all_frames("../video/frames/");
|
||||||
let encoded = encode(&frames);
|
let encoded = encode(&frames);
|
||||||
println!("{} frames, total {} bytes", frames.len(), encoded.len());
|
|
||||||
//
|
// todo print average bytes per frame for each encoding type
|
||||||
|
let mut stats: EnumMap<Encoding, u32> = 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;
|
||||||
|
reader += 1;
|
||||||
|
let decoder = get_matching_decoder(frame_type);
|
||||||
|
last_frame = decoder(&last_frame, &encoded[reader..], &mut reader);
|
||||||
|
if INSPECT_DEC {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (encoding, frames) in stats.iter() {
|
||||||
|
println!("{encoding:?} - {frames} frames");
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
println!(
|
||||||
|
"{} frames, total {} bytes (~{} bpf)",
|
||||||
|
frames.len(),
|
||||||
|
encoded.len(),
|
||||||
|
encoded.len() / frames.len()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode(frames: &[Frame]) -> Vec<u8> {
|
fn encode(frames: &[Frame]) -> Vec<u8> {
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
let encodings: Vec<(FrameEncoder, FrameDecoder)> = vec![
|
let encodings: Vec<FrameEncoder> = vec![
|
||||||
(enc::fill_white, dec::fill_white),
|
enc::fill_white,
|
||||||
(enc::fill_black, dec::fill_black),
|
enc::fill_black,
|
||||||
(enc::rle_horizontal, dec::rle_horizontal),
|
enc::rle_horizontal,
|
||||||
(enc::rle_vertical, dec::rle_vertical),
|
enc::rle_vertical,
|
||||||
|
enc::rle_diff_horizontal,
|
||||||
|
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, decode) in &encodings {
|
for encode in &encodings {
|
||||||
let encoded = encode(&last_frame, frame);
|
let encoded = encode(&last_frame, frame);
|
||||||
let decoded = decode(&last_frame, &encoded.data);
|
let decode = get_matching_decoder(encoded.encoding);
|
||||||
|
let decoded = decode(&last_frame, &encoded.data, &mut 0);
|
||||||
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());
|
||||||
let best_encoding = options.into_iter().next().unwrap();
|
let best_encoding = options.into_iter().next().unwrap();
|
||||||
if INTERACTIVE {
|
if INSPECT_ENC {
|
||||||
println!();
|
println!();
|
||||||
println!(
|
println!(
|
||||||
"{:?}, {} bytes",
|
"{:?}, {} bytes",
|
||||||
|
@ -43,48 +86,69 @@ fn encode(frames: &[Frame]) -> Vec<u8> {
|
||||||
best_encoding.data.len() + 1
|
best_encoding.data.len() + 1
|
||||||
);
|
);
|
||||||
render_image(frame);
|
render_image(frame);
|
||||||
let mut a = String::new();
|
wait_for_input();
|
||||||
std::io::stdin().read_line(&mut a).unwrap();
|
|
||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, TryFromPrimitive, Enum, Copy, Clone)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
enum Encoding {
|
enum Encoding {
|
||||||
FillWhite,
|
FillWhite,
|
||||||
FillBlack,
|
FillBlack,
|
||||||
RLEHorizontal,
|
RLEHorizontal,
|
||||||
RLEVertical,
|
RLEVertical,
|
||||||
BGStrips,
|
RLEDiffHorizontal,
|
||||||
CellDiff8Horizontal,
|
RLEDiffVertical,
|
||||||
CellDiff8Vertical,
|
BGStripsH,
|
||||||
CellDiff4HH,
|
// BGStripsV,
|
||||||
CellDiff4HV,
|
// QuadTree,
|
||||||
CellDiff4VH,
|
// DrawCommands,
|
||||||
CellDiff4VV,
|
// CellDiff8H,
|
||||||
|
// CellDiff8V,
|
||||||
|
// CellDiff4HH,
|
||||||
|
// CellDiff4HV,
|
||||||
|
// CellDiff4VH,
|
||||||
|
// CellDiff4VV,
|
||||||
}
|
}
|
||||||
type FrameEncoder = fn(previous_frame: &Frame, new_frame: &Frame) -> EncodedFrame;
|
|
||||||
type FrameDecoder = fn(previous_frame: &Frame, encoded_bytes: &[u8]) -> Frame;
|
|
||||||
|
|
||||||
|
fn get_matching_decoder(encoding: Encoding) -> FrameDecoder {
|
||||||
|
match encoding {
|
||||||
|
Encoding::FillWhite => dec::fill_white,
|
||||||
|
Encoding::FillBlack => dec::fill_black,
|
||||||
|
Encoding::RLEHorizontal => dec::rle_horizontal,
|
||||||
|
Encoding::RLEVertical => dec::rle_vertical,
|
||||||
|
Encoding::RLEDiffHorizontal => dec::rle_diff_horizontal,
|
||||||
|
Encoding::RLEDiffVertical => dec::rle_diff_vertical,
|
||||||
|
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 {
|
struct EncodedFrame {
|
||||||
encoding: Encoding,
|
encoding: Encoding,
|
||||||
head_u4: u8,
|
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EncodedFrame {
|
impl EncodedFrame {
|
||||||
fn into_bytes(self) -> Vec<u8> {
|
fn into_bytes(mut self) -> Vec<u8> {
|
||||||
let head = (self.encoding as u8) << 4;
|
self.data.insert(0, self.encoding as u8);
|
||||||
let head = head | (self.head_u4 & 15);
|
self.data
|
||||||
|
|
||||||
let mut out = Vec::with_capacity(self.data.len() + 1);
|
|
||||||
out.push(head);
|
|
||||||
out.extend(self.data.into_iter());
|
|
||||||
out
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
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};
|
||||||
|
|
||||||
pub const WIDTH: usize = 40;
|
pub const WIDTH: usize = 40;
|
||||||
pub const HEIGHT: usize = 32;
|
pub const HEIGHT: usize = 32;
|
||||||
pub const SIZE: usize = WIDTH * HEIGHT;
|
pub const FRAME_SIZE: usize = WIDTH * HEIGHT;
|
||||||
|
|
||||||
pub type Frame = [[u8; HEIGHT]; WIDTH];
|
pub type Frame = [[u8; HEIGHT]; WIDTH];
|
||||||
pub const FRAME_0: Frame = [[0; HEIGHT]; WIDTH];
|
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() {
|
||||||
|
stdin().read_line(&mut String::new()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
fn convert_pixel(rgba: Rgba<u8>) -> u8 {
|
fn convert_pixel(rgba: Rgba<u8>) -> u8 {
|
||||||
(rgba.0[0] > 128) as u8
|
(rgba.0[0] > 128) as u8
|
||||||
}
|
}
|
||||||
|
@ -51,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) => " ",
|
||||||
|
@ -73,12 +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 {
|
||||||
render_pixel_pair(right, x, y);
|
render_pixel_pair(right, x, y);
|
||||||
}
|
}
|
||||||
println!();
|
println!("|");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,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;
|
||||||
|
@ -102,14 +126,19 @@ pub fn rle_255_encode(raw: &[u8]) -> Vec<u8> {
|
||||||
encoded
|
encoded
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rle_255_decode(encoded: &[u8]) -> Vec<u8> {
|
pub fn rle_255_decode_until(encoded: &[u8], max_size: usize) -> (usize, Vec<u8>) {
|
||||||
let mut raw = Vec::new();
|
let mut raw = Vec::new();
|
||||||
let mut val = 0;
|
let mut val = 0;
|
||||||
|
let mut consumed_bytes = 0;
|
||||||
for &run in encoded {
|
for &run in encoded {
|
||||||
|
consumed_bytes += 1;
|
||||||
for _ in 0..run {
|
for _ in 0..run {
|
||||||
raw.push(val);
|
raw.push(val);
|
||||||
}
|
}
|
||||||
|
if raw.len() >= max_size {
|
||||||
|
break;
|
||||||
|
}
|
||||||
val = 1 - val;
|
val = 1 - val;
|
||||||
}
|
}
|
||||||
raw
|
(consumed_bytes, raw)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue