pub const MAX_IMAGE_DIMENSION: i64 = 100_000;
pub const MAX_IMAGE_PIXELS: u64 = 4_000_000_000;
pub const MAX_BITS_PER_COMPONENT: i64 = 16;
pub fn validate_image_dimension(value: Option<i64>) -> Option<u32> {
match value {
Some(n) if n > 0 && n <= MAX_IMAGE_DIMENSION => Some(n as u32),
_ => None,
}
}
pub fn validate_image_size(width: u32, height: u32) -> Option<usize> {
let pixels = u64::from(width).checked_mul(u64::from(height))?;
if pixels == 0 || pixels > MAX_IMAGE_PIXELS {
return None;
}
usize::try_from(pixels).ok()
}
pub fn validate_bits_per_component(value: Option<i64>) -> Option<u32> {
match value {
None => Some(8),
Some(n) if n > 0 && n <= MAX_BITS_PER_COMPONENT => Some(n as u32),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn prepress_sizes_are_accepted() {
for (label, w, h) in [
("40x28in press sheet @ 600dpi", 24_000, 16_800),
("A0 poster @ 600dpi", 19_800, 28_200),
("60x40in grand format @ 600dpi", 36_000, 24_000),
("60x40in grand format @ 1200dpi", 72_000, 48_000),
] {
assert!(
validate_image_size(w, h).is_some(),
"{label} ({w}x{h}) must be accepted"
);
}
}
#[test]
fn overflowing_and_degenerate_sizes_are_rejected() {
assert!(validate_image_size(65_537, 65_536).is_none());
assert!(validate_image_size(0, 100).is_none());
assert!(validate_image_dimension(Some(0)).is_none());
assert!(validate_image_dimension(Some(-1)).is_none());
assert!(validate_image_dimension(Some(4_294_967_297)).is_none());
assert!(validate_image_dimension(None).is_none());
}
#[test]
fn pixel_ceiling_stays_within_u32() {
assert!(
MAX_IMAGE_PIXELS < u64::from(u32::MAX),
"sites computing width * height in u32 depend on this"
);
}
#[test]
fn bits_per_component_is_bounded() {
assert_eq!(validate_bits_per_component(None), Some(8));
assert_eq!(validate_bits_per_component(Some(8)), Some(8));
assert!(validate_bits_per_component(Some(99)).is_none());
assert!(validate_bits_per_component(Some(0)).is_none());
}
}