Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/fast_blur/main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use image::imageops::GaussianBlurParameters;
use image::ImageReader;
use image::ImageReaderOptions;

fn main() {
let path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/images/tiff/testsuite/mandrill.tiff"
);
let img = ImageReader::open(path).unwrap().decode().unwrap();
let img = ImageReaderOptions::open(path).unwrap().decode().unwrap();

let img2 = img.blur_advanced(GaussianBlurParameters::new_from_sigma(10.0));

Expand Down
10 changes: 6 additions & 4 deletions fuzz-afl/fuzzers/fuzz_pnm.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
extern crate afl;
extern crate image;

use image::{DynamicImage, ImageDecoder};
use image::error::{ImageError, ImageResult, LimitError, LimitErrorKind};
use image::{DynamicImage, ImageDecoder};

#[inline(always)]
fn pnm_decode(data: &[u8]) -> ImageResult<DynamicImage> {
let decoder = image::codecs::pnm::PnmDecoder::new(data)?;
let (width, height) = decoder.dimensions();
let mut decoder = image::codecs::pnm::PnmDecoder::new(data)?;
let (width, height) = decoder.peek_layout()?.dimensions();

if width.saturating_mul(height) > 4_000_000 {
return Err(ImageError::Limits(LimitError::from_kind(LimitErrorKind::DimensionError)));
return Err(ImageError::Limits(LimitError::from_kind(
LimitErrorKind::DimensionError,
)));
}

DynamicImage::from_decoder(decoder)
Expand Down
10 changes: 6 additions & 4 deletions fuzz-afl/fuzzers/fuzz_webp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ extern crate image;

use std::io::Cursor;

use image::{DynamicImage, ImageDecoder};
use image::error::{ImageError, ImageResult, LimitError, LimitErrorKind};
use image::{DynamicImage, ImageDecoder};

#[inline(always)]
fn webp_decode(data: &[u8]) -> ImageResult<DynamicImage> {
let decoder = image::codecs::webp::WebPDecoder::new(Cursor::new(data))?;
let (width, height) = decoder.dimensions();
let mut decoder = image::codecs::webp::WebPDecoder::new(Cursor::new(data))?;
let (width, height) = decoder.peek_layout()?.dimensions();

if width.saturating_mul(height) > 4_000_000 {
return Err(ImageError::Limits(LimitError::from_kind(LimitErrorKind::DimensionError)));
return Err(ImageError::Limits(LimitError::from_kind(
LimitErrorKind::DimensionError,
)));
}

DynamicImage::from_decoder(decoder)
Expand Down
4 changes: 2 additions & 2 deletions fuzz-afl/reproducers/reproduce_pnm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ mod utils;

#[inline(always)]
fn pnm_decode(data: &[u8]) -> ImageResult<DynamicImage> {
let decoder = image::codecs::pnm::PnmDecoder::new(data)?;
let (width, height) = decoder.dimensions();
let mut decoder = image::codecs::pnm::PnmDecoder::new(data)?;
let (width, height) = decoder.peek_layout()?.dimensions();

if width.saturating_mul(height) > 4_000_000 {
return Err(ImageError::Limits(LimitError::from_kind(LimitErrorKind::DimensionError)));
Expand Down
10 changes: 6 additions & 4 deletions fuzz-afl/reproducers/reproduce_webp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ extern crate image;

use std::io::Cursor;

use image::{DynamicImage, ImageDecoder};
use image::error::{ImageError, ImageResult, LimitError, LimitErrorKind};
use image::{DynamicImage, ImageDecoder};

mod utils;

#[inline(always)]
fn webp_decode(data: &[u8]) -> ImageResult<DynamicImage> {
let decoder = image::codecs::webp::WebPDecoder::new(Cursor::new(data))?;
let (width, height) = decoder.dimensions();
let mut decoder = image::codecs::webp::WebPDecoder::new(Cursor::new(data))?;
let (width, height) = decoder.peek_layout()?.dimensions();

if width.saturating_mul(height) > 4_000_000 {
return Err(ImageError::Limits(LimitError::from_kind(LimitErrorKind::DimensionError)));
return Err(ImageError::Limits(LimitError::from_kind(
LimitErrorKind::DimensionError,
)));
}

DynamicImage::from_decoder(decoder)
Expand Down
7 changes: 4 additions & 3 deletions fuzz/fuzzers/fuzzer_script_exr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ extern crate libfuzzer_sys;
extern crate image;

use image::codecs::openexr::*;
use image::Limits;
use image::ExtendedColorType;
use image::ImageDecoder;
use image::ImageEncoder;
use image::ImageResult;
use image::Limits;
use std::io::{BufRead, Cursor, Seek, Write};

// "just dont panic"
Expand All @@ -17,10 +17,11 @@ fn roundtrip(bytes: &[u8]) -> ImageResult<()> {
// TODO this method should probably already exist in the main image crate
fn read_as_rgba_byte_image(read: impl BufRead + Seek) -> ImageResult<(u32, u32, Vec<u8>)> {
let mut decoder = OpenExrDecoder::with_alpha_preference(read, Some(true))?;
match usize::try_from(decoder.total_bytes()) {
let layout = decoder.peek_layout()?;
match usize::try_from(layout.total_bytes()) {
Ok(decoded_size) if decoded_size <= 256 * 1024 * 1024 => {
decoder.set_limits(Limits::default())?;
let (width, height) = decoder.dimensions();
let (width, height) = layout.dimensions();
let mut buffer = vec![0; decoded_size];
decoder.read_image(buffer.as_mut_slice())?;
Ok((width, height, buffer))
Expand Down
6 changes: 3 additions & 3 deletions fuzz/fuzzers/fuzzer_script_tga.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ fuzz_target!(|data: &[u8]| {

fn decode(data: &[u8]) -> Result<(), image::ImageError> {
use image::ImageDecoder;
let decoder = image::codecs::tga::TgaDecoder::new(std::io::Cursor::new(data))?;
if decoder.total_bytes() > 4_000_000 {
let mut decoder = image::codecs::tga::TgaDecoder::new(std::io::Cursor::new(data))?;
if decoder.peek_layout()?.total_bytes() > 4_000_000 {
return Ok(());
}
let mut buffer = vec![0; decoder.total_bytes() as usize];
let mut buffer = vec![0; decoder.peek_layout()?.total_bytes() as usize];
decoder.read_image(&mut buffer)?;
Ok(())
}
36 changes: 17 additions & 19 deletions src/codecs/avif/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::error::{
DecodingError, ImageFormatHint, LimitError, LimitErrorKind, UnsupportedError,
UnsupportedErrorKind,
};
use crate::io::DecodedImageAttributes;
use crate::{ColorType, ImageDecoder, ImageError, ImageFormat, ImageResult};
///
/// The [AVIF] specification defines an image derivative of the AV1 bitstream, an open video codec.
Expand Down Expand Up @@ -327,32 +328,33 @@ fn get_matrix(
}

impl<R: Read> ImageDecoder for AvifDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
(self.picture.width(), self.picture.height())
}

fn color_type(&self) -> ColorType {
if self.picture.bit_depth() == 8 {
ColorType::Rgba8
} else {
ColorType::Rgba16
}
fn peek_layout(&mut self) -> ImageResult<crate::ImageLayout> {
Ok(crate::ImageLayout {
width: self.picture.width(),
height: self.picture.height(),
color: if self.picture.bit_depth() == 8 {
ColorType::Rgba8
} else {
ColorType::Rgba16
},
})
}

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

fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
fn read_image(&mut self, buf: &mut [u8]) -> ImageResult<DecodedImageAttributes> {
let layout = self.peek_layout()?;
assert_eq!(u64::try_from(buf.len()), Ok(layout.total_bytes()));

let bit_depth = self.picture.bit_depth();

// Normally this should never happen,
// if this happens then there is an incorrect implementation somewhere else
assert!(bit_depth == 8 || bit_depth == 10 || bit_depth == 12);

let (width, height) = self.dimensions();
let (width, height) = layout.dimensions();
// This is suspicious if this happens, better fail early
if width == 0 || height == 0 {
return Err(ImageError::Limits(LimitError::from_kind(
Expand Down Expand Up @@ -439,7 +441,7 @@ impl<R: Read> ImageDecoder for AvifDecoder<R> {
}

// Squashing alpha plane into a picture
if let Some(picture) = self.alpha_picture {
if let Some(picture) = &self.alpha_picture {
if picture.pixel_layout() != PixelLayout::I400 {
return Err(ImageError::Decoding(DecodingError::new(
ImageFormat::Avif.into(),
Expand Down Expand Up @@ -476,11 +478,7 @@ impl<R: Read> ImageDecoder for AvifDecoder<R> {
}
}

Ok(())
}

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

Expand Down
43 changes: 21 additions & 22 deletions src/codecs/bmp/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::color::ColorType;
use crate::error::{
DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
};
use crate::io::ReadExt;
use crate::io::{DecodedImageAttributes, ReadExt};
use crate::{ImageDecoder, ImageFormat};

const BITMAPCOREHEADER_SIZE: u32 = 12;
Expand Down Expand Up @@ -1386,31 +1386,29 @@ impl<R: BufRead + Seek> BmpDecoder<R> {
}

impl<R: BufRead + Seek> ImageDecoder for BmpDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
(self.width as u32, self.height as u32)
}

fn color_type(&self) -> ColorType {
if self.indexed_color {
ColorType::L8
} else if self.add_alpha_channel {
ColorType::Rgba8
} else {
ColorType::Rgb8
}
fn peek_layout(&mut self) -> ImageResult<crate::ImageLayout> {
Ok(crate::ImageLayout {
width: self.width as u32,
height: self.height as u32,
color: if self.indexed_color {
ColorType::L8
} else if self.add_alpha_channel {
ColorType::Rgba8
} else {
ColorType::Rgb8
},
})
}

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

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)
fn read_image(&mut self, buf: &mut [u8]) -> ImageResult<DecodedImageAttributes> {
let layout = self.peek_layout()?;
assert_eq!(u64::try_from(buf.len()), Ok(layout.total_bytes()));
self.read_image_data(buf)?;
Ok(DecodedImageAttributes::default())
}
}

Expand Down Expand Up @@ -1458,8 +1456,9 @@ mod test {
0x4d, 0x00, 0x2a, 0x00,
];

let decoder = BmpDecoder::new(Cursor::new(&data)).unwrap();
let mut buf = vec![0; usize::try_from(decoder.total_bytes()).unwrap()];
let mut decoder = BmpDecoder::new(Cursor::new(&data)).unwrap();
let layout = decoder.peek_layout().unwrap();
let mut buf = vec![0; usize::try_from(layout.total_bytes()).unwrap()];
assert!(decoder.read_image(&mut buf).is_ok());
}

Expand Down
6 changes: 3 additions & 3 deletions src/codecs/bmp/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,9 +376,9 @@ mod tests {
.expect("could not encode image");
}

let decoder = BmpDecoder::new(Cursor::new(&encoded_data)).expect("failed to decode");

let mut buf = vec![0; decoder.total_bytes() as usize];
let mut decoder = BmpDecoder::new(Cursor::new(&encoded_data)).expect("failed to decode");
let layout = decoder.peek_layout().unwrap();
let mut buf = vec![0; layout.total_bytes() as usize];
decoder.read_image(&mut buf).expect("failed to decode");
buf
}
Expand Down
16 changes: 4 additions & 12 deletions src/codecs/dds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ use byteorder_lite::{LittleEndian, ReadBytesExt};

#[allow(deprecated)]
use crate::codecs::dxt::{DxtDecoder, DxtVariant};
use crate::color::ColorType;
use crate::error::{
DecodingError, ImageError, ImageFormatHint, ImageResult, UnsupportedError, UnsupportedErrorKind,
};
use crate::io::DecodedImageAttributes;
use crate::{ImageDecoder, ImageFormat};

/// Errors that can occur during decoding and parsing a DDS image
Expand Down Expand Up @@ -326,21 +326,13 @@ impl<R: Read> DdsDecoder<R> {
}

impl<R: Read> ImageDecoder for DdsDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
self.inner.dimensions()
fn peek_layout(&mut self) -> ImageResult<crate::ImageLayout> {
self.inner.peek_layout()
}

fn color_type(&self) -> ColorType {
self.inner.color_type()
}

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

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

#[cfg(test)]
Expand Down
23 changes: 11 additions & 12 deletions src/codecs/dxt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::io::{self, Read};

use crate::color::ColorType;
use crate::error::{ImageError, ImageResult, ParameterError, ParameterErrorKind};
use crate::io::DecodedImageAttributes;
use crate::io::ReadExt;
use crate::ImageDecoder;

Expand Down Expand Up @@ -129,26 +130,24 @@ impl<R: Read> DxtDecoder<R> {
// Note that, due to the way that DXT compression works, a scanline is considered to consist out of
// 4 lines of pixels.
impl<R: Read> ImageDecoder for DxtDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
(self.width_blocks * 4, self.height_blocks * 4)
}

fn color_type(&self) -> ColorType {
self.variant.color_type()
fn peek_layout(&mut self) -> ImageResult<crate::ImageLayout> {
Ok(crate::ImageLayout {
width: self.width_blocks * 4,
height: self.height_blocks * 4,
color: self.variant.color_type(),
})
}

fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
fn read_image(&mut self, buf: &mut [u8]) -> ImageResult<DecodedImageAttributes> {
let layout = self.peek_layout()?;
assert_eq!(u64::try_from(buf.len()), Ok(layout.total_bytes()));

#[allow(deprecated)]
for chunk in buf.chunks_mut(self.scanline_bytes().max(1) as usize) {
self.read_scanline(chunk)?;
}
Ok(())
}

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

Expand Down
Loading
Loading