From c746653269cb9173601da77561603a1a7bb58373 Mon Sep 17 00:00:00 2001 From: "A. Molzer" <5550310+197g@users.noreply.github.com> Date: Mon, 14 Jul 2025 11:49:31 +0200 Subject: [PATCH] Add Decoder::read_image_bytes, borrowed output --- src/decoder/mod.rs | 144 ++++++++++++++++++++++++++++++++++++--------- src/error.rs | 3 + 2 files changed, 120 insertions(+), 27 deletions(-) diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 0f91d23..4576868 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -1,3 +1,4 @@ +use std::alloc::{Layout, LayoutError}; use std::collections::BTreeMap; use std::io::{self, Read, Seek}; @@ -197,6 +198,61 @@ impl<'a> DecodingBuffer<'a> { } } +/// The count and matching discriminant for a `DecodingBuffer`. +#[derive(Clone)] +enum DecodingExtent { + U8(usize), + U16(usize), + U32(usize), + U64(usize), + F16(usize), + F32(usize), + F64(usize), + I8(usize), + I16(usize), + I32(usize), + I64(usize), +} + +impl DecodingExtent { + fn to_result_buffer(&self, limits: &Limits) -> TiffResult { + match *self { + DecodingExtent::U8(count) => DecodingResult::new_u8(count, limits), + DecodingExtent::U16(count) => DecodingResult::new_u16(count, limits), + DecodingExtent::U32(count) => DecodingResult::new_u32(count, limits), + DecodingExtent::U64(count) => DecodingResult::new_u64(count, limits), + DecodingExtent::F16(count) => DecodingResult::new_f16(count, limits), + DecodingExtent::F32(count) => DecodingResult::new_f32(count, limits), + DecodingExtent::F64(count) => DecodingResult::new_f64(count, limits), + DecodingExtent::I8(count) => DecodingResult::new_i8(count, limits), + DecodingExtent::I16(count) => DecodingResult::new_i16(count, limits), + DecodingExtent::I32(count) => DecodingResult::new_i32(count, limits), + DecodingExtent::I64(count) => DecodingResult::new_i64(count, limits), + } + } + + fn preferred_layout(self) -> TiffResult { + fn overflow(_: LayoutError) -> TiffError { + TiffError::LimitsExceeded + } + + match self { + DecodingExtent::U8(count) => Layout::array::(count), + DecodingExtent::U16(count) => Layout::array::(count), + DecodingExtent::U32(count) => Layout::array::(count), + DecodingExtent::U64(count) => Layout::array::(count), + DecodingExtent::F16(count) => Layout::array::(count), + DecodingExtent::F32(count) => Layout::array::(count), + DecodingExtent::F64(count) => Layout::array::(count), + DecodingExtent::I8(count) => Layout::array::(count), + DecodingExtent::I16(count) => Layout::array::(count), + DecodingExtent::I32(count) => Layout::array::(count), + DecodingExtent::I64(count) => Layout::array::(count), + } + .map_err(overflow) + } +} + #[derive(Debug, Copy, Clone, PartialEq)] /// Chunk type of the internal representation pub enum ChunkType { @@ -856,6 +912,11 @@ impl Decoder { } fn result_buffer(&self, width: usize, height: usize) -> TiffResult { + self.result_extent(width, height)? + .to_result_buffer(&self.value_reader.limits) + } + + fn result_extent(&self, width: usize, height: usize) -> TiffResult { let bits_per_sample = self.image().bits_per_sample; let row_samples = if bits_per_sample >= 8 { @@ -872,37 +933,44 @@ impl Decoder { .ok_or(TiffError::LimitsExceeded)?; let max_sample_bits = self.image().bits_per_sample; - let limits = &self.value_reader.limits; - match self.image().sample_format { + Ok(match self.image().sample_format { SampleFormat::Uint => match max_sample_bits { - n if n <= 8 => DecodingResult::new_u8(buffer_size, limits), - n if n <= 16 => DecodingResult::new_u16(buffer_size, limits), - n if n <= 32 => DecodingResult::new_u32(buffer_size, limits), - n if n <= 64 => DecodingResult::new_u64(buffer_size, limits), - n => Err(TiffError::UnsupportedError( - TiffUnsupportedError::UnsupportedBitsPerChannel(n), - )), + n if n <= 8 => DecodingExtent::U8(buffer_size), + n if n <= 16 => DecodingExtent::U16(buffer_size), + n if n <= 32 => DecodingExtent::U32(buffer_size), + n if n <= 64 => DecodingExtent::U64(buffer_size), + n => { + return Err(TiffError::UnsupportedError( + TiffUnsupportedError::UnsupportedBitsPerChannel(n), + )) + } }, SampleFormat::IEEEFP => match max_sample_bits { - 16 => DecodingResult::new_f16(buffer_size, limits), - 32 => DecodingResult::new_f32(buffer_size, limits), - 64 => DecodingResult::new_f64(buffer_size, limits), - n => Err(TiffError::UnsupportedError( - TiffUnsupportedError::UnsupportedBitsPerChannel(n), - )), + 16 => DecodingExtent::F16(buffer_size), + 32 => DecodingExtent::F32(buffer_size), + 64 => DecodingExtent::F64(buffer_size), + n => { + return Err(TiffError::UnsupportedError( + TiffUnsupportedError::UnsupportedBitsPerChannel(n), + )) + } }, SampleFormat::Int => match max_sample_bits { - n if n <= 8 => DecodingResult::new_i8(buffer_size, limits), - n if n <= 16 => DecodingResult::new_i16(buffer_size, limits), - n if n <= 32 => DecodingResult::new_i32(buffer_size, limits), - n if n <= 64 => DecodingResult::new_i64(buffer_size, limits), - n => Err(TiffError::UnsupportedError( - TiffUnsupportedError::UnsupportedBitsPerChannel(n), - )), + n if n <= 8 => DecodingExtent::I8(buffer_size), + n if n <= 16 => DecodingExtent::I16(buffer_size), + n if n <= 32 => DecodingExtent::I32(buffer_size), + n if n <= 64 => DecodingExtent::I64(buffer_size), + n => { + return Err(TiffError::UnsupportedError( + TiffUnsupportedError::UnsupportedBitsPerChannel(n), + )) + } }, - format => Err(TiffUnsupportedError::UnsupportedSampleFormat(vec![format]).into()), - } + format => { + return Err(TiffUnsupportedError::UnsupportedSampleFormat(vec![format]).into()) + } + }) } /// Read the specified chunk (at index `chunk_index`) and return the binary data as a Vector. @@ -934,9 +1002,31 @@ impl Decoder { pub fn read_image(&mut self) -> TiffResult { let width = self.image().width; let height = self.image().height; + let mut result = self.result_buffer(width as usize, height as usize)?; + self.read_image_bytes(result.as_buffer(0).as_bytes_mut())?; + + Ok(result) + } + + pub fn read_image_bytes(&mut self, buffer: &mut [u8]) -> TiffResult<()> { + let width = self.image().width; + let height = self.image().height; + + let extent = self.result_extent(width as usize, height as usize)?; + let needed_bytes = extent.preferred_layout()?.size(); + + if buffer.len() < needed_bytes { + return Err(TiffError::UsageError( + UsageError::InsufficientOutputBufferSize { + needed: needed_bytes, + provided: buffer.len(), + }, + )); + } + if width == 0 || height == 0 { - return Ok(result); + return Ok(()); } let chunk_dimensions = self.image().chunk_dimensions()?; @@ -989,13 +1079,13 @@ impl Decoder { y * output_row_stride * chunk_dimensions.1 as usize + x * chunk_row_bytes; self.image.expand_chunk( &mut self.value_reader, - &mut result.as_buffer(0).as_bytes_mut()[buffer_offset..], + &mut buffer[buffer_offset..], output_row_stride, chunk as u32, )?; } - Ok(result) + Ok(()) } /// Get the IFD decoder for our current image IFD. diff --git a/src/error.rs b/src/error.rs index 64ef5bb..c3ebefe 100644 --- a/src/error.rs +++ b/src/error.rs @@ -184,6 +184,9 @@ quick_error! { PredictorUnavailable { display("the requested predictor is not available") } + InsufficientOutputBufferSize { needed: usize, provided: usize } { + display("the borrowed output buffer is not large enough for the decoded data, needed {needed} but have {provided}") + } } }