gfwx 0.3.0

GFWX: Good, Fast Wavelet Codec (Rust)
Documentation
use super::{get_hint_do_parallel, lift, unlift};
use crate::config::Config;
use crate::processing::{
    process_maybe_parallel_for_each, DoubleOverlappingChunks, DoubleOverlappingChunksIterator,
};

#[inline(always)]
pub fn lift_cubic(mut image: &mut [&mut [i16]]) {
    lift(
        &mut image,
        Config::multithreading_factors().cubic_horizontal_lifting,
        horizontal_lift,
        vertical_lift,
    );
}

#[inline(always)]
pub fn unlift_cubic(mut image: &mut [&mut [i16]]) {
    unlift(
        &mut image,
        Config::multithreading_factors().cubic_horizontal_lifting,
        horizontal_unlift,
        vertical_unlift,
    );
}

#[inline(always)]
unsafe fn horizontal_lifting_base(
    column: &mut [i16],
    step: usize,
    c0_column_start: usize,
    c2_step_multiplier: usize,
    step_start_multiplier: usize,
    divider: i32,
) {
    let mut c0 = *column.get_unchecked(c0_column_start);
    let mut c1 = c0;
    let mut c2 = if c2_step_multiplier * step < column.len() {
        *column.get_unchecked(c2_step_multiplier * step)
    } else {
        c0
    };

    let mut x = step_start_multiplier * step;
    if column.len() > 3 * step {
        while x < column.len() - 3 * step {
            let c3 = *column.get_unchecked(3 * step + x);
            *column.get_unchecked_mut(x) += (cubic(c0, c1, c2, c3) / divider) as i16;
            c0 = c1;
            c1 = c2;
            c2 = c3;
            x += step * 2;
        }
    }
    while x < column.len() {
        *column.get_unchecked_mut(x) += (cubic(c0, c1, c2, c2) / divider) as i16;
        c0 = c1;
        c1 = c2;
        x += step * 2;
    }
}

#[inline(always)]
unsafe fn vertical_lifting_base(
    chunks: &mut DoubleOverlappingChunks<'_, &mut [i16]>,
    step: usize,
    divider: i32,
) {
    let mut x = 0;
    while x < chunks.middle.get_unchecked(0).len() {
        let c1 = *chunks.left.get_unchecked(0).get_unchecked(x);
        let c2 = if let Some(right_value) = chunks.right.first() {
            *right_value.get_unchecked(x)
        } else {
            c1
        };
        let c0 = if let Some(prev_left_value) = chunks.prev_left.first() {
            *prev_left_value.get_unchecked(x)
        } else {
            c1
        };
        let c3 = if let Some(next_right_value) = chunks.next_right.first() {
            *next_right_value.get_unchecked(x)
        } else {
            c2
        };

        *chunks.middle.get_unchecked_mut(0).get_unchecked_mut(x) +=
            (cubic(c0, c1, c2, c3) / divider) as i16;
        x += step;
    }
}

#[inline(always)]
unsafe fn horizontal_lift(mut column: &mut [i16], step: usize) {
    horizontal_lifting_base(&mut column, step, 0, 2, 1, -1);
    horizontal_lifting_base(&mut column, step, step, 3, 2, 2);
}

#[inline(always)]
unsafe fn horizontal_unlift(mut column: &mut [i16], step: usize) {
    horizontal_lifting_base(&mut column, step, step, 3, 2, -2);
    horizontal_lifting_base(&mut column, step, 0, 2, 1, 1);
}

#[inline(always)]
unsafe fn vertical_lift(mut image: &mut [&mut [i16]], step: usize) {
    let config_factor = Config::multithreading_factors().cubic_vertical_lifting;
    let hint_do_parallel = get_hint_do_parallel(&image, config_factor);
    process_maybe_parallel_for_each(
        DoubleOverlappingChunksIterator::from_slice(&mut image, step),
        |mut chunks| {
            vertical_lifting_base(&mut chunks, step, -1);
        },
        hint_do_parallel,
    );
    process_maybe_parallel_for_each(
        DoubleOverlappingChunksIterator::from_slice(&mut image[step..], step),
        |mut chunks| {
            vertical_lifting_base(&mut chunks, step, 2);
        },
        hint_do_parallel,
    );
}

#[inline(always)]
unsafe fn vertical_unlift(mut image: &mut [&mut [i16]], step: usize) {
    let config_factor = Config::multithreading_factors().cubic_vertical_lifting;
    let hint_do_parallel = get_hint_do_parallel(&image, config_factor);
    process_maybe_parallel_for_each(
        DoubleOverlappingChunksIterator::from_slice(&mut image[step..], step),
        |mut chunks| {
            vertical_lifting_base(&mut chunks, step, -2);
        },
        hint_do_parallel,
    );
    process_maybe_parallel_for_each(
        DoubleOverlappingChunksIterator::from_slice(&mut image, step),
        |mut chunks| {
            vertical_lifting_base(&mut chunks, step, 1);
        },
        hint_do_parallel,
    );
}

fn median(mut a: i32, mut b: i32, mut c: i32) -> i32 {
    use std::mem;

    if a > b {
        mem::swap(&mut a, &mut b);
    }
    if b > c {
        mem::swap(&mut b, &mut c);
    }
    if a > b {
        mem::swap(&mut a, &mut b);
    }

    b
}

fn round_fraction(num: i32, denom: i32) -> i32 {
    if num < 0 {
        (num - denom / 2) / denom
    } else {
        (num + denom / 2) / denom
    }
}

pub fn cubic(c0: i16, c1: i16, c2: i16, c3: i16) -> i32 {
    let c0 = i32::from(c0);
    let c1 = i32::from(c1);
    let c2 = i32::from(c2);
    let c3 = i32::from(c3);
    let num = -c0 + 9 * (c1 + c2) - c3;
    median(round_fraction(num, 16), c1, c2)
}