aboutsummaryrefslogtreecommitdiff
path: root/old/evc/src/codec
diff options
context:
space:
mode:
Diffstat (limited to 'old/evc/src/codec')
-rw-r--r--old/evc/src/codec/compress.rs155
-rw-r--r--old/evc/src/codec/decode.rs34
-rw-r--r--old/evc/src/codec/encode/advanced.rs115
-rw-r--r--old/evc/src/codec/encode/mod.rs112
-rw-r--r--old/evc/src/codec/encode/simple.rs65
-rw-r--r--old/evc/src/codec/mod.rs3
6 files changed, 484 insertions, 0 deletions
diff --git a/old/evc/src/codec/compress.rs b/old/evc/src/codec/compress.rs
new file mode 100644
index 0000000..09d1f29
--- /dev/null
+++ b/old/evc/src/codec/compress.rs
@@ -0,0 +1,155 @@
+use crate::{
+ block::Block,
+ frame::Frame,
+ helpers::{pixel::Pixel, vector::Vec2},
+ view::View,
+};
+use rustdct::DctPlanner;
+
+pub fn compress_block(block: &mut Block, size: Vec2<usize>) {
+ match block {
+ Block::Literal(p) => {
+ let comp = lit_compress(size.x, size.y, &p);
+ *block = Block::CompressedLiteral(comp)
+ }
+ Block::Split(box [a, b]) => {
+ let vert = size.x > size.y;
+ compress_block(
+ a,
+ if vert {
+ Vec2 {
+ x: size.x / 2,
+ y: size.y,
+ }
+ } else {
+ Vec2 {
+ x: size.x,
+ y: size.y / 2,
+ }
+ },
+ );
+ compress_block(
+ b,
+ if vert {
+ Vec2 {
+ x: size.x - size.x / 2,
+ y: size.y,
+ }
+ } else {
+ Vec2 {
+ x: size.x,
+ y: size.y - size.y / 2,
+ }
+ },
+ );
+ }
+ _ => (),
+ }
+}
+
+pub fn lit_compress(w: usize, h: usize, pixels: &[Pixel]) -> Vec<u8> {
+ let mut out = vec![];
+ for ci in 0..3 {
+ let mut ch = vec![0.; w * h];
+ for y in 0..h {
+ for x in 0..w {
+ let p = pixels[x + y * w];
+ ch[x + y * w] = match ci {
+ 0 => p.r,
+ 1 => p.g,
+ 2 => p.b,
+ _ => unreachable!(),
+ } as f32
+ }
+ }
+
+ norm_dct_channel(w, h, &mut ch);
+ for i in 0..w * h {
+ out.extend(unsafe { std::mem::transmute::<_, [u8; 4]>(ch[i]) });
+ }
+ }
+ out
+}
+
+pub fn lit_decompress(compressed: &[u8], mut target: View<&mut Frame>) {
+ let (w, h) = (target.size.x as usize, target.size.y as usize);
+ for ci in 0..3 {
+ let mut ch = compressed[ci * w * h..(ci + 1) * w * h]
+ .chunks_exact(4)
+ .map(|v| unsafe {
+ std::mem::transmute::<_, f32>(TryInto::<[u8; 4]>::try_into(v).unwrap())
+ })
+ .collect::<Vec<_>>();
+ norm_idct_channel(w, h, &mut ch);
+ for y in 0..h {
+ for x in 0..w {
+ let p = &mut target[Vec2 {
+ x: x as isize,
+ y: y as isize,
+ }];
+ *(match ci {
+ 0 => &mut p.r,
+ 1 => &mut p.g,
+ 2 => &mut p.b,
+ _ => unreachable!(),
+ }) = ch[x + y * w] as u8
+ }
+ }
+ }
+}
+
+fn norm_dct_channel(w: usize, h: usize, values: &mut [f32]) {
+ let d = DctPlanner::new().plan_dct2(w);
+ let mut temp = vec![0.; d.get_scratch_len()];
+ for c in values.chunks_exact_mut(w) {
+ d.process_dct2_with_scratch(c, &mut temp)
+ }
+
+ let mut values_trans = transpose(w, h, values);
+ let d = DctPlanner::new().plan_dct2(h);
+ let mut temp = vec![0.; d.get_scratch_len()];
+ for c in values_trans.chunks_exact_mut(h) {
+ d.process_dct2_with_scratch(c, &mut temp)
+ }
+
+ let scale = 2. / (w as f32 * h as f32).sqrt();
+ for (i, v) in values_trans.iter().enumerate() {
+ values[i] = *v * scale * 127.0
+ }
+}
+
+fn norm_idct_channel(h: usize, w: usize, values: &mut [f32]) {
+ let scale = 2. / (w as f32 * h as f32).sqrt();
+ for v in values.iter_mut() {
+ *v /= scale * 127.0
+ }
+
+ let d = DctPlanner::new().plan_dct2(w);
+ let mut temp = vec![0.; d.get_scratch_len()];
+ for c in values.chunks_exact_mut(w) {
+ d.process_dct3_with_scratch(c, &mut temp)
+ }
+
+ let mut values_trans = transpose(w, h, values);
+ let d = DctPlanner::new().plan_dct2(h);
+ let mut temp = vec![0.; d.get_scratch_len()];
+ for c in values_trans.chunks_exact_mut(h) {
+ d.process_dct3_with_scratch(c, &mut temp)
+ }
+ values.copy_from_slice(&values_trans)
+}
+
+fn transpose(w: usize, h: usize, values: &[f32]) -> Vec<f32> {
+ let mut io = 0;
+ let mut it;
+ let mut transposed = vec![0.; values.len()];
+ for row in 0..h {
+ it = row;
+ for _ in 0..w {
+ transposed[it] = values[io];
+ io += 1;
+ it += h;
+ }
+ }
+ transposed
+}
diff --git a/old/evc/src/codec/decode.rs b/old/evc/src/codec/decode.rs
new file mode 100644
index 0000000..c042278
--- /dev/null
+++ b/old/evc/src/codec/decode.rs
@@ -0,0 +1,34 @@
+use super::compress::lit_decompress;
+use crate::{block::Block, frame::Frame, refsampler::Sampler, view::View};
+
+pub struct DecodeConfig {}
+
+pub fn decode_block(
+ block: &Block,
+ mut target: View<&mut Frame>,
+ prev: View<&Frame>,
+ config: &DecodeConfig,
+) {
+ match &block {
+ Block::Literal(pixels) => target.set_pixels(pixels),
+ Block::Split(box [a, b]) => {
+ let [a, b] = unsafe { std::mem::transmute::<_, [&'static Block; 2]>([a, b]) };
+ let [at, bt] = unsafe {
+ std::mem::transmute::<_, [View<&'static mut Frame>; 2]>(target.split_mut_unsafe())
+ };
+ let [ap, bp] =
+ unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(prev.split()) };
+ let config = unsafe { std::mem::transmute::<_, &'static DecodeConfig>(config) };
+
+ rayon::join(
+ move || decode_block(a, at, ap, config),
+ move || decode_block(b, bt, bp, config),
+ );
+ }
+ Block::CompressedLiteral(data) => {
+ lit_decompress(&data, target);
+ }
+ Block::Reference { translation } => target.copy_from(&prev.offset(*translation)),
+ Block::AdvancedReference(r) => target.copy_from_sampler(&Sampler::from_refblock(prev, r)),
+ }
+}
diff --git a/old/evc/src/codec/encode/advanced.rs b/old/evc/src/codec/encode/advanced.rs
new file mode 100644
index 0000000..0e45176
--- /dev/null
+++ b/old/evc/src/codec/encode/advanced.rs
@@ -0,0 +1,115 @@
+use super::EncodeConfig;
+use crate::{
+ block::{AdvancedReference, Block},
+ frame::Frame,
+ refsampler::Sampler,
+ view::View,
+};
+
+pub fn default(
+ view: &View<&Frame>,
+ prev: &View<&Frame>,
+ config: &EncodeConfig,
+ max_diff: f64,
+) -> (f64, Block) {
+ let mut pm = AdvancedReference::default();
+ let sampler = Sampler::from_refblock(prev.clone(), &pm);
+ let mut diff = View::diff_sampler(&view, &sampler);
+ if diff < max_diff {
+ (diff, Block::REFZERO)
+ } else {
+ loop {
+ let (mut d, mut p) = (diff, pm.clone());
+
+ if config.do_translate {
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 4);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 4);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 4);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 4);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 1);
+ }
+ if config.do_value_scale {
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale -= 1);
+ }
+ if config.do_linear_transform {
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.a -= 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.a += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.b -= 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.b += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.c -= 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.c += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.d -= 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.d += 1);
+ }
+
+ if d >= diff {
+ break (diff, Block::AdvancedReference(pm));
+ }
+
+ diff = d;
+ pm = p;
+ }
+ }
+}
+
+pub fn partial(
+ view: &View<&Frame>,
+ prev: &View<&Frame>,
+ _config: &EncodeConfig,
+ max_diff: f64,
+) -> (f64, Block) {
+ let mut pm = AdvancedReference::default();
+ let sampler = Sampler::from_refblock(prev.clone(), &pm);
+ let mut diff = View::diff_sampler(&view, &sampler);
+ if diff < max_diff {
+ (diff, Block::REFZERO)
+ } else {
+ loop {
+ let (mut d, mut p) = (diff, pm.clone());
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 2);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 1);
+
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale += 1);
+ pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale -= 1);
+
+ if d >= diff {
+ break (diff, Block::AdvancedReference(pm));
+ }
+ diff = d;
+ pm = p;
+ }
+ }
+}
+
+#[inline]
+fn pk<F: FnMut(&mut AdvancedReference) -> ()>(
+ view: &View<&Frame>,
+ prev: &View<&Frame>,
+ diff: &mut f64,
+ params: &mut AdvancedReference,
+ initial_params: &AdvancedReference,
+ mut f: F,
+) {
+ let mut p = initial_params.clone();
+ f(&mut p);
+ let sampler = Sampler::from_refblock(prev.clone(), &p);
+ let d = View::diff_sampler(view, &sampler);
+ if d < *diff {
+ *diff = d;
+ *params = p;
+ }
+}
diff --git a/old/evc/src/codec/encode/mod.rs b/old/evc/src/codec/encode/mod.rs
new file mode 100644
index 0000000..30342f0
--- /dev/null
+++ b/old/evc/src/codec/encode/mod.rs
@@ -0,0 +1,112 @@
+pub mod advanced;
+pub mod simple;
+
+use crate::{
+ block::Block,
+ frame::Frame,
+ helpers::{pixel::Pixel, vector::Vec2},
+ view::View,
+};
+use clap::ValueEnum;
+
+#[derive(Debug, Clone)]
+pub struct EncodeConfig {
+ pub mode: EncodeMode,
+ pub ref_thres: f64,
+ pub max_diff_area: isize,
+ pub min_block_size: isize,
+ pub weight_factor: f64,
+
+ pub do_translate: bool,
+ pub do_value_scale: bool,
+ pub do_linear_transform: bool,
+}
+
+#[derive(Debug, Clone, ValueEnum)]
+pub enum EncodeMode {
+ Trivial,
+ SimpleFast,
+ SimpleExhaustive,
+ Advanced,
+ AdvancedPartial,
+}
+
+pub fn encode_block(view: View<&Frame>, prev: View<&Frame>, config: &EncodeConfig) -> (f64, Block) {
+ let (diff, refblock) = if view.area() > config.max_diff_area {
+ (
+ f64::INFINITY,
+ Block::Reference {
+ translation: Vec2::<isize>::ZERO,
+ },
+ )
+ } else {
+ let weight = importance(&view).max(0.5);
+ let irrelevance = config.weight_factor / weight;
+ let max_diff = config.ref_thres - irrelevance;
+ let (diff, refblock) = match config.mode {
+ EncodeMode::Trivial => (
+ View::diff(&view, &prev),
+ Block::Reference {
+ translation: Vec2::<isize>::ZERO,
+ },
+ ),
+ EncodeMode::SimpleExhaustive => simple::exhaustive(&view, &prev, config, max_diff),
+ EncodeMode::SimpleFast => simple::fast(&view, &prev, config, max_diff),
+ EncodeMode::Advanced => advanced::default(&view, &prev, config, max_diff),
+ EncodeMode::AdvancedPartial => advanced::partial(&view, &prev, config, max_diff),
+ };
+ (diff - irrelevance, refblock)
+ };
+ if diff < config.ref_thres {
+ (diff, refblock)
+ } else {
+ if view.size.x < config.min_block_size || view.size.y < config.min_block_size {
+ (0.0, Block::Literal(view.pixels()))
+ } else {
+ let [av, bv] =
+ unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(view.split()) };
+ let [ap, bp] =
+ unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(prev.split()) };
+ let config = unsafe { std::mem::transmute::<_, &'static EncodeConfig>(config) };
+
+ // only bother to do multithreading, when the block is big.
+ let ((ad, a), (bd, b)) = if view.area() > 100 {
+ rayon::join(
+ || encode_block(av, ap, config),
+ || encode_block(bv, bp, config),
+ )
+ } else {
+ (encode_block(av, ap, config), encode_block(bv, bp, config))
+ };
+
+ (
+ ad + bd,
+ if a.is_literal() && b.is_literal() {
+ Block::Literal(view.pixels())
+ } else if Block::identical_ref(&a, &b) {
+ Block::Reference {
+ translation: Vec2::<isize>::ZERO,
+ }
+ } else {
+ Block::Split(Box::new([a, b]))
+ },
+ )
+ }
+ }
+}
+
+pub fn importance(view: &View<&Frame>) -> f64 {
+ let mut acc = 0;
+ for x in 0..view.size.x {
+ for y in 0..view.size.y {
+ let p = Vec2 { x, y };
+ if x > 0 {
+ acc += Pixel::distance(view[p], view[p + Vec2::<isize>::LEFT]);
+ }
+ if y > 0 {
+ acc += Pixel::distance(view[p], view[p + Vec2::<isize>::UP]);
+ }
+ }
+ }
+ (acc / view.area() as usize) as f64
+}
diff --git a/old/evc/src/codec/encode/simple.rs b/old/evc/src/codec/encode/simple.rs
new file mode 100644
index 0000000..2a971af
--- /dev/null
+++ b/old/evc/src/codec/encode/simple.rs
@@ -0,0 +1,65 @@
+use crate::{block::Block, frame::Frame, helpers::vector::Vec2, view::View};
+
+use super::EncodeConfig;
+
+pub fn exhaustive(
+ view: &View<&Frame>,
+ prev: &View<&Frame>,
+ _config: &EncodeConfig,
+ _max_diff: f64,
+) -> (f64, Block) {
+ let mut diff = f64::INFINITY;
+ let mut translation = Vec2::<isize>::ZERO;
+ const OFFSETS: &[isize] = &[-64, -32, -16, -8, -4, -2, -1, 0, 1, 2, 4, 8, 16, 32, 64];
+ for x in OFFSETS {
+ for y in OFFSETS {
+ let t = Vec2 { x: *x, y: *y };
+ let d = View::diff(&view, &prev.offset(t));
+ if d < diff {
+ translation = t;
+ diff = d;
+ }
+ }
+ }
+ (diff, Block::Reference { translation })
+}
+
+pub fn fast(
+ view: &View<&Frame>,
+ prev: &View<&Frame>,
+ _config: &EncodeConfig,
+ max_diff: f64,
+) -> (f64, Block) {
+ let mut offset = Vec2::<isize>::ZERO;
+ let mut diff = View::diff(&view, &prev);
+ if diff < max_diff {
+ (diff, Block::REFZERO)
+ } else {
+ loop {
+ let (mut d, mut o) = (diff, offset);
+ let mut probe = |test_o: Vec2<isize>| {
+ let test_d = View::diff(&prev.clone().offset(test_o), &view);
+ if test_d < d {
+ d = test_d;
+ o = test_o;
+ }
+ };
+
+ probe(offset + Vec2 { x: 8, y: 0 });
+ probe(offset + Vec2 { x: -8, y: 0 });
+ probe(offset + Vec2 { x: 0, y: 8 });
+ probe(offset + Vec2 { x: 0, y: -8 });
+
+ probe(offset + Vec2 { x: 1, y: 0 });
+ probe(offset + Vec2 { x: -1, y: 0 });
+ probe(offset + Vec2 { x: 0, y: 1 });
+ probe(offset + Vec2 { x: 0, y: -1 });
+
+ if d >= diff {
+ break (diff, Block::Reference { translation: o });
+ }
+ diff = d;
+ offset = o;
+ }
+ }
+}
diff --git a/old/evc/src/codec/mod.rs b/old/evc/src/codec/mod.rs
new file mode 100644
index 0000000..3203e6e
--- /dev/null
+++ b/old/evc/src/codec/mod.rs
@@ -0,0 +1,3 @@
+pub mod decode;
+pub mod encode;
+pub mod compress;