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_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::::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::::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 { both_par( || encode_block(av, ap, config), || encode_block(bv, bp, config), config.max_threads, ) } 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::::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::::LEFT]); } if y > 0 { acc += Pixel::distance(view[p], view[p + Vec2::::UP]); } } } (acc / view.area() as usize) as f64 }