diff --git a/src/codecs/bmp/decoder.rs b/src/codecs/bmp/decoder.rs index 43d4aade4a..fa50952c87 100644 --- a/src/codecs/bmp/decoder.rs +++ b/src/codecs/bmp/decoder.rs @@ -11,9 +11,8 @@ use crate::color::ColorType; use crate::error::{ DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind, }; -use crate::io::free_functions::load_rect; use crate::io::ReadExt; -use crate::{ImageDecoder, ImageDecoderRect, ImageFormat}; +use crate::{ImageDecoder, ImageFormat}; const BITMAPCOREHEADER_SIZE: u32 = 12; const BITMAPINFOHEADER_SIZE: u32 = 40; @@ -1415,34 +1414,6 @@ impl ImageDecoder for BmpDecoder { } } -impl ImageDecoderRect for BmpDecoder { - fn read_rect( - &mut self, - x: u32, - y: u32, - width: u32, - height: u32, - buf: &mut [u8], - row_pitch: usize, - ) -> ImageResult<()> { - let start = self.reader.stream_position()?; - load_rect( - x, - y, - width, - height, - buf, - row_pitch, - self, - self.total_bytes() as usize, - |_, _| Ok(()), - |s, buf| s.read_image_data(buf), - )?; - self.reader.seek(SeekFrom::Start(start))?; - Ok(()) - } -} - #[cfg(test)] mod test { use std::io::{BufReader, Cursor}; @@ -1464,16 +1435,6 @@ mod test { } } - #[test] - fn read_rect() { - let f = - BufReader::new(std::fs::File::open("tests/images/bmp/images/Core_8_Bit.bmp").unwrap()); - let mut decoder = BmpDecoder::new(f).unwrap(); - - let mut buf: Vec = vec![0; 8 * 8 * 3]; - decoder.read_rect(0, 0, 8, 8, &mut buf, 8 * 3).unwrap(); - } - #[test] fn read_rle_too_short() { let data = vec![ diff --git a/src/codecs/farbfeld.rs b/src/codecs/farbfeld.rs index a38897058a..faa941a476 100644 --- a/src/codecs/farbfeld.rs +++ b/src/codecs/farbfeld.rs @@ -22,8 +22,7 @@ use crate::color::ExtendedColorType; use crate::error::{ DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind, }; -use crate::io::free_functions::load_rect; -use crate::{ColorType, ImageDecoder, ImageDecoderRect, ImageEncoder, ImageFormat}; +use crate::{ColorType, ImageDecoder, ImageEncoder, ImageFormat}; /// farbfeld Reader pub struct FarbfeldReader { @@ -215,36 +214,6 @@ impl ImageDecoder for FarbfeldDecoder { } } -impl ImageDecoderRect for FarbfeldDecoder { - fn read_rect( - &mut self, - x: u32, - y: u32, - width: u32, - height: u32, - buf: &mut [u8], - row_pitch: usize, - ) -> ImageResult<()> { - // A "scanline" (defined as "shortest non-caching read" in the doc) is just one channel in this case - - let start = self.reader.stream_position()?; - load_rect( - x, - y, - width, - height, - buf, - row_pitch, - self, - 2, - |s, scanline| s.reader.seek(SeekFrom::Start(scanline * 2)).map(|_| ()), - |s, buf| s.reader.read_exact(buf), - )?; - self.reader.seek(SeekFrom::Start(start))?; - Ok(()) - } -} - /// farbfeld encoder pub struct FarbfeldEncoder { w: W, @@ -314,73 +283,7 @@ impl ImageEncoder for FarbfeldEncoder { #[cfg(test)] mod tests { use crate::codecs::farbfeld::FarbfeldDecoder; - use crate::ImageDecoderRect; - use byteorder_lite::{ByteOrder, NativeEndian}; - use std::io::{Cursor, Seek, SeekFrom}; - - static RECTANGLE_IN: &[u8] = b"farbfeld\ - \x00\x00\x00\x02\x00\x00\x00\x03\ - \xFF\x01\xFE\x02\xFD\x03\xFC\x04\xFB\x05\xFA\x06\xF9\x07\xF8\x08\ - \xF7\x09\xF6\x0A\xF5\x0B\xF4\x0C\xF3\x0D\xF2\x0E\xF1\x0F\xF0\x10\ - \xEF\x11\xEE\x12\xED\x13\xEC\x14\xEB\x15\xEA\x16\xE9\x17\xE8\x18"; - - #[test] - fn read_rect_1x2() { - static RECTANGLE_OUT: &[u16] = &[ - 0xF30D, 0xF20E, 0xF10F, 0xF010, 0xEB15, 0xEA16, 0xE917, 0xE818, - ]; - - read_rect(1, 1, 1, 2, RECTANGLE_OUT); - } - - #[test] - fn read_rect_2x2() { - static RECTANGLE_OUT: &[u16] = &[ - 0xFF01, 0xFE02, 0xFD03, 0xFC04, 0xFB05, 0xFA06, 0xF907, 0xF808, 0xF709, 0xF60A, 0xF50B, - 0xF40C, 0xF30D, 0xF20E, 0xF10F, 0xF010, - ]; - - read_rect(0, 0, 2, 2, RECTANGLE_OUT); - } - - #[test] - fn read_rect_2x1() { - static RECTANGLE_OUT: &[u16] = &[ - 0xEF11, 0xEE12, 0xED13, 0xEC14, 0xEB15, 0xEA16, 0xE917, 0xE818, - ]; - - read_rect(0, 2, 2, 1, RECTANGLE_OUT); - } - - #[test] - fn read_rect_2x3() { - static RECTANGLE_OUT: &[u16] = &[ - 0xFF01, 0xFE02, 0xFD03, 0xFC04, 0xFB05, 0xFA06, 0xF907, 0xF808, 0xF709, 0xF60A, 0xF50B, - 0xF40C, 0xF30D, 0xF20E, 0xF10F, 0xF010, 0xEF11, 0xEE12, 0xED13, 0xEC14, 0xEB15, 0xEA16, - 0xE917, 0xE818, - ]; - - read_rect(0, 0, 2, 3, RECTANGLE_OUT); - } - - #[test] - fn read_rect_in_stream() { - static RECTANGLE_OUT: &[u16] = &[0xEF11, 0xEE12, 0xED13, 0xEC14]; - - let mut input = vec![]; - input.extend_from_slice(b"This is a 31-byte-long prologue"); - input.extend_from_slice(RECTANGLE_IN); - let mut input_cur = Cursor::new(input); - input_cur.seek(SeekFrom::Start(31)).unwrap(); - - let mut out_buf = [0u8; 64]; - FarbfeldDecoder::new(input_cur) - .unwrap() - .read_rect(0, 2, 1, 1, &mut out_buf, 8) - .unwrap(); - let exp = degenerate_pixels(RECTANGLE_OUT); - assert_eq!(&out_buf[..exp.len()], &exp[..]); - } + use std::io::Cursor; #[test] fn dimension_overflow() { @@ -388,20 +291,4 @@ mod tests { assert!(FarbfeldDecoder::new(Cursor::new(header)).is_err()); } - - fn read_rect(x: u32, y: u32, width: u32, height: u32, exp_wide: &[u16]) { - let mut out_buf = [0u8; 64]; - FarbfeldDecoder::new(Cursor::new(RECTANGLE_IN)) - .unwrap() - .read_rect(x, y, width, height, &mut out_buf, width as usize * 8) - .unwrap(); - let exp = degenerate_pixels(exp_wide); - assert_eq!(&out_buf[..exp.len()], &exp[..]); - } - - fn degenerate_pixels(exp_wide: &[u16]) -> Vec { - let mut exp = vec![0u8; exp_wide.len() * 2]; - NativeEndian::write_u16_into(exp_wide, &mut exp); - exp - } } diff --git a/src/io/decoder.rs b/src/io/decoder.rs index e71c83ea76..7d0981bc4f 100644 --- a/src/io/decoder.rs +++ b/src/io/decoder.rs @@ -175,26 +175,6 @@ impl ImageDecoder for Box { } } -/// Specialized image decoding not be supported by all formats -pub trait ImageDecoderRect: ImageDecoder { - /// Decode a rectangular section of the image. - /// - /// This function takes a slice of bytes and writes the pixel data of the image into it. - /// The rectangle is specified by the x and y coordinates of the top left corner, the width - /// and height of the rectangle, and the row pitch of the buffer. The row pitch is the number - /// of bytes between the start of one row and the start of the next row. The row pitch must be - /// at least as large as the width of the rectangle in bytes. - fn read_rect( - &mut self, - x: u32, - y: u32, - width: u32, - height: u32, - buf: &mut [u8], - row_pitch: usize, - ) -> ImageResult<()>; -} - /// `AnimationDecoder` trait pub trait AnimationDecoder<'a> { /// Consume the decoder producing a series of frames. diff --git a/src/io/free_functions.rs b/src/io/free_functions.rs index 33afbd3dc0..aee821f0d1 100644 --- a/src/io/free_functions.rs +++ b/src/io/free_functions.rs @@ -1,5 +1,5 @@ use std::fs::File; -use std::io::{self, BufRead, BufWriter, Seek, Write}; +use std::io::{BufRead, BufWriter, Seek, Write}; use std::path::Path; use std::{iter, mem::size_of}; @@ -7,8 +7,8 @@ use crate::io::encoder::ImageEncoderBoxed; use crate::{codecs::*, ExtendedColorType, ImageReader}; use crate::error::{ - ImageError, ImageFormatHint, ImageResult, LimitError, LimitErrorKind, ParameterError, - ParameterErrorKind, UnsupportedError, UnsupportedErrorKind, + ImageError, ImageFormatHint, ImageResult, LimitError, LimitErrorKind, UnsupportedError, + UnsupportedErrorKind, }; use crate::{DynamicImage, ImageDecoder, ImageFormat}; @@ -162,142 +162,6 @@ pub(crate) fn guess_format_impl(buffer: &[u8]) -> Option { None } -/// Decodes a specific region of the image, represented by the rectangle -/// starting from ```x``` and ```y``` and having ```length``` and ```width``` -#[allow(dead_code)] -#[allow(clippy::too_many_arguments)] -pub(crate) fn load_rect( - x: u32, - y: u32, - width: u32, - height: u32, - buf: &mut [u8], - row_pitch: usize, - decoder: &mut D, - scanline_bytes: usize, - mut seek_scanline: F1, - mut read_scanline: F2, -) -> ImageResult<()> -where - D: ImageDecoder, - F1: FnMut(&mut D, u64) -> io::Result<()>, - F2: FnMut(&mut D, &mut [u8]) -> Result<(), E>, - ImageError: From, -{ - let scanline_bytes = u64::try_from(scanline_bytes).unwrap(); - let row_pitch = u64::try_from(row_pitch).unwrap(); - - let (x, y, width, height) = ( - u64::from(x), - u64::from(y), - u64::from(width), - u64::from(height), - ); - let dimensions = decoder.dimensions(); - let bytes_per_pixel = u64::from(decoder.color_type().bytes_per_pixel()); - let row_bytes = bytes_per_pixel * u64::from(dimensions.0); - let total_bytes = width * height * bytes_per_pixel; - - assert!( - buf.len() >= usize::try_from(total_bytes).unwrap_or(usize::MAX), - "output buffer too short\n expected `{}`, provided `{}`", - total_bytes, - buf.len() - ); - - let mut current_scanline = 0; - let mut tmp = Vec::new(); - let mut tmp_scanline = None; - - { - // Read a range of the image starting from byte number `start` and continuing until byte - // number `end`. Updates `current_scanline` and `bytes_read` appropriately. - let mut read_image_range = - |mut start: u64, end: u64, mut output: &mut [u8]| -> ImageResult<()> { - // If the first scanline we need is already stored in the temporary buffer, then handle - // it first. - let target_scanline = start / scanline_bytes; - if tmp_scanline == Some(target_scanline) { - let position = target_scanline * scanline_bytes; - let offset = start.saturating_sub(position); - let len = (end - start) - .min(scanline_bytes - offset) - .min(end - position); - - output - .write_all(&tmp[offset as usize..][..len as usize]) - .unwrap(); - start += len; - - if start == end { - return Ok(()); - } - } - - let target_scanline = start / scanline_bytes; - if target_scanline != current_scanline { - seek_scanline(decoder, target_scanline)?; - current_scanline = target_scanline; - } - - let mut position = current_scanline * scanline_bytes; - while position < end { - if position >= start && end - position >= scanline_bytes { - read_scanline(decoder, &mut output[..(scanline_bytes as usize)])?; - output = &mut output[scanline_bytes as usize..]; - } else { - tmp.resize(scanline_bytes as usize, 0u8); - read_scanline(decoder, &mut tmp)?; - tmp_scanline = Some(current_scanline); - - let offset = start.saturating_sub(position); - let len = (end - start) - .min(scanline_bytes - offset) - .min(end - position); - - output - .write_all(&tmp[offset as usize..][..len as usize]) - .unwrap(); - } - - current_scanline += 1; - position += scanline_bytes; - } - Ok(()) - }; - - if x + width > u64::from(dimensions.0) - || y + height > u64::from(dimensions.1) - || width == 0 - || height == 0 - { - return Err(ImageError::Parameter(ParameterError::from_kind( - ParameterErrorKind::DimensionMismatch, - ))); - } - if scanline_bytes > usize::MAX as u64 { - return Err(ImageError::Limits(LimitError::from_kind( - LimitErrorKind::InsufficientMemory, - ))); - } - - if x == 0 && width == u64::from(dimensions.0) && row_pitch == row_bytes { - let start = x * bytes_per_pixel + y * row_bytes; - let end = (x + width) * bytes_per_pixel + (y + height - 1) * row_bytes; - read_image_range(start, end, buf)?; - } else { - for (output_slice, row) in buf.chunks_mut(row_pitch as usize).zip(y..(y + height)) { - let start = x * bytes_per_pixel + row * row_bytes; - let end = (x + width) * bytes_per_pixel + row * row_bytes; - read_image_range(start, end, output_slice)?; - } - } - } - - // Seek back to the start - Ok(seek_scanline(decoder, 0)?) -} - /// Reads all of the bytes of a decoder into a Vec. No particular alignment /// of the output buffer is guaranteed. /// @@ -317,188 +181,3 @@ where decoder.read_image(bytemuck::cast_slice_mut(buf.as_mut_slice()))?; Ok(buf) } - -#[cfg(test)] -mod tests { - use crate::ColorType; - use std::io; - - use super::{load_rect, ImageDecoder, ImageResult}; - - #[test] - fn test_load_rect() { - struct MockDecoder { - scanline_number: u64, - scanline_bytes: u64, - } - impl ImageDecoder for MockDecoder { - fn dimensions(&self) -> (u32, u32) { - (5, 5) - } - fn color_type(&self) -> ColorType { - ColorType::L8 - } - fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> { - unimplemented!() - } - fn read_image_boxed(self: Box, buf: &mut [u8]) -> ImageResult<()> { - (*self).read_image(buf) - } - } - - const DATA: [u8; 25] = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, - ]; - - fn seek_scanline(m: &mut MockDecoder, n: u64) -> io::Result<()> { - m.scanline_number = n; - Ok(()) - } - fn read_scanline(m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> { - let bytes_read = m.scanline_number * m.scanline_bytes; - if bytes_read >= 25 { - return Ok(()); - } - - let len = m.scanline_bytes.min(25 - bytes_read); - buf[..(len as usize)].copy_from_slice(&DATA[(bytes_read as usize)..][..(len as usize)]); - m.scanline_number += 1; - Ok(()) - } - - for scanline_bytes in 1..30 { - let mut output = [0u8; 26]; - - load_rect( - 0, - 0, - 5, - 5, - &mut output, - 5, - &mut MockDecoder { - scanline_number: 0, - scanline_bytes, - }, - scanline_bytes as usize, - seek_scanline, - read_scanline, - ) - .unwrap(); - assert_eq!(output[0..25], DATA); - assert_eq!(output[25], 0); - - output = [0u8; 26]; - load_rect( - 3, - 2, - 1, - 1, - &mut output, - 1, - &mut MockDecoder { - scanline_number: 0, - scanline_bytes, - }, - scanline_bytes as usize, - seek_scanline, - read_scanline, - ) - .unwrap(); - assert_eq!(output[0..2], [13, 0]); - - output = [0u8; 26]; - load_rect( - 3, - 2, - 2, - 2, - &mut output, - 2, - &mut MockDecoder { - scanline_number: 0, - scanline_bytes, - }, - scanline_bytes as usize, - seek_scanline, - read_scanline, - ) - .unwrap(); - assert_eq!(output[0..5], [13, 14, 18, 19, 0]); - - output = [0u8; 26]; - load_rect( - 1, - 1, - 2, - 4, - &mut output, - 2, - &mut MockDecoder { - scanline_number: 0, - scanline_bytes, - }, - scanline_bytes as usize, - seek_scanline, - read_scanline, - ) - .unwrap(); - assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]); - } - } - - #[test] - fn test_load_rect_single_scanline() { - const DATA: [u8; 25] = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, - ]; - - struct MockDecoder; - impl ImageDecoder for MockDecoder { - fn dimensions(&self) -> (u32, u32) { - (5, 5) - } - fn color_type(&self) -> ColorType { - ColorType::L8 - } - fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> { - unimplemented!() - } - fn read_image_boxed(self: Box, buf: &mut [u8]) -> ImageResult<()> { - (*self).read_image(buf) - } - } - - // Ensure that seek scanline is called only once. - let mut seeks = 0; - let seek_scanline = |_d: &mut MockDecoder, n: u64| -> io::Result<()> { - seeks += 1; - assert_eq!(n, 0); - assert_eq!(seeks, 1); - Ok(()) - }; - - fn read_scanline(_m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> { - buf.copy_from_slice(&DATA); - Ok(()) - } - - let mut output = [0; 26]; - load_rect( - 1, - 1, - 2, - 4, - &mut output, - 2, - &mut MockDecoder, - DATA.len(), - seek_scanline, - read_scanline, - ) - .unwrap(); - assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]); - } -} diff --git a/src/lib.rs b/src/lib.rs index 811833ab40..30bbc3784d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,7 +88,7 @@ //! # } //! # #[cfg(not(feature = "jpeg"))] fn main() {} //! ``` -//! While [`ImageDecoder`] and [`ImageDecoderRect`] give access to more advanced decoding options: +//! While [`ImageDecoder`] give access to more advanced decoding options: //! //! ```rust,no_run //! # use std::io::{BufReader, Cursor}; @@ -108,7 +108,6 @@ //! ``` //! //! [`DynamicImage::from_decoder`]: enum.DynamicImage.html#method.from_decoder -//! [`ImageDecoderRect`]: trait.ImageDecoderRect.html //! [`ImageDecoder`]: trait.ImageDecoder.html //! [`ImageEncoder`]: trait.ImageEncoder.html #![warn(missing_docs)] @@ -161,7 +160,7 @@ pub use crate::images::dynimage::{ pub use crate::io::free_functions::{guess_format, load, save_buffer, save_buffer_with_format}; pub use crate::io::{ - decoder::{AnimationDecoder, ImageDecoder, ImageDecoderRect}, + decoder::{AnimationDecoder, ImageDecoder}, encoder::ImageEncoder, format::ImageFormat, image_reader_type::ImageReader,