aboutsummaryrefslogtreecommitdiff
path: root/evc/src/codec/encode/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'evc/src/codec/encode/mod.rs')
-rw-r--r--evc/src/codec/encode/mod.rs105
1 files changed, 105 insertions, 0 deletions
diff --git a/evc/src/codec/encode/mod.rs b/evc/src/codec/encode/mod.rs
new file mode 100644
index 0000000..2ccac64
--- /dev/null
+++ b/evc/src/codec/encode/mod.rs
@@ -0,0 +1,105 @@
+pub mod advanced;
+pub mod simple;
+
+use crate::{
+ block::Block,
+ frame::Frame,
+ helpers::{pixel::Pixel, threading::both_par, 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 max_threads: usize,
+ pub weight_factor: f64,
+ pub do_value_scale: bool,
+ pub do_matrix_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) -> 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 {
+ refblock
+ } else {
+ if view.size.x < config.min_block_size || view.size.y < config.min_block_size {
+ 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 (a, b) = both_par(
+ || encode_block(av, ap, config),
+ || encode_block(bv, bp, config),
+ config.max_threads,
+ );
+
+ if a.is_literal() && b.is_literal() {
+ Block::Literal(view.pixels())
+ } else if a.is_ref_without_translation() && b.is_ref_without_translation() {
+ 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
+}