Skip to content

Commit

Permalink
Improve ImageDecoder trait (#1869)
Browse files Browse the repository at this point in the history
  • Loading branch information
fintelia authored Feb 12, 2024
1 parent 85721e3 commit 79ecd0f
Show file tree
Hide file tree
Showing 26 changed files with 444 additions and 1,490 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
features: ['', default, gif, jpeg, png, tiff, ico, pnm, tga, webp, bmp, hdr, dxt, dds, farbfeld, openexr, jpeg_rayon, webp-encoder]
features: ['', default, gif, jpeg, png, tiff, ico, pnm, tga, webp, bmp, hdr, dds, farbfeld, openexr, jpeg_rayon, webp-encoder]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
Expand Down
5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,14 @@ jpeg = { package = "jpeg-decoder", version = "0.3.0", default-features = false,

[features]
# TODO: Add "avif" to this list while preparing for 0.24.0
default = ["gif", "jpeg", "ico", "png", "pnm", "tga", "tiff", "webp", "bmp", "hdr", "dxt", "dds", "farbfeld", "jpeg_rayon", "openexr", "qoi"]
default = ["gif", "jpeg", "ico", "png", "pnm", "tga", "tiff", "webp", "bmp", "hdr", "dds", "farbfeld", "jpeg_rayon", "openexr", "qoi"]

ico = ["bmp", "png"]
pnm = []
tga = []
bmp = []
hdr = []
dxt = []
dds = ["dxt"]
dds = []
farbfeld = []
openexr = ["exr"]
qoi = ["dep:qoi"]
Expand Down
40 changes: 8 additions & 32 deletions src/codecs/avif/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
/// [AVIF]: https://aomediacodec.github.io/av1-avif/
use std::convert::TryFrom;
use std::error::Error;
use std::io::{self, Cursor, Read};
use std::io::Read;
use std::marker::PhantomData;
use std::mem;

use crate::error::{DecodingError, UnsupportedError, UnsupportedErrorKind};
use crate::{ColorType, ImageDecoder, ImageError, ImageFormat, ImageResult};
Expand Down Expand Up @@ -66,26 +65,7 @@ impl<R: Read> AvifDecoder<R> {
}
}

/// Wrapper struct around a `Cursor<Vec<u8>>`
pub struct AvifReader<R>(Cursor<Vec<u8>>, PhantomData<R>);

impl<R> Read for AvifReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
if self.0.position() == 0 && buf.is_empty() {
mem::swap(buf, self.0.get_mut());
Ok(buf.len())
} else {
self.0.read_to_end(buf)
}
}
}

impl<'a, R: 'a + Read> ImageDecoder<'a> for AvifDecoder<R> {
type Reader = AvifReader<R>;

impl<R: Read> ImageDecoder for AvifDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
(self.picture.width(), self.picture.height())
}
Expand All @@ -94,16 +74,8 @@ impl<'a, R: 'a + Read> ImageDecoder<'a> for AvifDecoder<R> {
ColorType::Rgba8
}

fn icc_profile(&mut self) -> Option<Vec<u8>> {
self.icc_profile.clone()
}

fn into_reader(self) -> ImageResult<Self::Reader> {
let plane = self.picture.plane(PlanarImageComponent::Y);
Ok(AvifReader(
Cursor::new(plane.as_ref().to_vec()),
PhantomData,
))
fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
Ok(self.icc_profile.clone())
}

fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
Expand Down Expand Up @@ -182,6 +154,10 @@ impl<'a, R: 'a + Read> ImageDecoder<'a> for AvifDecoder<R> {

Ok(())
}

fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
(*self).read_image(buf)
}
}

/// `get_picture` and `send_pending_data` yield `Again` as a non-fatal error requesting more data is sent to the decoder
Expand Down
54 changes: 18 additions & 36 deletions src/codecs/bmp/decoder.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
use std::cmp::{self, Ordering};
use std::convert::TryFrom;
use std::io::{self, Cursor, Read, Seek, SeekFrom};
use std::io::{self, Read, Seek, SeekFrom};
use std::iter::{repeat, Iterator, Rev};
use std::marker::PhantomData;
use std::slice::ChunksMut;
use std::{error, fmt, mem};
use std::{error, fmt};

use byteorder::{LittleEndian, ReadBytesExt};

use crate::color::ColorType;
use crate::error::{
DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
};
use crate::image::{self, ImageDecoder, ImageDecoderRect, ImageFormat, Progress};
use crate::image::{self, ImageDecoder, ImageFormat};
use crate::ImageDecoderRect;

