aboutsummaryrefslogtreecommitdiff
path: root/vendor/image/src/codecs/hdr/decoder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/image/src/codecs/hdr/decoder.rs')
-rw-r--r--vendor/image/src/codecs/hdr/decoder.rs1033
1 files changed, 0 insertions, 1033 deletions
diff --git a/vendor/image/src/codecs/hdr/decoder.rs b/vendor/image/src/codecs/hdr/decoder.rs
deleted file mode 100644
index 8329d57..0000000
--- a/vendor/image/src/codecs/hdr/decoder.rs
+++ /dev/null
@@ -1,1033 +0,0 @@
-use crate::Primitive;
-use num_traits::identities::Zero;
-#[cfg(test)]
-use std::borrow::Cow;
-use std::convert::TryFrom;
-use std::io::{self, BufRead, Cursor, Read, Seek};
-use std::iter::Iterator;
-use std::marker::PhantomData;
-use std::num::{ParseFloatError, ParseIntError};
-use std::path::Path;
-use std::{error, fmt, mem};
-
-use crate::color::{ColorType, Rgb};
-use crate::error::{
- DecodingError, ImageError, ImageFormatHint, ImageResult, ParameterError, ParameterErrorKind,
- UnsupportedError, UnsupportedErrorKind,
-};
-use crate::image::{self, ImageDecoder, ImageDecoderRect, ImageFormat, Progress};
-
-/// Errors that can occur during decoding and parsing of a HDR image
-#[derive(Debug, Clone, PartialEq, Eq)]
-enum DecoderError {
- /// HDR's "#?RADIANCE" signature wrong or missing
- RadianceHdrSignatureInvalid,
- /// EOF before end of header
- TruncatedHeader,
- /// EOF instead of image dimensions
- TruncatedDimensions,
-
- /// A value couldn't be parsed
- UnparsableF32(LineType, ParseFloatError),
- /// A value couldn't be parsed
- UnparsableU32(LineType, ParseIntError),
- /// Not enough numbers in line
- LineTooShort(LineType),
-
- /// COLORCORR contains too many numbers in strict mode
- ExtraneousColorcorrNumbers,
-
- /// Dimensions line had too few elements
- DimensionsLineTooShort(usize, usize),
- /// Dimensions line had too many elements
- DimensionsLineTooLong(usize),
-
- /// The length of a scanline (1) wasn't a match for the specified length (2)
- WrongScanlineLength(usize, usize),
- /// First pixel of a scanline is a run length marker
- FirstPixelRlMarker,
-}
-
-impl fmt::Display for DecoderError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- DecoderError::RadianceHdrSignatureInvalid => {
- f.write_str("Radiance HDR signature not found")
- }
- DecoderError::TruncatedHeader => f.write_str("EOF in header"),
- DecoderError::TruncatedDimensions => f.write_str("EOF in dimensions line"),
- DecoderError::UnparsableF32(line, pe) => {
- f.write_fmt(format_args!("Cannot parse {} value as f32: {}", line, pe))
- }
- DecoderError::UnparsableU32(line, pe) => {
- f.write_fmt(format_args!("Cannot parse {} value as u32: {}", line, pe))
- }
- DecoderError::LineTooShort(line) => {
- f.write_fmt(format_args!("Not enough numbers in {}", line))
- }
- DecoderError::ExtraneousColorcorrNumbers => f.write_str("Extra numbers in COLORCORR"),
- DecoderError::DimensionsLineTooShort(elements, expected) => f.write_fmt(format_args!(
- "Dimensions line too short: have {} elements, expected {}",
- elements, expected
- )),
- DecoderError::DimensionsLineTooLong(expected) => f.write_fmt(format_args!(
- "Dimensions line too long, expected {} elements",
- expected
- )),
- DecoderError::WrongScanlineLength(len, expected) => f.write_fmt(format_args!(
- "Wrong length of decoded scanline: got {}, expected {}",
- len, expected
- )),
- DecoderError::FirstPixelRlMarker => {
- f.write_str("First pixel of a scanline shouldn't be run length marker")
- }
- }
- }
-}
-
-impl From<DecoderError> for ImageError {
- fn from(e: DecoderError) -> ImageError {
- ImageError::Decoding(DecodingError::new(ImageFormat::Hdr.into(), e))
- }
-}
-
-impl error::Error for DecoderError {
- fn source(&self) -> Option<&(dyn error::Error + 'static)> {
- match self {
- DecoderError::UnparsableF32(_, err) => Some(err),
- DecoderError::UnparsableU32(_, err) => Some(err),
- _ => None,
- }
- }
-}
-
-/// Lines which contain parsable data that can fail
-#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
-enum LineType {
- Exposure,
- Pixaspect,
- Colorcorr,
- DimensionsHeight,
- DimensionsWidth,
-}
-
-impl fmt::Display for LineType {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str(match self {
- LineType::Exposure => "EXPOSURE",
- LineType::Pixaspect => "PIXASPECT",
- LineType::Colorcorr => "COLORCORR",
- LineType::DimensionsHeight => "height dimension",
- LineType::DimensionsWidth => "width dimension",
- })
- }
-}
-
-/// Adapter to conform to `ImageDecoder` trait
-#[derive(Debug)]
-pub struct HdrAdapter<R: Read> {
- inner: Option<HdrDecoder<R>>,
- // data: Option<Vec<u8>>,
- meta: HdrMetadata,
-}
-
-impl<R: BufRead> HdrAdapter<R> {
- /// Creates adapter
- pub fn new(r: R) -> ImageResult<HdrAdapter<R>> {
- let decoder = HdrDecoder::new(r)?;
- let meta = decoder.metadata();
- Ok(HdrAdapter {
- inner: Some(decoder),
- meta,
- })
- }
-
- /// Allows reading old Radiance HDR images
- pub fn new_nonstrict(r: R) -> ImageResult<HdrAdapter<R>> {
- let decoder = HdrDecoder::with_strictness(r, false)?;
- let meta = decoder.metadata();
- Ok(HdrAdapter {
- inner: Some(decoder),
- meta,
- })
- }
-
- /// Read the actual data of the image, and store it in Self::data.
- fn read_image_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
- assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
- match self.inner.take() {
- Some(decoder) => {
- let img: Vec<Rgb<u8>> = decoder.read_image_ldr()?;
- for (i, Rgb(data)) in img.into_iter().enumerate() {
- buf[(i * 3)..][..3].copy_from_slice(&data);
- }
-
- Ok(())
- }
- None => Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::NoMoreData,
- ))),
- }
- }
-}
-
-/// Wrapper struct around a `Cursor<Vec<u8>>`
-pub struct HdrReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
-impl<R> Read for HdrReader<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 + BufRead> ImageDecoder<'a> for HdrAdapter<R> {
- type Reader = HdrReader<R>;
-
- fn dimensions(&self) -> (u32, u32) {
- (self.meta.width, self.meta.height)
- }
-
- fn color_type(&self) -> ColorType {
- ColorType::Rgb8
- }
-
- fn into_reader(self) -> ImageResult<Self::Reader> {
- Ok(HdrReader(
- Cursor::new(image::decoder_to_vec(self)?),
- PhantomData,
- ))
- }
-
- fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
- self.read_image_data(buf)
- }
-}
-
-impl<'a, R: 'a + BufRead + Seek> ImageDecoderRect<'a> for HdrAdapter<R> {
- fn read_rect_with_progress<F: Fn(Progress)>(
- &mut self,
- x: u32,
- y: u32,
- width: u32,
- height: u32,
- buf: &mut [u8],
- progress_callback: F,
- ) -> ImageResult<()> {
- image::load_rect(
- x,
- y,
- width,
- height,
- buf,
- progress_callback,
- self,
- |_, _| unreachable!(),
- |s, buf| s.read_image_data(buf),
- )
- }
-}
-
-/// Radiance HDR file signature
-pub const SIGNATURE: &[u8] = b"#?RADIANCE";
-const SIGNATURE_LENGTH: usize = 10;
-
-/// An Radiance HDR decoder
-#[derive(Debug)]
-pub struct HdrDecoder<R> {
- r: R,
- width: u32,
- height: u32,
- meta: HdrMetadata,
-}
-
-/// Refer to [wikipedia](https://en.wikipedia.org/wiki/RGBE_image_format)
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
-pub struct Rgbe8Pixel {
- /// Color components
- pub c: [u8; 3],
- /// Exponent
- pub e: u8,
-}
-
-/// Creates `Rgbe8Pixel` from components
-pub fn rgbe8(r: u8, g: u8, b: u8, e: u8) -> Rgbe8Pixel {
- Rgbe8Pixel { c: [r, g, b], e }
-}
-
-impl Rgbe8Pixel {
- /// Converts `Rgbe8Pixel` into `Rgb<f32>` linearly
- #[inline]
- pub fn to_hdr(self) -> Rgb<f32> {
- if self.e == 0 {
- Rgb([0.0, 0.0, 0.0])
- } else {
- // let exp = f32::ldexp(1., self.e as isize - (128 + 8)); // unstable
- let exp = f32::exp2(<f32 as From<_>>::from(self.e) - (128.0 + 8.0));
- Rgb([
- exp * <f32 as From<_>>::from(self.c[0]),
- exp * <f32 as From<_>>::from(self.c[1]),
- exp * <f32 as From<_>>::from(self.c[2]),
- ])
- }
- }
-
- /// Converts `Rgbe8Pixel` into `Rgb<T>` with scale=1 and gamma=2.2
- ///
- /// color_ldr = (color_hdr*scale)<sup>gamma</sup>
- ///
- /// # Panic
- ///
- /// Panics when `T::max_value()` cannot be represented as f32.
- #[inline]
- pub fn to_ldr<T: Primitive + Zero>(self) -> Rgb<T> {
- self.to_ldr_scale_gamma(1.0, 2.2)
- }
-
- /// Converts `Rgbe8Pixel` into `Rgb<T>` using provided scale and gamma
- ///
- /// color_ldr = (color_hdr*scale)<sup>gamma</sup>
- ///
- /// # Panic
- ///
- /// Panics when `T::max_value()` cannot be represented as f32.
- /// Panics when scale or gamma is NaN
- #[inline]
- pub fn to_ldr_scale_gamma<T: Primitive + Zero>(self, scale: f32, gamma: f32) -> Rgb<T> {
- let Rgb(data) = self.to_hdr();
- let (r, g, b) = (data[0], data[1], data[2]);
- #[inline]
- fn sg<T: Primitive + Zero>(v: f32, scale: f32, gamma: f32) -> T {
- let t_max = T::max_value();
- // Disassembly shows that t_max_f32 is compiled into constant
- let t_max_f32: f32 = num_traits::NumCast::from(t_max)
- .expect("to_ldr_scale_gamma: maximum value of type is not representable as f32");
- let fv = f32::powf(v * scale, gamma) * t_max_f32 + 0.5;
- if fv < 0.0 {
- T::zero()
- } else if fv > t_max_f32 {
- t_max
- } else {
- num_traits::NumCast::from(fv)
- .expect("to_ldr_scale_gamma: cannot convert f32 to target type. NaN?")
- }
- }
- Rgb([
- sg(r, scale, gamma),
- sg(g, scale, gamma),
- sg(b, scale, gamma),
- ])
- }
-}
-
-impl<R: BufRead> HdrDecoder<R> {
- /// Reads Radiance HDR image header from stream `r`
- /// if the header is valid, creates HdrDecoder
- /// strict mode is enabled
- pub fn new(reader: R) -> ImageResult<HdrDecoder<R>> {
- HdrDecoder::with_strictness(reader, true)
- }
-
- /// Reads Radiance HDR image header from stream `reader`,
- /// if the header is valid, creates `HdrDecoder`.
- ///
- /// strict enables strict mode
- ///
- /// Warning! Reading wrong file in non-strict mode
- /// could consume file size worth of memory in the process.
- pub fn with_strictness(mut reader: R, strict: bool) -> ImageResult<HdrDecoder<R>> {
- let mut attributes = HdrMetadata::new();
-
- {
- // scope to make borrowck happy
- let r = &mut reader;
- if strict {
- let mut signature = [0; SIGNATURE_LENGTH];
- r.read_exact(&mut signature)?;
- if signature != SIGNATURE {
- return Err(DecoderError::RadianceHdrSignatureInvalid.into());
- } // no else
- // skip signature line ending
- read_line_u8(r)?;
- } else {
- // Old Radiance HDR files (*.pic) don't use signature
- // Let them be parsed in non-strict mode
- }
- // read header data until empty line
- loop {
- match read_line_u8(r)? {
- None => {
- // EOF before end of header
- return Err(DecoderError::TruncatedHeader.into());
- }
- Some(line) => {
- if line.is_empty() {
- // end of header
- break;
- } else if line[0] == b'#' {
- // line[0] will not panic, line.len() == 0 is false here
- // skip comments
- continue;
- } // no else
- // process attribute line
- let line = String::from_utf8_lossy(&line[..]);
- attributes.update_header_info(&line, strict)?;
- } // <= Some(line)
- } // match read_line_u8()
- } // loop
- } // scope to end borrow of reader
- // parse dimensions
- let (width, height) = match read_line_u8(&mut reader)? {
- None => {
- // EOF instead of image dimensions
- return Err(DecoderError::TruncatedDimensions.into());
- }
- Some(dimensions) => {
- let dimensions = String::from_utf8_lossy(&dimensions[..]);
- parse_dimensions_line(&dimensions, strict)?
- }
- };
-
- // color type is always rgb8
- if crate::utils::check_dimension_overflow(width, height, ColorType::Rgb8.bytes_per_pixel())
- {
- return Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Hdr.into(),
- UnsupportedErrorKind::GenericFeature(format!(
- "Image dimensions ({}x{}) are too large",
- width, height
- )),
- ),
- ));
- }
-
- Ok(HdrDecoder {
- r: reader,
-
- width,
- height,
- meta: HdrMetadata {
- width,
- height,
- ..attributes
- },
- })
- } // end with_strictness
-
- /// Returns file metadata. Refer to `HdrMetadata` for details.
- pub fn metadata(&self) -> HdrMetadata {
- self.meta.clone()
- }
-
- /// Consumes decoder and returns a vector of RGBE8 pixels
- pub fn read_image_native(mut self) -> ImageResult<Vec<Rgbe8Pixel>> {
- // Don't read anything if image is empty
- if self.width == 0 || self.height == 0 {
- return Ok(vec![]);
- }
- // expression self.width > 0 && self.height > 0 is true from now to the end of this method
- let pixel_count = self.width as usize * self.height as usize;
- let mut ret = vec![Default::default(); pixel_count];
- for chunk in ret.chunks_mut(self.width as usize) {
- read_scanline(&mut self.r, chunk)?;
- }
- Ok(ret)
- }
-
- /// Consumes decoder and returns a vector of transformed pixels
- pub fn read_image_transform<T: Send, F: Send + Sync + Fn(Rgbe8Pixel) -> T>(
- mut self,
- f: F,
- output_slice: &mut [T],
- ) -> ImageResult<()> {
- assert_eq!(
- output_slice.len(),
- self.width as usize * self.height as usize
- );
-
- // Don't read anything if image is empty
- if self.width == 0 || self.height == 0 {
- return Ok(());
- }
-
- let chunks_iter = output_slice.chunks_mut(self.width as usize);
-
- let mut buf = vec![Default::default(); self.width as usize];
- for chunk in chunks_iter {
- // read_scanline overwrites the entire buffer or returns an Err,
- // so not resetting the buffer here is ok.
- read_scanline(&mut self.r, &mut buf[..])?;
- for (dst, &pix) in chunk.iter_mut().zip(buf.iter()) {
- *dst = f(pix);
- }
- }
- Ok(())
- }
-
- /// Consumes decoder and returns a vector of `Rgb<u8>` pixels.
- /// scale = 1, gamma = 2.2
- pub fn read_image_ldr(self) -> ImageResult<Vec<Rgb<u8>>> {
- let mut ret = vec![Rgb([0, 0, 0]); self.width as usize * self.height as usize];
- self.read_image_transform(|pix| pix.to_ldr(), &mut ret[..])?;
- Ok(ret)
- }
-
- /// Consumes decoder and returns a vector of `Rgb<f32>` pixels.
- ///
- pub fn read_image_hdr(self) -> ImageResult<Vec<Rgb<f32>>> {
- let mut ret = vec![Rgb([0.0, 0.0, 0.0]); self.width as usize * self.height as usize];
- self.read_image_transform(|pix| pix.to_hdr(), &mut ret[..])?;
- Ok(ret)
- }
-}
-
-impl<R: Read> IntoIterator for HdrDecoder<R> {
- type Item = ImageResult<Rgbe8Pixel>;
- type IntoIter = HdrImageDecoderIterator<R>;
-
- fn into_iter(self) -> Self::IntoIter {
- HdrImageDecoderIterator {
- r: self.r,
- scanline_cnt: self.height as usize,
- buf: vec![Default::default(); self.width as usize],
- col: 0,
- scanline: 0,
- trouble: true, // make first call to `next()` read scanline
- error_encountered: false,
- }
- }
-}
-
-/// Scanline buffered pixel by pixel iterator
-pub struct HdrImageDecoderIterator<R: Read> {
- r: R,
- scanline_cnt: usize,
- buf: Vec<Rgbe8Pixel>, // scanline buffer
- col: usize, // current position in scanline
- scanline: usize, // current scanline
- trouble: bool, // optimization, true indicates that we need to check something
- error_encountered: bool,
-}
-
-impl<R: Read> HdrImageDecoderIterator<R> {
- // Advances counter to the next pixel
- #[inline]
- fn advance(&mut self) {
- self.col += 1;
- if self.col == self.buf.len() {
- self.col = 0;
- self.scanline += 1;
- self.trouble = true;
- }
- }
-}
-
-impl<R: Read> Iterator for HdrImageDecoderIterator<R> {
- type Item = ImageResult<Rgbe8Pixel>;
-
- fn next(&mut self) -> Option<Self::Item> {
- if !self.trouble {
- let ret = self.buf[self.col];
- self.advance();
- Some(Ok(ret))
- } else {
- // some condition is pending
- if self.buf.is_empty() || self.scanline == self.scanline_cnt {
- // No more pixels
- return None;
- } // no else
- if self.error_encountered {
- self.advance();
- // Error was encountered. Keep producing errors.
- // ImageError can't implement Clone, so just dump some error
- return Some(Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::FailedAlready,
- ))));
- } // no else
- if self.col == 0 {
- // fill scanline buffer
- match read_scanline(&mut self.r, &mut self.buf[..]) {
- Ok(_) => {
- // no action required
- }
- Err(err) => {
- self.advance();
- self.error_encountered = true;
- self.trouble = true;
- return Some(Err(err));
- }
- }
- } // no else
- self.trouble = false;
- let ret = self.buf[0];
- self.advance();
- Some(Ok(ret))
- }
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- let total_cnt = self.buf.len() * self.scanline_cnt;
- let cur_cnt = self.buf.len() * self.scanline + self.col;
- let remaining = total_cnt - cur_cnt;
- (remaining, Some(remaining))
- }
-}
-
-impl<R: Read> ExactSizeIterator for HdrImageDecoderIterator<R> {}
-
-// Precondition: buf.len() > 0
-fn read_scanline<R: Read>(r: &mut R, buf: &mut [Rgbe8Pixel]) -> ImageResult<()> {
- assert!(!buf.is_empty());
- let width = buf.len();
- // first 4 bytes in scanline allow to determine compression method
- let fb = read_rgbe(r)?;
- if fb.c[0] == 2 && fb.c[1] == 2 && fb.c[2] < 128 {
- // denormalized pixel value (2,2,<128,_) indicates new per component RLE method
- // decode_component guarantees that offset is within 0 .. width
- // therefore we can skip bounds checking here, but we will not
- decode_component(r, width, |offset, value| buf[offset].c[0] = value)?;
- decode_component(r, width, |offset, value| buf[offset].c[1] = value)?;
- decode_component(r, width, |offset, value| buf[offset].c[2] = value)?;
- decode_component(r, width, |offset, value| buf[offset].e = value)?;
- } else {
- // old RLE method (it was considered old around 1991, should it be here?)
- decode_old_rle(r, fb, buf)?;
- }
- Ok(())
-}
-
-#[inline(always)]
-fn read_byte<R: Read>(r: &mut R) -> io::Result<u8> {
- let mut buf = [0u8];
- r.read_exact(&mut buf[..])?;
- Ok(buf[0])
-}
-
-// Guarantees that first parameter of set_component will be within pos .. pos+width
-#[inline]
-fn decode_component<R: Read, S: FnMut(usize, u8)>(
- r: &mut R,
- width: usize,
- mut set_component: S,
-) -> ImageResult<()> {
- let mut buf = [0; 128];
- let mut pos = 0;
- while pos < width {
- // increment position by a number of decompressed values
- pos += {
- let rl = read_byte(r)?;
- if rl <= 128 {
- // sanity check
- if pos + rl as usize > width {
- return Err(DecoderError::WrongScanlineLength(pos + rl as usize, width).into());
- }
- // read values
- r.read_exact(&mut buf[0..rl as usize])?;
- for (offset, &value) in buf[0..rl as usize].iter().enumerate() {
- set_component(pos + offset, value);
- }
- rl as usize
- } else {
- // run
- let rl = rl - 128;
- // sanity check
- if pos + rl as usize > width {
- return Err(DecoderError::WrongScanlineLength(pos + rl as usize, width).into());
- }
- // fill with same value
- let value = read_byte(r)?;
- for offset in 0..rl as usize {
- set_component(pos + offset, value);
- }
- rl as usize
- }
- };
- }
- if pos != width {
- return Err(DecoderError::WrongScanlineLength(pos, width).into());
- }
- Ok(())
-}
-
-// Decodes scanline, places it into buf
-// Precondition: buf.len() > 0
-// fb - first 4 bytes of scanline
-fn decode_old_rle<R: Read>(r: &mut R, fb: Rgbe8Pixel, buf: &mut [Rgbe8Pixel]) -> ImageResult<()> {
- assert!(!buf.is_empty());
- let width = buf.len();
- // convenience function.
- // returns run length if pixel is a run length marker
- #[inline]
- fn rl_marker(pix: Rgbe8Pixel) -> Option<usize> {
- if pix.c == [1, 1, 1] {
- Some(pix.e as usize)
- } else {
- None
- }
- }
- // first pixel in scanline should not be run length marker
- // it is error if it is
- if rl_marker(fb).is_some() {
- return Err(DecoderError::FirstPixelRlMarker.into());
- }
- buf[0] = fb; // set first pixel of scanline
-
- let mut x_off = 1; // current offset from beginning of a scanline
- let mut rl_mult = 1; // current run length multiplier
- let mut prev_pixel = fb;
- while x_off < width {
- let pix = read_rgbe(r)?;
- // it's harder to forget to increase x_off if I write this this way.
- x_off += {
- if let Some(rl) = rl_marker(pix) {
- // rl_mult takes care of consecutive RL markers
- let rl = rl * rl_mult;
- rl_mult *= 256;
- if x_off + rl <= width {
- // do run
- for b in &mut buf[x_off..x_off + rl] {
- *b = prev_pixel;
- }
- } else {
- return Err(DecoderError::WrongScanlineLength(x_off + rl, width).into());
- };
- rl // value to increase x_off by
- } else {
- rl_mult = 1; // chain of consecutive RL markers is broken
- prev_pixel = pix;
- buf[x_off] = pix;
- 1 // value to increase x_off by
- }
- };
- }
- if x_off != width {
- return Err(DecoderError::WrongScanlineLength(x_off, width).into());
- }
- Ok(())
-}
-
-fn read_rgbe<R: Read>(r: &mut R) -> io::Result<Rgbe8Pixel> {
- let mut buf = [0u8; 4];
- r.read_exact(&mut buf[..])?;
- Ok(Rgbe8Pixel {
- c: [buf[0], buf[1], buf[2]],
- e: buf[3],
- })
-}
-
-/// Metadata for Radiance HDR image
-#[derive(Debug, Clone)]
-pub struct HdrMetadata {
- /// Width of decoded image. It could be either scanline length,
- /// or scanline count, depending on image orientation.
- pub width: u32,
- /// Height of decoded image. It depends on orientation too.
- pub height: u32,
- /// Orientation matrix. For standard orientation it is ((1,0),(0,1)) - left to right, top to bottom.
- /// First pair tells how resulting pixel coordinates change along a scanline.
- /// Second pair tells how they change from one scanline to the next.
- pub orientation: ((i8, i8), (i8, i8)),
- /// Divide color values by exposure to get to get physical radiance in
- /// watts/steradian/m<sup>2</sup>
- ///
- /// Image may not contain physical data, even if this field is set.
- pub exposure: Option<f32>,
- /// Divide color values by corresponding tuple member (r, g, b) to get to get physical radiance
- /// in watts/steradian/m<sup>2</sup>
- ///
- /// Image may not contain physical data, even if this field is set.
- pub color_correction: Option<(f32, f32, f32)>,
- /// Pixel height divided by pixel width
- pub pixel_aspect_ratio: Option<f32>,
- /// All lines contained in image header are put here. Ordering of lines is preserved.
- /// Lines in the form "key=value" are represented as ("key", "value").
- /// All other lines are ("", "line")
- pub custom_attributes: Vec<(String, String)>,
-}
-
-impl HdrMetadata {
- fn new() -> HdrMetadata {
- HdrMetadata {
- width: 0,
- height: 0,
- orientation: ((1, 0), (0, 1)),
- exposure: None,
- color_correction: None,
- pixel_aspect_ratio: None,
- custom_attributes: vec![],
- }
- }
-
- // Updates header info, in strict mode returns error for malformed lines (no '=' separator)
- // unknown attributes are skipped
- fn update_header_info(&mut self, line: &str, strict: bool) -> ImageResult<()> {
- // split line at first '='
- // old Radiance HDR files (*.pic) feature tabs in key, so vvv trim
- let maybe_key_value = split_at_first(line, "=").map(|(key, value)| (key.trim(), value));
- // save all header lines in custom_attributes
- match maybe_key_value {
- Some((key, val)) => self
- .custom_attributes
- .push((key.to_owned(), val.to_owned())),
- None => self.custom_attributes.push(("".into(), line.to_owned())),
- }
- // parse known attributes
- match maybe_key_value {
- Some(("FORMAT", val)) => {
- if val.trim() != "32-bit_rle_rgbe" {
- // XYZE isn't supported yet
- return Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Hdr.into(),
- UnsupportedErrorKind::Format(ImageFormatHint::Name(limit_string_len(
- val, 20,
- ))),
- ),
- ));
- }
- }
- Some(("EXPOSURE", val)) => {
- match val.trim().parse::<f32>() {
- Ok(v) => {
- self.exposure = Some(self.exposure.unwrap_or(1.0) * v); // all encountered exposure values should be multiplied
- }
- Err(parse_error) => {
- if strict {
- return Err(DecoderError::UnparsableF32(
- LineType::Exposure,
- parse_error,
- )
- .into());
- } // no else, skip this line in non-strict mode
- }
- };
- }
- Some(("PIXASPECT", val)) => {
- match val.trim().parse::<f32>() {
- Ok(v) => {
- self.pixel_aspect_ratio = Some(self.pixel_aspect_ratio.unwrap_or(1.0) * v);
- // all encountered exposure values should be multiplied
- }
- Err(parse_error) => {
- if strict {
- return Err(DecoderError::UnparsableF32(
- LineType::Pixaspect,
- parse_error,
- )
- .into());
- } // no else, skip this line in non-strict mode
- }
- };
- }
- Some(("COLORCORR", val)) => {
- let mut rgbcorr = [1.0, 1.0, 1.0];
- match parse_space_separated_f32(val, &mut rgbcorr, LineType::Colorcorr) {
- Ok(extra_numbers) => {
- if strict && extra_numbers {
- return Err(DecoderError::ExtraneousColorcorrNumbers.into());
- } // no else, just ignore extra numbers
- let (rc, gc, bc) = self.color_correction.unwrap_or((1.0, 1.0, 1.0));
- self.color_correction =
- Some((rc * rgbcorr[0], gc * rgbcorr[1], bc * rgbcorr[2]));
- }
- Err(err) => {
- if strict {
- return Err(err);
- } // no else, skip malformed line in non-strict mode
- }
- }
- }
- None => {
- // old Radiance HDR files (*.pic) contain commands in a header
- // just skip them
- }
- _ => {
- // skip unknown attribute
- }
- } // match attributes
- Ok(())
- }
-}
-
-fn parse_space_separated_f32(line: &str, vals: &mut [f32], line_tp: LineType) -> ImageResult<bool> {
- let mut nums = line.split_whitespace();
- for val in vals.iter_mut() {
- if let Some(num) = nums.next() {
- match num.parse::<f32>() {
- Ok(v) => *val = v,
- Err(err) => return Err(DecoderError::UnparsableF32(line_tp, err).into()),
- }
- } else {
- // not enough numbers in line
- return Err(DecoderError::LineTooShort(line_tp).into());
- }
- }
- Ok(nums.next().is_some())
-}
-
-// Parses dimension line "-Y height +X width"
-// returns (width, height) or error
-fn parse_dimensions_line(line: &str, strict: bool) -> ImageResult<(u32, u32)> {
- const DIMENSIONS_COUNT: usize = 4;
-
- let mut dim_parts = line.split_whitespace();
- let c1_tag = dim_parts
- .next()
- .ok_or(DecoderError::DimensionsLineTooShort(0, DIMENSIONS_COUNT))?;
- let c1_str = dim_parts
- .next()
- .ok_or(DecoderError::DimensionsLineTooShort(1, DIMENSIONS_COUNT))?;
- let c2_tag = dim_parts
- .next()
- .ok_or(DecoderError::DimensionsLineTooShort(2, DIMENSIONS_COUNT))?;
- let c2_str = dim_parts
- .next()
- .ok_or(DecoderError::DimensionsLineTooShort(3, DIMENSIONS_COUNT))?;
- if strict && dim_parts.next().is_some() {
- // extra data in dimensions line
- return Err(DecoderError::DimensionsLineTooLong(DIMENSIONS_COUNT).into());
- } // no else
- // dimensions line is in the form "-Y 10 +X 20"
- // There are 8 possible orientations: +Y +X, +X -Y and so on
- match (c1_tag, c2_tag) {
- ("-Y", "+X") => {
- // Common orientation (left-right, top-down)
- // c1_str is height, c2_str is width
- let height = c1_str
- .parse::<u32>()
- .map_err(|pe| DecoderError::UnparsableU32(LineType::DimensionsHeight, pe))?;
- let width = c2_str
- .parse::<u32>()
- .map_err(|pe| DecoderError::UnparsableU32(LineType::DimensionsWidth, pe))?;
- Ok((width, height))
- }
- _ => Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Hdr.into(),
- UnsupportedErrorKind::GenericFeature(format!(
- "Orientation {} {}",
- limit_string_len(c1_tag, 4),
- limit_string_len(c2_tag, 4)
- )),
- ),
- )),
- } // final expression. Returns value
-}
-
-// Returns string with no more than len+3 characters
-fn limit_string_len(s: &str, len: usize) -> String {
- let s_char_len = s.chars().count();
- if s_char_len > len {
- s.chars().take(len).chain("...".chars()).collect()
- } else {
- s.into()
- }
-}
-
-// Splits string into (before separator, after separator) tuple
-// or None if separator isn't found
-fn split_at_first<'a>(s: &'a str, separator: &str) -> Option<(&'a str, &'a str)> {
- match s.find(separator) {
- None | Some(0) => None,
- Some(p) if p >= s.len() - separator.len() => None,
- Some(p) => Some((&s[..p], &s[(p + separator.len())..])),
- }
-}
-
-#[test]
-fn split_at_first_test() {
- assert_eq!(split_at_first(&Cow::Owned("".into()), "="), None);
- assert_eq!(split_at_first(&Cow::Owned("=".into()), "="), None);
- assert_eq!(split_at_first(&Cow::Owned("= ".into()), "="), None);
- assert_eq!(
- split_at_first(&Cow::Owned(" = ".into()), "="),
- Some((" ", " "))
- );
- assert_eq!(
- split_at_first(&Cow::Owned("EXPOSURE= ".into()), "="),
- Some(("EXPOSURE", " "))
- );
- assert_eq!(
- split_at_first(&Cow::Owned("EXPOSURE= =".into()), "="),
- Some(("EXPOSURE", " ="))
- );
- assert_eq!(
- split_at_first(&Cow::Owned("EXPOSURE== =".into()), "=="),
- Some(("EXPOSURE", " ="))
- );
- assert_eq!(split_at_first(&Cow::Owned("EXPOSURE".into()), ""), None);
-}
-
-// Reads input until b"\n" or EOF
-// Returns vector of read bytes NOT including end of line characters
-// or return None to indicate end of file
-fn read_line_u8<R: BufRead>(r: &mut R) -> ::std::io::Result<Option<Vec<u8>>> {
- let mut ret = Vec::with_capacity(16);
- match r.read_until(b'\n', &mut ret) {
- Ok(0) => Ok(None),
- Ok(_) => {
- if let Some(&b'\n') = ret[..].last() {
- let _ = ret.pop();
- }
- Ok(Some(ret))
- }
- Err(err) => Err(err),
- }
-}
-
-#[test]
-fn read_line_u8_test() {
- let buf: Vec<_> = (&b"One\nTwo\nThree\nFour\n\n\n"[..]).into();
- let input = &mut ::std::io::Cursor::new(buf);
- assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"One"[..]);
- assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Two"[..]);
- assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Three"[..]);
- assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Four"[..]);
- assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b""[..]);
- assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b""[..]);
- assert_eq!(read_line_u8(input).unwrap(), None);
-}
-
-/// Helper function for reading raw 3-channel f32 images
-pub fn read_raw_file<P: AsRef<Path>>(path: P) -> ::std::io::Result<Vec<Rgb<f32>>> {
- use byteorder::{LittleEndian as LE, ReadBytesExt};
- use std::fs::File;
- use std::io::BufReader;
-
- let mut r = BufReader::new(File::open(path)?);
- let w = r.read_u32::<LE>()? as usize;
- let h = r.read_u32::<LE>()? as usize;
- let c = r.read_u32::<LE>()? as usize;
- assert_eq!(c, 3);
- let cnt = w * h;
- let mut ret = Vec::with_capacity(cnt);
- for _ in 0..cnt {
- let cr = r.read_f32::<LE>()?;
- let cg = r.read_f32::<LE>()?;
- let cb = r.read_f32::<LE>()?;
- ret.push(Rgb([cr, cg, cb]));
- }
- Ok(ret)
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
- use std::io::Cursor;
-
- #[test]
- fn dimension_overflow() {
- let data = b"#?RADIANCE\nFORMAT=32-bit_rle_rgbe\n\n -Y 4294967295 +X 4294967295";
-
- assert!(HdrAdapter::new(Cursor::new(data)).is_err());
- assert!(HdrAdapter::new_nonstrict(Cursor::new(data)).is_err());
- }
-}