type Dims = (u32, u32);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Resize {
Exact(u32, u32),
Fit(u32, u32),
FitEven(u32, u32),
}
impl Resize {
pub fn compute_for(self, dims: Dims) -> Option<Dims> {
match self {
Resize::Exact(w, h) => Some((w, h)),
Resize::Fit(w, h) => calculate_fit_dims(dims, (w, h)),
Resize::FitEven(w, h) => calculate_fit_dims_even(dims, (w, h)),
}
}
}
fn calculate_fit_dims(dims: (u32, u32), fit_dims: (u32, u32)) -> Option<(u32, u32)> {
let (w, h) = dims;
let (w_max, h_max) = fit_dims;
if w_max >= w && h_max >= h {
Some((w, h))
} else {
let wf = w_max as f32 / w as f32;
let hf = h_max as f32 / h as f32;
let f = wf.min(hf);
let (w_out, h_out) = ((w as f32 * f) as u32, (h as f32 * f) as u32);
if (w_out > 0) && (h_out > 0) {
Some((w_out, h_out))
} else {
None
}
}
}
fn calculate_fit_dims_even(dims: (u32, u32), fit_dims: (u32, u32)) -> Option<(u32, u32)> {
let (w, h) = dims;
let (mut w_max, mut h_max) = fit_dims;
while w_max > 0 && h_max > 0 {
let wf = w_max as f32 / w as f32;
let hf = h_max as f32 / h as f32;
let f = wf.min(hf).min(1.0);
let out_w = (w as f32 * f).round() as u32;
let out_h = (h as f32 * f).round() as u32;
if (out_w > 0) && (out_h > 0) {
if out_w.is_multiple_of(2) && out_h.is_multiple_of(2) {
return Some((out_w, out_h));
} else if wf < hf {
w_max -= 1;
} else {
h_max -= 1;
}
} else {
break;
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
const TESTING_DIM_CANDIDATES: [u32; 8] = [0, 1, 2, 3, 8, 111, 256, 1000];
#[test]
fn calculate_fit_dims_works() {
let testset = generate_testset();
for ((w, h), (fit_w, fit_h)) in testset {
let out = calculate_fit_dims((w, h), (fit_w, fit_h));
if let Some((out_w, out_h)) = out {
let input_dim_zero = w == 0 || h == 0 || fit_w == 0 || fit_h == 0;
let output_dim_zero = out_w == 0 || out_h == 0;
assert!(
(input_dim_zero && output_dim_zero) || (!input_dim_zero && !output_dim_zero),
"computed dims are never zero unless the inputs dims were",
);
assert!(
(out_w <= fit_w) && (out_h <= fit_h),
"computed dims fit inside provided dims",
);
}
}
}
#[test]
fn calculate_fit_dims_even_works() {
let testset = generate_testset();
for ((w, h), (fit_w, fit_h)) in testset {
let out = calculate_fit_dims_even((w, h), (fit_w, fit_h));
if let Some((out_w, out_h)) = out {
let input_dim_zero = w == 0 || h == 0 || fit_w == 0 || fit_h == 0;
let output_dim_zero = out_w == 0 || out_h == 0;
assert!(
(input_dim_zero && output_dim_zero) || (!input_dim_zero && !output_dim_zero),
"computed dims are never zero unless the inputs dims were",
);
assert!(
(out_w % 2 == 0) && (out_h % 2 == 0),
"computed dims are even",
);
assert!(
(out_w <= fit_w) && (out_h <= fit_h),
"computed dims fit inside provided dims",
);
}
}
}
fn generate_testset() -> Vec<((u32, u32), (u32, u32))> {
let testing_dims = generate_testing_dims();
testing_dims
.iter()
.flat_map(|dims| testing_dims.iter().map(|fit_dims| (*dims, *fit_dims)))
.collect()
}
fn generate_testing_dims() -> Vec<(u32, u32)> {
TESTING_DIM_CANDIDATES
.iter()
.flat_map(|a| TESTING_DIM_CANDIDATES.iter().map(|b| (*a, *b)))
.collect()
}
}