use crate::error::DecodeError;
use crate::limits::ParseLimits;
use crate::{DecodedImage, DecodedYuv};
pub struct Decoder {
limits: ParseLimits,
pool: crate::threadpool::ThreadPool,
}
impl Default for Decoder {
fn default() -> Self {
Decoder::new()
}
}
impl Decoder {
pub fn new() -> Self {
Decoder {
limits: ParseLimits::default(),
pool: crate::threadpool::ThreadPool::with_available_parallelism(),
}
}
pub fn with_threads(mut self, threads: usize) -> Self {
self.pool = crate::threadpool::ThreadPool::new(threads);
self
}
pub fn with_limits(mut self, limits: ParseLimits) -> Self {
self.limits = limits;
self
}
pub fn with_max_dimension(mut self, max_dimension: u32) -> Self {
self.limits.max_dimension = max_dimension;
self
}
pub fn with_max_pixels(mut self, max_pixels: u64) -> Self {
self.limits.max_pixels = max_pixels;
self
}
pub fn with_max_box_size(mut self, max_box_size: u64) -> Self {
self.limits.max_box_size = max_box_size;
self
}
pub fn with_max_item_size(mut self, max_item_size: u64) -> Self {
self.limits.max_item_size = max_item_size;
self
}
pub fn limits(&self) -> &ParseLimits {
&self.limits
}
pub fn threads(&self) -> usize {
self.pool.threads()
}
pub(crate) fn check_dims(&self, w: usize, h: usize) -> Result<(), DecodeError> {
if w == 0 || h == 0 {
return Err(DecodeError::Bitstream(format!(
"image dimensions {w}×{h} are zero"
)));
}
let (Ok(w32), Ok(h32)) = (u32::try_from(w), u32::try_from(h)) else {
return Err(DecodeError::LimitExceeded {
what: "image dimension",
value: w.max(h) as u64,
limit: self.limits.max_dimension as u64,
});
};
self.limits.check_image(w32, h32)
}
pub(crate) fn pool(&self) -> &crate::threadpool::ThreadPool {
&self.pool
}
pub fn decode(&self, file: &[u8]) -> Result<DecodedImage, DecodeError> {
crate::decode_heic_with(self, file)
}
pub fn decode_yuv(&self, file: &[u8]) -> Result<DecodedYuv, DecodeError> {
crate::decode_heic_yuv_with(self, file)
}
pub fn decode_rgb8(&self, file: &[u8]) -> Result<(Vec<u8>, u32, u32), DecodeError> {
crate::decode_heic_rgb8_with(self, file)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builder_sets_limits() {
let d = Decoder::new().with_max_dimension(100).with_max_pixels(4000);
assert!(d.check_dims(50, 50).is_ok()); assert!(d.check_dims(0, 10).is_err()); assert!(d.check_dims(101, 10).is_err()); assert!(d.check_dims(80, 80).is_err()); }
#[test]
fn default_limits_match_historical_constants() {
let d = Decoder::default();
let max = ParseLimits::DEFAULT_MAX_DIMENSION as usize;
assert!(d.check_dims(max, 1).is_ok());
assert!(d.check_dims(max + 1, 1).is_err());
}
#[test]
fn box_and_item_size_setters() {
let d = Decoder::new()
.with_max_box_size(1024)
.with_max_item_size(2048);
assert_eq!(d.limits().max_box_size, 1024);
assert_eq!(d.limits().max_item_size, 2048);
}
#[test]
fn threads_reports_configured_count() {
assert!(Decoder::new().threads() >= 1);
assert_eq!(Decoder::new().with_threads(2).threads(), 2);
assert_eq!(Decoder::new().with_threads(0).threads(), 1);
}
}