From 306f96164784a8cbf405e72fa4364d6523366e95 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Mon, 5 May 2025 15:09:54 +0200 Subject: old dir --- old/evc/src/bin/decode.rs | 63 +++++++ old/evc/src/bin/encode.rs | 111 ++++++++++++ old/evc/src/bin/info.rs | 10 ++ old/evc/src/block.rs | 123 +++++++++++++ old/evc/src/codec/compress.rs | 155 ++++++++++++++++ old/evc/src/codec/decode.rs | 34 ++++ old/evc/src/codec/encode/advanced.rs | 115 ++++++++++++ old/evc/src/codec/encode/mod.rs | 112 ++++++++++++ old/evc/src/codec/encode/simple.rs | 65 +++++++ old/evc/src/codec/mod.rs | 3 + old/evc/src/debug.rs | 103 +++++++++++ old/evc/src/format/header.rs | 29 +++ old/evc/src/format/mod.rs | 2 + old/evc/src/format/ser.rs | 335 +++++++++++++++++++++++++++++++++++ old/evc/src/frame.rs | 109 ++++++++++++ old/evc/src/helpers/matrix.rs | 33 ++++ old/evc/src/helpers/mod.rs | 4 + old/evc/src/helpers/pixel.rs | 81 +++++++++ old/evc/src/helpers/threading.rs | 26 +++ old/evc/src/helpers/vector.rs | 126 +++++++++++++ old/evc/src/lib.rs | 13 ++ old/evc/src/refsampler.rs | 51 ++++++ old/evc/src/view.rs | 241 +++++++++++++++++++++++++ 23 files changed, 1944 insertions(+) create mode 100644 old/evc/src/bin/decode.rs create mode 100644 old/evc/src/bin/encode.rs create mode 100644 old/evc/src/bin/info.rs create mode 100644 old/evc/src/block.rs create mode 100644 old/evc/src/codec/compress.rs create mode 100644 old/evc/src/codec/decode.rs create mode 100644 old/evc/src/codec/encode/advanced.rs create mode 100644 old/evc/src/codec/encode/mod.rs create mode 100644 old/evc/src/codec/encode/simple.rs create mode 100644 old/evc/src/codec/mod.rs create mode 100644 old/evc/src/debug.rs create mode 100644 old/evc/src/format/header.rs create mode 100644 old/evc/src/format/mod.rs create mode 100644 old/evc/src/format/ser.rs create mode 100644 old/evc/src/frame.rs create mode 100644 old/evc/src/helpers/matrix.rs create mode 100644 old/evc/src/helpers/mod.rs create mode 100644 old/evc/src/helpers/pixel.rs create mode 100644 old/evc/src/helpers/threading.rs create mode 100644 old/evc/src/helpers/vector.rs create mode 100644 old/evc/src/lib.rs create mode 100644 old/evc/src/refsampler.rs create mode 100644 old/evc/src/view.rs (limited to 'old/evc/src') diff --git a/old/evc/src/bin/decode.rs b/old/evc/src/bin/decode.rs new file mode 100644 index 0000000..fbf5624 --- /dev/null +++ b/old/evc/src/bin/decode.rs @@ -0,0 +1,63 @@ +#![feature(box_patterns)] +use anyhow::Context; +use clap::Parser; +use indicatif::ProgressBar; +use libreschmux::{ + block::Block, + codec::decode::{decode_block, DecodeConfig}, + debug::draw_debug, + format::{header::Header, ser::Source}, + frame::Frame, +}; +use log::{info, warn}; +use std::io::{BufReader, BufWriter}; + +#[derive(Parser)] +#[clap(about, version)] +pub struct DecodeArgs { + #[arg(long)] + debug: bool, +} + +fn main() -> anyhow::Result<()> { + env_logger::init_from_env("LOG"); + let args = DecodeArgs::parse(); + + let mut input = BufReader::new(std::io::stdin()); + let mut output = BufWriter::new(std::io::stdout()); + + let header = input.get::
().context("reading header")?; + info!("{header:?}"); + if header.resolution.x * header.resolution.y > 100_000_000 { + warn!("resolution is quite big. video is likely corrupt."); + } + let size = header.resolution; + + let config = DecodeConfig {}; + + let progress_bar = ProgressBar::new(header.frame_count as u64); + + let mut prev = Frame::new(size); + for i in 0..header.frame_count { + info!("decode frame {i}"); + + let block = Block::read(&mut input, size).context("reading encoded frame")?; + let mut frame = Frame::new(size); + + decode_block(&block, frame.view_mut(), prev.view(), &config); + + progress_bar.inc(1); + + if args.debug { + let mut f2 = frame.clone(); + draw_debug(&block, f2.view_mut()); + f2.write(&mut output).context("writing raw frame")?; + } else { + frame.write(&mut output).context("writing raw frame")?; + } + + prev = frame; + } + drop(input); + Ok(()) +} diff --git a/old/evc/src/bin/encode.rs b/old/evc/src/bin/encode.rs new file mode 100644 index 0000000..43f2c57 --- /dev/null +++ b/old/evc/src/bin/encode.rs @@ -0,0 +1,111 @@ +use anyhow::Context; +use clap::Parser; +use indicatif::ProgressBar; +use libreschmux::{ + codec::{ + decode::{decode_block, DecodeConfig}, + encode::{encode_block, EncodeConfig, EncodeMode}, + }, + format::{header::Header, ser::Sink}, + frame::Frame, + helpers::vector::Vec2, +}; +use log::info; +use std::io::{BufReader, BufWriter}; + +#[derive(Parser)] +#[clap(about, version)] +pub struct EncodeArgs { + #[arg(short = 'W', long)] + width: usize, + #[arg(short = 'H', long)] + height: usize, + + #[arg(short, long)] + mode: EncodeMode, + + #[arg(long)] + no_linear_transform: bool, + #[arg(long)] + no_value_scale: bool, + #[arg(long)] + no_translate: bool, + + #[arg[short = 'L', long]] + length: Option, + + #[arg(short, long, default_value = "8")] + min_block_size: isize, + + #[arg(short = 't', long, default_value = "200")] + ref_thres: f64, +} + +fn main() -> anyhow::Result<()> { + env_logger::init_from_env("LOG"); + let args = EncodeArgs::parse(); + + let mut input = BufReader::new(std::io::stdin()); + let mut output = BufWriter::new(std::io::stdout()); + + let config = EncodeConfig { + mode: args.mode, + ref_thres: args.ref_thres, + weight_factor: 50.0, + max_diff_area: 10_000, + min_block_size: args.min_block_size, + do_translate: !args.no_translate, + do_linear_transform: !args.no_linear_transform, + do_value_scale: !args.no_value_scale, + }; + + let size = Vec2 { + x: args.width as isize, + y: args.height as isize, + }; + output + .put(Header { + resolution: size, + frame_count: args.length.unwrap_or(usize::MAX), + }) + .context("writing header")?; + + let progress_bar = args.length.map(|len| ProgressBar::new(len as u64)); + let mut prev_frame = Frame::new(size); + + for i in 0.. { + info!("encode frame {i}"); + let mut frame = Frame::read(&mut input, size).context("reading raw frame")?; + + let v1 = frame.view(); + let v2 = prev_frame.view(); + + let (error, root) = encode_block(v1, v2, &config); + + // compress_block( + // &mut root, + // Vec2 { + // x: size.x as usize, + // y: size.y as usize, + // }, + // ); + + root.write(&mut output, size) + .context("writing encoded frame")?; + + info!( + "cumulative error: {error} ({} per pixel)", + error / frame.view().area() as f64 + ); + + if let Some(progress_bar) = &progress_bar { + progress_bar.inc(1); + } + + decode_block(&root, frame.view_mut(), prev_frame.view(), &DecodeConfig {}); + + prev_frame = frame; + } + + Ok(()) +} diff --git a/old/evc/src/bin/info.rs b/old/evc/src/bin/info.rs new file mode 100644 index 0000000..ebd8442 --- /dev/null +++ b/old/evc/src/bin/info.rs @@ -0,0 +1,10 @@ +use anyhow::Context; +use libreschmux::format::{header::Header, ser::Source}; +use std::io::BufReader; + +fn main() { + env_logger::init_from_env("LOG"); + let mut input = BufReader::new(std::io::stdin()); + let header = input.get::
().context("reading header").unwrap(); + eprintln!("{header:#?}") +} diff --git a/old/evc/src/block.rs b/old/evc/src/block.rs new file mode 100644 index 0000000..d0f940b --- /dev/null +++ b/old/evc/src/block.rs @@ -0,0 +1,123 @@ +use crate::{ + format::ser::{ConstSizeSerExt, Ser, Sink, Small, Source}, + helpers::vector::Vec2, + helpers::{matrix::Mat2, pixel::Pixel}, +}; +use anyhow::bail; + +#[derive(Clone, Debug, PartialEq)] +pub enum Block { + Literal(Vec), + CompressedLiteral(Vec), + Split(Box<[Block; 2]>), + Reference { translation: Vec2 }, + AdvancedReference(AdvancedReference), +} + +#[derive(Clone, Debug, PartialEq)] +pub struct AdvancedReference { + pub translation: Vec2, + pub transform: Mat2, + pub value_scale: i8, +} + +impl Block { + pub const REFZERO: Block = Block::Reference { + translation: Vec2::::ZERO, + }; + + pub fn write(&self, sink: &mut impl std::io::Write, size: Vec2) -> anyhow::Result<()> { + match &self { + Block::Literal(pixels) => { + sink.put(0u8)?; + pixels.write_const_size(sink, size.area() as usize)?; + } + Block::CompressedLiteral(data) => { + sink.put(1u8)?; + data.write(sink)?; + } + Block::Split(box [a, b]) => { + sink.put(2u8)?; + let [asize, bsize] = split_size(size); + a.write(sink, asize)?; + b.write(sink, bsize)?; + } + Block::Reference { translation } => { + sink.put(3u8)?; + sink.put(Small(*translation))?; + } + Block::AdvancedReference(AdvancedReference { + translation, + transform, + value_scale, + }) => { + sink.put(4u8)?; + sink.put((*translation, *transform, *value_scale))?; + } + } + Ok(()) + } + + pub fn read(source: &mut impl std::io::Read, size: Vec2) -> anyhow::Result { + let variant = source.get::()?; + Ok(match variant { + 0 => Block::Literal(Vec::read_const_size(source, size.area() as usize)?), + 1 => Block::CompressedLiteral(Vec::read(source)?), + 2 => Block::Split(Box::new({ + let [asize, bsize] = split_size(size); + let a = Block::read(source, asize)?; + let b = Block::read(source, bsize)?; + [a, b] + })), + 3 => Block::Reference { + translation: source.get::>>()?.0, + }, + 4 => Block::AdvancedReference(AdvancedReference { + translation: source.get()?, + transform: source.get()?, + value_scale: source.get()?, + }), + x => bail!("corrupt block type ({})", x), + }) + } +} + +pub fn split_size(size: Vec2) -> [Vec2; 2] { + let vert = size.x > size.y; + [ + if vert { + (size.x / 2, size.y).into() + } else { + (size.x, size.y / 2).into() + }, + if vert { + (size.x - size.x / 2, size.y).into() + } else { + (size.x, size.y - size.y / 2).into() + }, + ] +} + +impl Block { + pub fn is_literal(&self) -> bool { + matches!(self, Block::Literal(..)) + } + pub fn identical_ref(a: &Self, b: &Self) -> bool { + matches!(a, Block::Reference { .. }) && a == b + } +} + +impl Default for AdvancedReference { + fn default() -> Self { + Self { + translation: Vec2 { x: 0, y: 0 }, + transform: Mat2 { + a: 4, + b: 0, + c: 0, + d: 4, + }, + value_scale: 0, + } + } +} 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) { + 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 { + 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::>(); + 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 { + 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 ()>( + 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::::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 { + 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::::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 +} 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::::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::::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| { + 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; diff --git a/old/evc/src/debug.rs b/old/evc/src/debug.rs new file mode 100644 index 0000000..a7a9545 --- /dev/null +++ b/old/evc/src/debug.rs @@ -0,0 +1,103 @@ +use crate::{ + block::Block, + format::ser::map_scalar8, + frame::Frame, + helpers::vector::Vec2, + helpers::{matrix::Mat2, pixel::Pixel}, + view::View, +}; + +impl View<&mut Frame> { + pub fn draw_box(&mut self, color: Pixel) { + let w = self.size.x; + let h = self.size.y; + for x in 0..w { + self[(x, 0)] = color; + self[(x, h - 1)] = color; + } + for y in 0..h { + self[(0, y)] = color; + self[(w - 1, y)] = color; + } + } +} + +impl Frame { + pub fn draw_line(&mut self, start: Vec2, end: Vec2, color: Pixel) { + let diff = end - start; + let len = (diff.x * diff.x + diff.y * diff.y).sqrt(); + let normal = Vec2 { + x: diff.x / len, + y: diff.y / len, + }; + let mut cursor = start.clone(); + let mut lc = 0.0; + while lc < len { + self.set(cursor.into(), color); + lc += 0.5; + cursor = cursor + normal.scale(0.5); + } + } +} + +impl Pixel { + pub const RED: Pixel = Pixel { r: 255, g: 0, b: 0 }; + pub const GREEN: Pixel = Pixel { r: 0, g: 255, b: 0 }; + pub const BLUE: Pixel = Pixel { r: 0, g: 0, b: 255 }; + pub const MAGENTA: Pixel = Pixel { + r: 255, + g: 0, + b: 255, + }; + pub const CYAN: Pixel = Pixel { + r: 0, + g: 255, + b: 255, + }; +} + +pub fn draw_debug(block: &Block, mut target: View<&mut Frame>) { + match &block { + Block::Literal(_) | Block::CompressedLiteral(_) => { + target.draw_box(Pixel::GREEN); + } + Block::Split(box [a, b]) => { + let [at, bt] = target.split_mut_unsafe(); + draw_debug(a, at); + draw_debug(b, bt); + } + Block::Reference { translation } => { + target.draw_box(Pixel::BLUE); + target.frame.draw_line( + target.center().into(), + (target.center() + *translation).into(), + Pixel::RED, + ) + } + Block::AdvancedReference(r) => { + let mat = Mat2 { + a: map_scalar8(r.transform.a), + b: map_scalar8(r.transform.b), + c: map_scalar8(r.transform.c), + d: map_scalar8(r.transform.d), + }; + let translation = Vec2 { + x: map_scalar8(r.translation.x), + y: map_scalar8(r.translation.y), + }; + let halfsize = Into::>::into(target.size).scale(0.5); + let transform = |p| translation + mat.transform(p - halfsize) + halfsize; + let (tl, tr, bl, br) = ( + transform(Vec2::::ZERO) + target.offset.into(), + transform(target.size.x_only().into()) + target.offset.into(), + transform(target.size.y_only().into()) + target.offset.into(), + transform(target.size.into()) + target.offset.into(), + ); + target.draw_box(Pixel::CYAN); + target.frame.draw_line(tl, tr, Pixel::MAGENTA); + target.frame.draw_line(tr, br, Pixel::MAGENTA); + target.frame.draw_line(bl, br, Pixel::MAGENTA); + target.frame.draw_line(tl, bl, Pixel::MAGENTA); + } + }; +} diff --git a/old/evc/src/format/header.rs b/old/evc/src/format/header.rs new file mode 100644 index 0000000..ecbae89 --- /dev/null +++ b/old/evc/src/format/header.rs @@ -0,0 +1,29 @@ +use crate::{ + format::ser::{Ser, Sink, Source}, + helpers::vector::Vec2, +}; + +#[derive(Debug, Clone, PartialEq, Copy)] +pub struct Header { + pub resolution: Vec2, + pub frame_count: usize, +} + +pub const MAGIC: [u8; 4] = [0x5eu8, 0xb1u8, 0xc3u8, 0x08u8]; + +impl Ser for Header { + fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> { + sink.put(MAGIC)?; + sink.put((Into::>::into(self.resolution), self.frame_count))?; + Ok(()) + } + + fn read(source: &mut impl std::io::Read) -> anyhow::Result { + assert_eq!(source.get::<[u8; 4]>()?, MAGIC); + let (resolution, frame_count): (Vec2, usize) = source.get()?; + Ok(Self { + resolution: resolution.into(), + frame_count, + }) + } +} diff --git a/old/evc/src/format/mod.rs b/old/evc/src/format/mod.rs new file mode 100644 index 0000000..d4fb18c --- /dev/null +++ b/old/evc/src/format/mod.rs @@ -0,0 +1,2 @@ +pub mod header; +pub mod ser; \ No newline at end of file diff --git a/old/evc/src/format/ser.rs b/old/evc/src/format/ser.rs new file mode 100644 index 0000000..f063377 --- /dev/null +++ b/old/evc/src/format/ser.rs @@ -0,0 +1,335 @@ +use anyhow::Context; +use std::io::{Read, Write}; + +use crate::helpers::{matrix::Mat2, vector::Vec2}; + +pub trait Sink { + fn put(&mut self, value: V) -> anyhow::Result<()>; +} +pub trait Source { + fn get(&mut self) -> anyhow::Result; +} + +impl Sink for T { + fn put(&mut self, value: V) -> anyhow::Result<()> { + value.write(self) + } +} +impl Source for T { + fn get(&mut self) -> anyhow::Result { + V::read(self) + } +} + +pub trait Ser: Sized { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()>; + fn read(source: &mut impl Read) -> anyhow::Result; +} + +impl Ser for (A, B) { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + self.0.write(sink).context("first tuple field")?; + self.1.write(sink).context("second tuple field")?; + Ok(()) + } + + fn read(source: &mut impl Read) -> anyhow::Result { + Ok((A::read(source)?, B::read(source)?)) + } +} +impl Ser for (A, B, C) { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + self.0.write(sink).context("first tuple field")?; + self.1.write(sink).context("second tuple field")?; + self.2.write(sink).context("third tuple field")?; + Ok(()) + } + + fn read(source: &mut impl Read) -> anyhow::Result { + Ok((A::read(source)?, B::read(source)?, C::read(source)?)) + } +} +impl Ser for (A, B, C, D) { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + self.0.write(sink).context("first tuple field")?; + self.1.write(sink).context("second tuple field")?; + self.2.write(sink).context("third tuple field")?; + self.3.write(sink).context("fourth tuple field")?; + Ok(()) + } + + fn read(source: &mut impl Read) -> anyhow::Result { + Ok(( + A::read(source)?, + B::read(source)?, + C::read(source)?, + D::read(source)?, + )) + } +} + +impl Ser for [A; N] { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + for e in self { + e.write(sink).context("some array")?; + } + Ok(()) + } + + fn read(source: &mut impl Read) -> anyhow::Result { + let mut k: [A; N] = unsafe { std::mem::zeroed() }; + for i in 0..N { + k[i] = A::read(source)?; + } + Ok(k) + } +} + +impl Ser for Vec { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + self.len().write(sink)?; + for e in self { + e.write(sink).context("some vec")?; + } + Ok(()) + } + + fn read(source: &mut impl Read) -> anyhow::Result { + let mut v = vec![]; + for _ in 0..usize::read(source)? { + v.push(T::read(source)?) + } + Ok(v) + } +} + +pub trait ConstSizeSerExt: Sized { + fn write_const_size(&self, sink: &mut impl Write, size: usize) -> anyhow::Result<()>; + fn read_const_size(source: &mut impl Read, size: usize) -> anyhow::Result; +} +impl ConstSizeSerExt for Vec { + fn write_const_size(&self, sink: &mut impl Write, size: usize) -> anyhow::Result<()> { + assert_eq!(self.len(), size); + for e in self { + e.write(sink).context("some const-size vec")?; + } + Ok(()) + } + + fn read_const_size(source: &mut impl Read, size: usize) -> anyhow::Result { + let mut v = vec![]; + for _ in 0..size { + v.push(T::read(source)?) + } + Ok(v) + } +} + +impl Ser for u8 { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink.write_all(&[*self]).context("write u8")?) + } + fn read(source: &mut impl Read) -> anyhow::Result { + let mut buf = [0u8; 1]; + source.read_exact(&mut buf)?; + Ok(buf[0]) + } +} +impl Ser for i8 { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 1]>(self) }) + .context("write i8")?) + } + fn read(source: &mut impl Read) -> anyhow::Result { + let mut buf = [0u8; 1]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} +impl Ser for u16 { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 2]>(self) }) + .context("write u16")?) + } + fn read(source: &mut impl Read) -> anyhow::Result { + let mut buf = [0u8; 2]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} +impl Ser for u32 { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 4]>(self) }) + .context("write u32")?) + } + fn read(source: &mut impl Read) -> anyhow::Result { + let mut buf = [0u8; 4]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} +impl Ser for u64 { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 8]>(self) }) + .context("write u64")?) + } + fn read(source: &mut impl Read) -> anyhow::Result { + let mut buf = [0u8; 8]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} +impl Ser for usize { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 8]>(self) }) + .context("write usize")?) + } + fn read(source: &mut impl Read) -> anyhow::Result { + let mut buf = [0u8; 8]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} +impl Ser for isize { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 8]>(self) }) + .context("write isize")?) + } + fn read(source: &mut impl Read) -> anyhow::Result { + let mut buf = [0u8; 8]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} +impl Ser for f32 { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 4]>(self) }) + .context("write f32")?) + } + fn read(source: &mut impl Read) -> anyhow::Result { + let mut buf = [0u8; 4]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} +impl Ser for f64 { + fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { + Ok(sink + .write_all(&unsafe { std::mem::transmute_copy::<_, [u8; 8]>(self) }) + .context("write f64")?) + } + fn read(source: &mut impl Read) -> anyhow::Result { + let mut buf = [0u8; 8]; + source.read_exact(&mut buf)?; + Ok(unsafe { std::mem::transmute_copy(&buf) }) + } +} + +impl Ser for Vec2 { + fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> { + sink.put((self.x, self.y)) + } + + fn read(source: &mut impl std::io::Read) -> anyhow::Result { + let (x, y) = source.get()?; + Ok(Vec2 { x, y }) + } +} +impl Ser for Mat2 { + fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> { + sink.put((self.a, self.b, self.c, self.d)) + } + + fn read(source: &mut impl std::io::Read) -> anyhow::Result { + let (a, b, c, d) = source.get()?; + Ok(Mat2 { a, b, c, d }) + } +} + +pub struct Small(pub T); +impl Ser for Small> { + fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> { + sink.put((self.0.x as i8, self.0.y as i8)) + } + + fn read(source: &mut impl std::io::Read) -> anyhow::Result { + let (x, y): (i8, i8) = source.get()?; + Ok(Small(Vec2 { + x: x as isize, + y: y as isize, + })) + } +} + +pub fn map_scalar8(v: i8) -> f32 { + match v { + 0 => 0.0, + x if x > 0 => 2f32.powf((x as f32).abs() / 2.0 - 2.0), + x => -2f32.powf((-x as f32).abs() / 2.0 - 2.0), + } +} +// const SCALAR8: [f32; 256] = gen_scalar8_lookup(); +// const fn gen_scalar8_lookup() -> [f32; 256] { +// let mut a = [0.0; 256]; +// let mut i = 0usize; +// while i < 256 { +// a[i as usize] = if i == 0 { +// 0.0 +// } else { +// let x = i as i8 as f32; +// x.signum() * 2f32.powf(x.abs() / 2.0 - 2.0) +// }; +// i += 1; +// } +// a +// } + +#[cfg(test)] +mod test { + use super::{Ser, Sink}; + use crate::format::header::Header; + use crate::{format::ser::Source, helpers::vector::Vec2}; + use std::fmt::Debug; + use std::io::Cursor; + + fn test_ser(value: T) { + let mut buf = vec![]; + Cursor::new(&mut buf).put(value.clone()).unwrap(); + assert_eq!(value, Cursor::new(&mut buf).get().unwrap()); + } + + #[test] + fn simple() { + let mut buf = vec![]; + Cursor::new(&mut buf).put(10usize).unwrap(); + assert_eq!(10usize, Cursor::new(&mut buf).get().unwrap()); + } + #[test] + fn tuple() { + let mut buf = vec![]; + Cursor::new(&mut buf).put((10usize, 5u8, 3u16)).unwrap(); + assert_eq!((10usize, 5u8, 3u16), Cursor::new(&mut buf).get().unwrap()); + } + #[test] + fn header() { + test_ser(Header { + frame_count: 123, + resolution: Vec2 { x: 13, y: 37 }, + }); + } + #[test] + fn vec() { + test_ser(vec![1u16, 2, 3, 4]); + } + #[test] + fn array() { + test_ser([1u16, 2, 3, 4]); + } +} diff --git a/old/evc/src/frame.rs b/old/evc/src/frame.rs new file mode 100644 index 0000000..78d0e73 --- /dev/null +++ b/old/evc/src/frame.rs @@ -0,0 +1,109 @@ +use crate::{ + format::ser::{Sink, Source}, + helpers::{pixel::Pixel, vector::Vec2}, + view::View, +}; +use std::ops::{Index, IndexMut}; + +#[derive(Debug, Clone)] +pub struct Frame { + pub size: Vec2, + buffer: Vec, +} + +impl Frame { + pub fn new(size: Vec2) -> Self { + Self { + size, + buffer: (0..size.x * size.y).map(|_| Pixel::default()).collect(), + } + } + pub fn read(source: &mut impl Source, size: Vec2) -> anyhow::Result { + let mut frame = Frame::new(size); + for y in 0..size.y { + for x in 0..size.x { + let pixel = source.get::()?; + frame[(x, y)] = pixel; + } + } + Ok(frame) + } + pub fn write(&self, sink: &mut impl Sink) -> anyhow::Result<()> { + for y in 0..self.size.y { + for x in 0..self.size.x { + sink.put(self[(x, y)])?; + } + } + Ok(()) + } + pub fn view<'a>(&'a self) -> View<&'a Frame> { + View::new(self, Vec2::::ZERO, self.size) + } + pub fn view_mut<'a>(&'a mut self) -> View<&'a mut Frame> { + View::new(self, Vec2::::ZERO, self.size) + } + pub fn view_area<'a>(&'a self, offset: Vec2, size: Vec2) -> View<&'a Frame> { + View::new(self, offset, size) + } + pub fn view_area_mut<'a>( + &'a mut self, + offset: Vec2, + size: Vec2, + ) -> View<&'a mut Frame> { + View::new(self, offset, size) + } + pub fn set(&mut self, pos: Vec2, color: Pixel) { + if pos.x >= 0 && pos.y >= 0 && pos.x < self.size.x && pos.y < self.size.y { + self[pos] = color + } + } +} + +impl Index> for Frame { + type Output = Pixel; + #[inline] + fn index(&self, Vec2 { x, y }: Vec2) -> &Self::Output { + &self.buffer + [(x.clamp(0, self.size.x - 1) + y.clamp(0, self.size.y - 1) * self.size.x) as usize] + } +} +impl IndexMut> for Frame { + #[inline] + fn index_mut(&mut self, Vec2 { x, y }: Vec2) -> &mut Self::Output { + &mut self.buffer[(x + y * self.size.x) as usize] + } +} + +impl Frame { + #[inline] + pub fn sample(&self, p: Vec2) -> Pixel { + self[Vec2 { + x: p.x as isize, + y: p.y as isize, + }] + // let fx = p.x.floor() as isize; + // let fy = p.y.floor() as isize; + // let cx = p.x.ceil() as isize; + // let cy = p.y.ceil() as isize; + + // // TODO dont loose accuracy here + // Pixel::average( + // Pixel::average(self[Vec2 { x: fx, y: fy }], self[Vec2 { x: fx, y: cy }]), + // Pixel::average(self[Vec2 { x: cx, y: fx }], self[Vec2 { x: cx, y: fy }]), + // ) + } +} + +impl Index<(isize, isize)> for Frame { + type Output = Pixel; + #[inline] + fn index(&self, (x, y): (isize, isize)) -> &Self::Output { + &self[Vec2 { x, y }] + } +} +impl IndexMut<(isize, isize)> for Frame { + #[inline] + fn index_mut(&mut self, (x, y): (isize, isize)) -> &mut Self::Output { + &mut self[Vec2 { x, y }] + } +} diff --git a/old/evc/src/helpers/matrix.rs b/old/evc/src/helpers/matrix.rs new file mode 100644 index 0000000..0007440 --- /dev/null +++ b/old/evc/src/helpers/matrix.rs @@ -0,0 +1,33 @@ +use crate::helpers::vector::Vec2; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Mat2 { + pub a: T, + pub b: T, + pub c: T, + pub d: T, +} + +impl + std::ops::Add + Copy> Mat2 { + #[inline] + pub fn transform(&self, v: Vec2) -> Vec2 { + Vec2 { + x: self.a * v.x + self.b * v.y, + y: self.c * v.x + self.d * v.y, + } + } +} + +impl + std::ops::Add + Copy> std::ops::Mul for Mat2 { + type Output = Mat2; + #[inline] + fn mul(self, rhs: Mat2) -> Mat2 { + let (x, y) = (self, rhs); + Mat2 { + a: x.a * y.a + x.b * y.c, + b: x.a * y.b + x.b * y.d, + c: x.c * y.a + x.d * y.c, + d: x.c * y.b + x.d * y.d, + } + } +} diff --git a/old/evc/src/helpers/mod.rs b/old/evc/src/helpers/mod.rs new file mode 100644 index 0000000..d3aa3d2 --- /dev/null +++ b/old/evc/src/helpers/mod.rs @@ -0,0 +1,4 @@ +pub mod vector; +pub mod threading; +pub mod matrix; +pub mod pixel; diff --git a/old/evc/src/helpers/pixel.rs b/old/evc/src/helpers/pixel.rs new file mode 100644 index 0000000..39fe98c --- /dev/null +++ b/old/evc/src/helpers/pixel.rs @@ -0,0 +1,81 @@ +use crate::format::ser::{Ser, Sink, Source}; + +#[derive(Copy, Clone, Debug, Default, PartialEq)] +pub struct Pixel { + pub r: u8, + pub g: u8, + pub b: u8, +} + +impl Ser for Pixel { + fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> { + sink.put((self.r, self.g, self.b)) + } + + fn read(source: &mut impl std::io::Read) -> anyhow::Result { + let (r, g, b) = source.get()?; + Ok(Self { r, g, b }) + } +} + +impl Pixel { + pub const BLACK: Pixel = Pixel { r: 0, g: 0, b: 0 }; + #[inline] + pub fn distance(a: Pixel, b: Pixel) -> usize { + let (rd, gd, bd) = ( + a.r.abs_diff(b.r) as usize, + a.g.abs_diff(b.g) as usize, + a.b.abs_diff(b.b) as usize, + ); + // fast_sqrt(rd * rd + gd * gd + bd * bd) + // SQRT[rd + gd + bd] + rd + gd + bd + } + + #[inline] + pub fn average(a: Pixel, b: Pixel) -> Pixel { + Pixel { + r: ((a.r as u16 + b.r as u16) >> 1) as u8, + g: ((a.g as u16 + b.g as u16) >> 1) as u8, + b: ((a.b as u16 + b.b as u16) >> 1) as u8, + } + } + + #[inline] + pub fn scale(&self, factor: f32) -> Pixel { + Pixel { + r: ((self.r as f32) * factor).clamp(0.0, 255.0) as u8, + g: ((self.g as f32) * factor).clamp(0.0, 255.0) as u8, + b: ((self.b as f32) * factor).clamp(0.0, 255.0) as u8, + } + } +} + +pub const SQRT: [usize; 256 * 3] = gen_sqrt_lookup(); + +const fn gen_sqrt_lookup() -> [usize; N] { + let mut arr = [0; N]; + let mut i = 0; + while i < N { + arr[i] = sqrt(i as f32) as usize; + i += 1; + } + arr +} + +const fn sqrt(x: f32) -> f32 { + let a = 1.0; + let a = (a + x / a) * 0.5; + let a = (a + x / a) * 0.5; + let a = (a + x / a) * 0.5; + a +} + +pub fn fast_sqrt(x: usize) -> usize { + let a = 1; + let a = (a + x / (a + 1)) / 2; + let a = (a + x / (a + 1)) / 2; + // let a = (a + x / (a + 1)) / 2; + // let a = (a + x / (a + 1)) / 2; + a +} diff --git a/old/evc/src/helpers/threading.rs b/old/evc/src/helpers/threading.rs new file mode 100644 index 0000000..3291172 --- /dev/null +++ b/old/evc/src/helpers/threading.rs @@ -0,0 +1,26 @@ +use std::{ + sync::atomic::{AtomicUsize, Ordering}, + thread, +}; + +static THREADS_RUNNING: AtomicUsize = AtomicUsize::new(0); + +pub fn both_par(f1: F1, f2: F2, max_threads: usize) -> (O1, O2) +where + F1: FnOnce() -> O1 + Send + 'static, + O1: Send + 'static, + F2: FnOnce() -> O2, +{ + if THREADS_RUNNING.load(Ordering::Relaxed) < max_threads { + THREADS_RUNNING.fetch_add(1, Ordering::Relaxed); + + let o1h = thread::spawn(move || f1()); + let o2 = f2(); + let o1 = o1h.join().unwrap(); + + THREADS_RUNNING.fetch_sub(1, Ordering::Relaxed); + (o1, o2) + } else { + (f1(), f2()) + } +} diff --git a/old/evc/src/helpers/vector.rs b/old/evc/src/helpers/vector.rs new file mode 100644 index 0000000..a4766d1 --- /dev/null +++ b/old/evc/src/helpers/vector.rs @@ -0,0 +1,126 @@ +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Vec2 { + pub x: T, + pub y: T, +} + +impl From> for Vec2 { + fn from(value: Vec2) -> Self { + Self { + x: value.x as u16, + y: value.y as u16, + } + } +} +impl From> for Vec2 { + fn from(value: Vec2) -> Self { + Self { + x: value.x as isize, + y: value.y as isize, + } + } +} +impl From> for Vec2 { + fn from(value: Vec2) -> Self { + Self { + x: value.x as isize, + y: value.y as isize, + } + } +} + +impl Vec2 { + pub const ZERO: Vec2 = Vec2 { x: 0, y: 0 }; + pub const UP: Vec2 = Vec2 { x: 0, y: -1 }; + pub const LEFT: Vec2 = Vec2 { x: -1, y: 0 }; +} +impl Vec2 { + pub const ZERO: Vec2 = Vec2 { x: 0.0, y: 0.0 }; + pub const UP: Vec2 = Vec2 { x: 0.0, y: -1.0 }; + pub const LEFT: Vec2 = Vec2 { x: -1.0, y: 0.0 }; +} + +impl + Copy> Vec2 { + pub fn downscale(&self, f: T) -> Self { + Self { + x: self.x / f, + y: self.y / f, + } + } +} + +impl + Copy> Vec2 { + pub fn scale(&self, f: T) -> Self { + Self { + x: self.x * f, + y: self.y * f, + } + } + pub fn area(&self) -> T { + self.x * self.y + } +} + +impl Vec2 { + pub fn x_only(&self) -> Self { + Self { x: self.x, y: 0 } + } + pub fn y_only(&self) -> Self { + Self { x: 0, y: self.y } + } +} + +impl Into> for Vec2 { + fn into(self) -> Vec2 { + Vec2 { + x: self.x as f32, + y: self.y as f32, + } + } +} +impl From<(isize, isize)> for Vec2 { + fn from((x, y): (isize, isize)) -> Self { + Vec2 { + x: x as f32, + y: y as f32, + } + } +} + +impl std::ops::Add for Vec2 { + type Output = Vec2; + #[inline] + fn add(self, rhs: Self) -> Self::Output { + Vec2 { + x: self.x + rhs.x, + y: self.y + rhs.y, + } + } +} +impl std::ops::Sub for Vec2 { + type Output = Vec2; + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Vec2 { + x: self.x - rhs.x, + y: self.y - rhs.y, + } + } +} +impl std::ops::Mul for Vec2 { + type Output = Vec2; + #[inline] + fn mul(self, rhs: Self) -> Self::Output { + Vec2 { + x: self.x * rhs.x, + y: self.y * rhs.y, + } + } +} + +impl From<(T, T)> for Vec2 { + #[inline] + fn from((x, y): (T, T)) -> Self { + Vec2 { x, y } + } +} diff --git a/old/evc/src/lib.rs b/old/evc/src/lib.rs new file mode 100644 index 0000000..d269471 --- /dev/null +++ b/old/evc/src/lib.rs @@ -0,0 +1,13 @@ +#![feature(box_patterns)] +#![feature(const_fn_floating_point_arithmetic)] +// #![feature(const_for)] +// #![feature(const_mut_refs)] + +pub mod block; +pub mod codec; +pub mod debug; +pub mod frame; +pub mod view; +pub mod refsampler; +pub mod helpers; +pub mod format; diff --git a/old/evc/src/refsampler.rs b/old/evc/src/refsampler.rs new file mode 100644 index 0000000..8a8f44f --- /dev/null +++ b/old/evc/src/refsampler.rs @@ -0,0 +1,51 @@ +use crate::{ + block::AdvancedReference, + format::ser::map_scalar8, + frame::Frame, + helpers::{matrix::Mat2, pixel::Pixel, vector::Vec2}, + view::View, +}; + +#[derive(Debug, Clone)] +pub struct Sampler<'a> { + pub view: View<&'a Frame>, + pub halfsize: Vec2, + + pub translation: Vec2, + pub transform: Mat2, + + pub value_scale: f32, +} + +impl<'a> Sampler<'a> { + #[inline] + pub fn sample(&self, p: Vec2) -> Pixel { + self.view + .sample(self.translation + self.transform.transform(p - self.halfsize) + self.halfsize) + .scale(self.value_scale) + } + pub fn from_refblock( + view: View<&'a Frame>, + AdvancedReference { + translation, + transform, + value_scale, + }: &AdvancedReference, + ) -> Self { + Self { + transform: Mat2 { + a: map_scalar8(transform.a), + b: map_scalar8(transform.b), + c: map_scalar8(transform.c), + d: map_scalar8(transform.d), + }, + halfsize: Into::>::into(view.size).scale(0.5), + translation: Vec2 { + x: map_scalar8(translation.x), + y: map_scalar8(translation.y), + }, + value_scale: 1.05f32.powf(*value_scale as f32), + view, + } + } +} diff --git a/old/evc/src/view.rs b/old/evc/src/view.rs new file mode 100644 index 0000000..6f34965 --- /dev/null +++ b/old/evc/src/view.rs @@ -0,0 +1,241 @@ +use crate::{ + frame::Frame, + helpers::{pixel::Pixel, vector::Vec2}, + refsampler::Sampler, +}; +use std::ops::{Index, IndexMut}; + +#[derive(Debug, Clone)] +pub struct View { + pub frame: T, + pub offset: Vec2, + pub size: Vec2, +} + +impl View { + pub fn new(frame: T, offset: Vec2, size: Vec2) -> Self { + Self { + frame, + offset, + size, + } + } + pub fn area(&self) -> isize { + self.size.x * self.size.y + } + pub fn center(&self) -> Vec2 { + self.offset + self.size.downscale(2) + } +} + +impl View<&mut T> { + pub fn split_mut_unsafe(&mut self) -> [Self; 2] { + let vert = self.size.x > self.size.y; + [ + Self { + frame: unsafe { std::mem::transmute::<&mut T, &mut T>(&mut self.frame) }, + offset: self.offset, + size: if vert { + Vec2 { + x: self.size.x / 2, + y: self.size.y, + } + } else { + Vec2 { + x: self.size.x, + y: self.size.y / 2, + } + }, + }, + Self { + frame: unsafe { std::mem::transmute::<&mut T, &mut T>(&mut self.frame) }, + offset: if vert { + Vec2 { + x: self.offset.x + self.size.x / 2, + y: self.offset.y, + } + } else { + Vec2 { + x: self.offset.x, + y: self.offset.y + self.size.y / 2, + } + }, + size: if vert { + Vec2 { + x: self.size.x - self.size.x / 2, + y: self.size.y, + } + } else { + Vec2 { + x: self.size.x, + y: self.size.y - self.size.y / 2, + } + }, + }, + ] + } +} +impl View { + pub fn offset(&self, offset: Vec2) -> Self { + Self { + frame: self.frame, + offset: self.offset + offset, + size: self.size, + } + } + pub fn split(&self) -> [Self; 2] { + let vert = self.size.x > self.size.y; + [ + Self { + frame: self.frame, + offset: self.offset, + size: if vert { + Vec2 { + x: self.size.x / 2, + y: self.size.y, + } + } else { + Vec2 { + x: self.size.x, + y: self.size.y / 2, + } + }, + }, + Self { + frame: self.frame, + offset: if vert { + Vec2 { + x: self.offset.x + self.size.x / 2, + y: self.offset.y, + } + } else { + Vec2 { + x: self.offset.x, + y: self.offset.y + self.size.y / 2, + } + }, + size: if vert { + Vec2 { + x: self.size.x - self.size.x / 2, + y: self.size.y, + } + } else { + Vec2 { + x: self.size.x, + y: self.size.y - self.size.y / 2, + } + }, + }, + ] + } +} +impl, Output = Pixel>> View<&T> { + pub fn diff(va: &Self, vb: &Self) -> f64 { + assert_eq!(va.size, vb.size); + let mut acc = 0; + for x in 0..va.size.x { + for y in 0..va.size.y { + let a = va[(x, y)]; + let b = vb[(x, y)]; + acc += Pixel::distance(a, b); + } + } + acc as f64 + } + pub fn diff_sampler(va: &Self, vb: &Sampler<'_>) -> f64 { + assert_eq!(va.size, vb.view.size); + let mut acc = 0; + for x in 0..va.size.x { + for y in 0..va.size.y { + let a = va[(x, y)]; + let b = vb.sample(Vec2 { + x: x as f32, + y: y as f32, + }); + acc += Pixel::distance(a, b); + } + } + acc as f64 + } + + pub fn pixels(&self) -> Vec { + let mut v = vec![]; + for y in 0..self.size.y { + for x in 0..self.size.x { + v.push(self[(x, y)]); + } + } + v + } +} +impl View<&mut Frame> { + pub fn copy_from(&mut self, other: &View<&Frame>) { + for x in 0..self.size.x { + for y in 0..self.size.y { + self[(x, y)] = other[(x, y)]; + } + } + } + pub fn copy_from_sampler(&mut self, other: &Sampler) { + for x in 0..self.size.x { + for y in 0..self.size.y { + self[(x, y)] = other.sample((x, y).into()); + } + } + } + + pub fn set_pixels(&mut self, pixels: &Vec) { + for y in 0..self.size.y { + for x in 0..self.size.x { + self[(x, y)] = pixels[(y * self.size.x + x) as usize] + } + } + } +} +impl View<&Frame> { + pub fn sample(&self, p: Vec2) -> Pixel { + self.frame.sample(p + self.offset.into()) + } +} + +impl, Output = Pixel>> Index> for View<&T> { + type Output = Pixel; + #[inline] + fn index(&self, p: Vec2) -> &Self::Output { + &self.frame[self.offset + p] + } +} +impl, Output = Pixel>> Index> for View<&mut T> { + type Output = Pixel; + #[inline] + fn index(&self, p: Vec2) -> &Self::Output { + &self.frame[self.offset + p] + } +} +impl, Output = Pixel>> IndexMut> for View<&mut T> { + #[inline] + fn index_mut(&mut self, p: Vec2) -> &mut Self::Output { + &mut self.frame[self.offset + p] + } +} + +impl, Output = Pixel>> Index<(isize, isize)> for View<&T> { + type Output = Pixel; + #[inline] + fn index(&self, (x, y): (isize, isize)) -> &Self::Output { + &self[Vec2 { x, y }] + } +} +impl, Output = Pixel>> Index<(isize, isize)> for View<&mut T> { + type Output = Pixel; + #[inline] + fn index(&self, (x, y): (isize, isize)) -> &Self::Output { + &self[Vec2 { x, y }] + } +} +impl, Output = Pixel>> IndexMut<(isize, isize)> for View<&mut T> { + #[inline] + fn index_mut(&mut self, (x, y): (isize, isize)) -> &mut Self::Output { + &mut self[Vec2 { x, y }] + } +} -- cgit v1.2.3-70-g09d2