use super::AspectRatioConformPolicy;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ConformedAperture {
pub aperture_size: [f32; 2],
pub pixel_aspect_ratio: f32,
}
pub fn apply_aspect_ratio_policy(
policy: AspectRatioConformPolicy,
resolution: [i32; 2],
pixel_aspect_ratio: f32,
aperture: [f32; 2],
) -> ConformedAperture {
if resolution[0] <= 0 || resolution[1] <= 0 || aperture[0] <= 0.0 || aperture[1] <= 0.0 {
return ConformedAperture {
aperture_size: aperture,
pixel_aspect_ratio,
};
}
let res_aspect = resolution[0] as f32 / resolution[1] as f32;
let image_aspect = pixel_aspect_ratio * res_aspect;
let aperture_aspect = aperture[0] / aperture[1];
if image_aspect <= 0.0 {
return ConformedAperture {
aperture_size: aperture,
pixel_aspect_ratio,
};
}
let mut size = aperture;
let mut par = pixel_aspect_ratio;
enum Adjust {
None,
Width,
Height,
}
let adjust = match policy {
AspectRatioConformPolicy::AdjustPixelAspectRatio => {
par = aperture_aspect / res_aspect;
Adjust::None
}
AspectRatioConformPolicy::AdjustApertureHeight => Adjust::Height,
AspectRatioConformPolicy::AdjustApertureWidth => Adjust::Width,
AspectRatioConformPolicy::ExpandAperture => {
if aperture_aspect > image_aspect {
Adjust::Height
} else {
Adjust::Width
}
}
AspectRatioConformPolicy::CropAperture => {
if aperture_aspect > image_aspect {
Adjust::Width
} else {
Adjust::Height
}
}
};
match adjust {
Adjust::Width => size[0] = size[1] * image_aspect,
Adjust::Height => size[1] = size[0] / image_aspect,
Adjust::None => {}
}
ConformedAperture {
aperture_size: size,
pixel_aspect_ratio: par,
}
}
#[cfg(test)]
mod tests {
use super::*;
const RES: [i32; 2] = [200, 100];
const PAR: f32 = 1.0;
const APERTURE: [f32; 2] = [10.0, 10.0];
fn run(policy: AspectRatioConformPolicy) -> ConformedAperture {
apply_aspect_ratio_policy(policy, RES, PAR, APERTURE)
}
fn approx(a: [f32; 2], b: [f32; 2]) -> bool {
(a[0] - b[0]).abs() < 1e-4 && (a[1] - b[1]).abs() < 1e-4
}
#[test]
fn expand_grows_width_when_aperture_is_taller() {
let c = run(AspectRatioConformPolicy::ExpandAperture);
assert!(approx(c.aperture_size, [20.0, 10.0]));
assert_eq!(c.pixel_aspect_ratio, 1.0);
}
#[test]
fn crop_shrinks_height_the_mirror_branch() {
let c = run(AspectRatioConformPolicy::CropAperture);
assert!(approx(c.aperture_size, [10.0, 5.0]));
}
#[test]
fn adjust_aperture_width_pins_height() {
let c = run(AspectRatioConformPolicy::AdjustApertureWidth);
assert!(approx(c.aperture_size, [20.0, 10.0]));
}
#[test]
fn adjust_aperture_height_pins_width() {
let c = run(AspectRatioConformPolicy::AdjustApertureHeight);
assert!(approx(c.aperture_size, [10.0, 5.0]));
}
#[test]
fn malformed_inputs_return_finite_unchanged() {
let policy = AspectRatioConformPolicy::ExpandAperture;
for (res, par, aperture) in [
([200, 0], 1.0, [10.0, 10.0]), ([0, 100], 1.0, [10.0, 10.0]), ([-200, 100], 1.0, [10.0, 10.0]), ([200, 100], 1.0, [10.0, 0.0]), ([200, 100], 1.0, [0.0, 10.0]), ([200, 100], -1.0, [10.0, 10.0]), ([200, 100], 0.0, [10.0, 10.0]), ] {
let c = apply_aspect_ratio_policy(policy, res, par, aperture);
assert_eq!(c.aperture_size, aperture);
assert!(c.aperture_size.iter().all(|v| v.is_finite()));
}
}
#[test]
fn adjust_pixel_aspect_ratio_leaves_aperture() {
let c = run(AspectRatioConformPolicy::AdjustPixelAspectRatio);
assert!(approx(c.aperture_size, [10.0, 10.0]));
assert!((c.pixel_aspect_ratio - 0.5).abs() < 1e-6);
}
}