const BITMAPCOREHEADER_SIZE: u32 = 12;
const BITMAPINFOHEADER_SIZE: u32 = 40;
Expand Down Expand Up @@ -1330,25 +1330,7 @@ impl<R: Read + Seek> BmpDecoder<R> {
}
}

/// Wrapper struct around a `Cursor<Vec<u8>>`
pub struct BmpReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for BmpReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
if self.0.position() == 0 && buf.is_empty() {
mem::swap(buf, self.0.get_mut());
Ok(buf.len())
} else {
self.0.read_to_end(buf)
}
}
}

impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for BmpDecoder<R> {
type Reader = BmpReader<R>;

impl<R: Read + Seek> ImageDecoder for BmpDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
(self.width as u32, self.height as u32)
}
Expand All @@ -1363,28 +1345,25 @@ impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for BmpDecoder<R> {
}
}

fn into_reader(self) -> ImageResult<Self::Reader> {
Ok(BmpReader(
Cursor::new(image::decoder_to_vec(self)?),
PhantomData,
))
}

fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
self.read_image_data(buf)
}

fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
(*self).read_image(buf)
}
}

impl<'a, R: 'a + Read + Seek> ImageDecoderRect<'a> for BmpDecoder<R> {
fn read_rect_with_progress<F: Fn(Progress)>(
impl<R: Read + Seek> ImageDecoderRect for BmpDecoder<R> {
fn read_rect(
&mut self,
x: u32,
y: u32,
width: u32,
height: u32,
buf: &mut [u8],
progress_callback: F,
row_pitch: usize,
) -> ImageResult<()> {
let start = self.reader.stream_position()?;
image::load_rect(
Expand All @@ -1393,8 +1372,9 @@ impl<'a, R: 'a + Read + Seek> ImageDecoderRect<'a> for BmpDecoder<R> {
width,
height,
buf,
progress_callback,
row_pitch,
self,
self.total_bytes() as usize,
|_, _| Ok(()),
|s, buf| s.read_image_data(buf),
)?;
Expand All @@ -1405,6 +1385,8 @@ impl<'a, R: 'a + Read + Seek> ImageDecoderRect<'a> for BmpDecoder<R> {

#[cfg(test)]
mod test {
use std::io::Cursor;

use super::*;

#[test]
Expand All @@ -1428,7 +1410,7 @@ mod test {
let mut decoder = super::BmpDecoder::new(f).unwrap();

let mut buf: Vec<u8> = vec![0; 8 * 8 * 3];
decoder.read_rect(0, 0, 8, 8, &mut buf).unwrap();
decoder.read_rect(0, 0, 8, 8, &mut buf, 8 * 3).unwrap();
}

#[test]
Expand All @@ -1454,7 +1436,7 @@ mod test {
0x4d, 0x00, 0x2a, 0x00,
];

let decoder = BmpDecoder::new(Cursor::new(&data)).unwrap();
let decoder = BmpDecoder::new(std::io::Cursor::new(&data)).unwrap();
let mut buf = vec![0; usize::try_from(decoder.total_bytes()).unwrap()];
assert!(decoder.read_image(&mut buf).is_ok());
}
Expand Down
21 changes: 6 additions & 15 deletions src/codecs/dds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::{error, fmt};
use byteorder::{LittleEndian, ReadBytesExt};

#[allow(deprecated)]
use crate::codecs::dxt::{DxtDecoder, DxtReader, DxtVariant};
use crate::codecs::dxt::{DxtDecoder, DxtVariant};
use crate::color::ColorType;
use crate::error::{
DecodingError, ImageError, ImageFormatHint, ImageResult, UnsupportedError, UnsupportedErrorKind,
Expand Down Expand Up @@ -327,10 +327,7 @@ impl<R: Read> DdsDecoder<R> {
}
}

impl<'a, R: 'a + Read> ImageDecoder<'a> for DdsDecoder<R> {
#[allow(deprecated)]
type Reader = DxtReader<R>;

impl<R: Read> ImageDecoder for DdsDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
self.inner.dimensions()
}
Expand All @@ -339,19 +336,13 @@ impl<'a, R: 'a + Read> ImageDecoder<'a> for DdsDecoder<R> {
self.inner.color_type()
}

fn scanline_bytes(&self) -> u64 {
#[allow(deprecated)]
self.inner.scanline_bytes()
}

fn into_reader(self) -> ImageResult<Self::Reader> {
#[allow(deprecated)]
self.inner.into_reader()
}

fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
self.inner.read_image(buf)
}

fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
(*self).read_image(buf)
}
}

#[cfg(test)]
Expand Down
Loading

0 comments on commit 79ecd0f

Please sign in to comment.