1use crate::Primitive;
2use num_traits::identities::Zero;
3#[cfg(test)]
4use std::borrow::Cow;
5use std::io::{self, BufRead, Cursor, Read, Seek};
6use std::marker::PhantomData;
7use std::num::{ParseFloatError, ParseIntError};
8use std::path::Path;
9use std::{error, fmt, mem};
10
11use crate::color::{ColorType, Rgb};
12use crate::error::{
13 DecodingError, ImageError, ImageFormatHint, ImageResult, ParameterError, ParameterErrorKind,
14 UnsupportedError, UnsupportedErrorKind,
15};
16use crate::image::{self, ImageDecoder, ImageDecoderRect, ImageFormat, Progress};
17
18/// Errors that can occur during decoding and parsing of a HDR image
19#[derive(Debug, Clone, PartialEq, Eq)]
20enum DecoderError {
21 /// HDR's "#?RADIANCE" signature wrong or missing
22 RadianceHdrSignatureInvalid,
23 /// EOF before end of header
24 TruncatedHeader,
25 /// EOF instead of image dimensions
26 TruncatedDimensions,
27
28 /// A value couldn't be parsed
29 UnparsableF32(LineType, ParseFloatError),
30 /// A value couldn't be parsed
31 UnparsableU32(LineType, ParseIntError),
32 /// Not enough numbers in line
33 LineTooShort(LineType),
34
35 /// COLORCORR contains too many numbers in strict mode
36 ExtraneousColorcorrNumbers,
37
38 /// Dimensions line had too few elements
39 DimensionsLineTooShort(usize, usize),
40 /// Dimensions line had too many elements
41 DimensionsLineTooLong(usize),
42
43 /// The length of a scanline (1) wasn't a match for the specified length (2)
44 WrongScanlineLength(usize, usize),
45 /// First pixel of a scanline is a run length marker
46 FirstPixelRlMarker,
47}
48
49impl fmt::Display for DecoderError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 DecoderError::RadianceHdrSignatureInvalid => {
53 f.write_str("Radiance HDR signature not found")
54 }
55 DecoderError::TruncatedHeader => f.write_str("EOF in header"),
56 DecoderError::TruncatedDimensions => f.write_str("EOF in dimensions line"),
57 DecoderError::UnparsableF32(line, pe) => {
58 f.write_fmt(format_args!("Cannot parse {} value as f32: {}", line, pe))
59 }
60 DecoderError::UnparsableU32(line, pe) => {
61 f.write_fmt(format_args!("Cannot parse {} value as u32: {}", line, pe))
62 }
63 DecoderError::LineTooShort(line) => {
64 f.write_fmt(format_args!("Not enough numbers in {}", line))
65 }
66 DecoderError::ExtraneousColorcorrNumbers => f.write_str("Extra numbers in COLORCORR"),
67 DecoderError::DimensionsLineTooShort(elements, expected) => f.write_fmt(format_args!(
68 "Dimensions line too short: have {} elements, expected {}",
69 elements, expected
70 )),
71 DecoderError::DimensionsLineTooLong(expected) => f.write_fmt(format_args!(
72 "Dimensions line too long, expected {} elements",
73 expected
74 )),
75 DecoderError::WrongScanlineLength(len, expected) => f.write_fmt(format_args!(
76 "Wrong length of decoded scanline: got {}, expected {}",
77 len, expected
78 )),
79 DecoderError::FirstPixelRlMarker => {
80 f.write_str("First pixel of a scanline shouldn't be run length marker")
81 }
82 }
83 }
84}
85
86impl From<DecoderError> for ImageError {
87 fn from(e: DecoderError) -> ImageError {
88 ImageError::Decoding(DecodingError::new(format:ImageFormat::Hdr.into(), err:e))
89 }
90}
91
92impl error::Error for DecoderError {
93 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
94 match self {
95 DecoderError::UnparsableF32(_, err: &ParseFloatError) => Some(err),
96 DecoderError::UnparsableU32(_, err: &ParseIntError) => Some(err),
97 _ => None,
98 }
99 }
100}
101
102/// Lines which contain parsable data that can fail
103#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
104enum LineType {
105 Exposure,
106 Pixaspect,
107 Colorcorr,
108 DimensionsHeight,
109 DimensionsWidth,
110}
111
112impl fmt::Display for LineType {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 f.write_str(data:match self {
115 LineType::Exposure => "EXPOSURE",
116 LineType::Pixaspect => "PIXASPECT",
117 LineType::Colorcorr => "COLORCORR",
118 LineType::DimensionsHeight => "height dimension",
119 LineType::DimensionsWidth => "width dimension",
120 })
121 }
122}
123
124/// Adapter to conform to `ImageDecoder` trait
125#[derive(Debug)]
126pub struct HdrAdapter<R: Read> {
127 inner: Option<HdrDecoder<R>>,
128 // data: Option<Vec<u8>>,
129 meta: HdrMetadata,
130}
131
132impl<R: BufRead> HdrAdapter<R> {
133 /// Creates adapter
134 pub fn new(r: R) -> ImageResult<HdrAdapter<R>> {
135 let decoder = HdrDecoder::new(r)?;
136 let meta = decoder.metadata();
137 Ok(HdrAdapter {
138 inner: Some(decoder),
139 meta,
140 })
141 }
142
143 /// Allows reading old Radiance HDR images
144 pub fn new_nonstrict(r: R) -> ImageResult<HdrAdapter<R>> {
145 let decoder = HdrDecoder::with_strictness(r, false)?;
146 let meta = decoder.metadata();
147 Ok(HdrAdapter {
148 inner: Some(decoder),
149 meta,
150 })
151 }
152
153 /// Read the actual data of the image, and store it in Self::data.
154 fn read_image_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
155 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
156 match self.inner.take() {
157 Some(decoder) => {
158 let img: Vec<Rgb<u8>> = decoder.read_image_ldr()?;
159 for (i, Rgb(data)) in img.into_iter().enumerate() {
160 buf[(i * 3)..][..3].copy_from_slice(&data);
161 }
162
163 Ok(())
164 }
165 None => Err(ImageError::Parameter(ParameterError::from_kind(
166 ParameterErrorKind::NoMoreData,
167 ))),
168 }
169 }
170}
171
172/// Wrapper struct around a `Cursor<Vec<u8>>`
173pub struct HdrReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
174impl<R> Read for HdrReader<R> {
175 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
176 self.0.read(buf)
177 }
178 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
179 if self.0.position() == 0 && buf.is_empty() {
180 mem::swap(x:buf, self.0.get_mut());
181 Ok(buf.len())
182 } else {
183 self.0.read_to_end(buf)
184 }
185 }
186}
187
188impl<'a, R: 'a + BufRead> ImageDecoder<'a> for HdrAdapter<R> {
189 type Reader = HdrReader<R>;
190
191 fn dimensions(&self) -> (u32, u32) {
192 (self.meta.width, self.meta.height)
193 }
194
195 fn color_type(&self) -> ColorType {
196 ColorType::Rgb8
197 }
198
199 fn into_reader(self) -> ImageResult<Self::Reader> {
200 Ok(HdrReader(
201 Cursor::new(inner:image::decoder_to_vec(self)?),
202 PhantomData,
203 ))
204 }
205
206 fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
207 self.read_image_data(buf)
208 }
209}
210
211impl<'a, R: 'a + BufRead + Seek> ImageDecoderRect<'a> for HdrAdapter<R> {
212 fn read_rect_with_progress<F: Fn(Progress)>(
213 &mut self,
214 x: u32,
215 y: u32,
216 width: u32,
217 height: u32,
218 buf: &mut [u8],
219 progress_callback: F,
220 ) -> ImageResult<()> {
221 image::load_rect(
222 x,
223 y,
224 width,
225 height,
226 buf,
227 progress_callback,
228 self,
229 |_, _| unreachable!(),
230 |s: &mut HdrAdapter, buf: &mut [u8]| s.read_image_data(buf),
231 )
232 }
233}
234
235/// Radiance HDR file signature
236pub const SIGNATURE: &[u8] = b"#?RADIANCE";
237const SIGNATURE_LENGTH: usize = 10;
238
239/// An Radiance HDR decoder
240#[derive(Debug)]
241pub struct HdrDecoder<R> {
242 r: R,
243 width: u32,
244 height: u32,
245 meta: HdrMetadata,
246}
247
248/// Refer to [wikipedia](https://en.wikipedia.org/wiki/RGBE_image_format)
249#[repr(C)]
250#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
251pub struct Rgbe8Pixel {
252 /// Color components
253 pub c: [u8; 3],
254 /// Exponent
255 pub e: u8,
256}
257
258/// Creates `Rgbe8Pixel` from components
259pub fn rgbe8(r: u8, g: u8, b: u8, e: u8) -> Rgbe8Pixel {
260 Rgbe8Pixel { c: [r, g, b], e }
261}
262
263impl Rgbe8Pixel {
264 /// Converts `Rgbe8Pixel` into `Rgb<f32>` linearly
265 #[inline]
266 pub fn to_hdr(self) -> Rgb<f32> {
267 if self.e == 0 {
268 Rgb([0.0, 0.0, 0.0])
269 } else {
270 // let exp = f32::ldexp(1., self.e as isize - (128 + 8)); // unstable
271 let exp = f32::exp2(<f32 as From<_>>::from(self.e) - (128.0 + 8.0));
272 Rgb([
273 exp * <f32 as From<_>>::from(self.c[0]),
274 exp * <f32 as From<_>>::from(self.c[1]),
275 exp * <f32 as From<_>>::from(self.c[2]),
276 ])
277 }
278 }
279
280 /// Converts `Rgbe8Pixel` into `Rgb<T>` with scale=1 and gamma=2.2
281 ///
282 /// color_ldr = (color_hdr*scale)<sup>gamma</sup>
283 ///
284 /// # Panic
285 ///
286 /// Panics when `T::max_value()` cannot be represented as f32.
287 #[inline]
288 pub fn to_ldr<T: Primitive + Zero>(self) -> Rgb<T> {
289 self.to_ldr_scale_gamma(1.0, 2.2)
290 }
291
292 /// Converts `Rgbe8Pixel` into `Rgb<T>` using provided scale and gamma
293 ///
294 /// color_ldr = (color_hdr*scale)<sup>gamma</sup>
295 ///
296 /// # Panic
297 ///
298 /// Panics when `T::max_value()` cannot be represented as f32.
299 /// Panics when scale or gamma is NaN
300 #[inline]
301 pub fn to_ldr_scale_gamma<T: Primitive + Zero>(self, scale: f32, gamma: f32) -> Rgb<T> {
302 let Rgb(data) = self.to_hdr();
303 let (r, g, b) = (data[0], data[1], data[2]);
304 #[inline]
305 fn sg<T: Primitive + Zero>(v: f32, scale: f32, gamma: f32) -> T {
306 let t_max = T::max_value();
307 // Disassembly shows that t_max_f32 is compiled into constant
308 let t_max_f32: f32 = num_traits::NumCast::from(t_max)
309 .expect("to_ldr_scale_gamma: maximum value of type is not representable as f32");
310 let fv = f32::powf(v * scale, gamma) * t_max_f32 + 0.5;
311 if fv < 0.0 {
312 T::zero()
313 } else if fv > t_max_f32 {
314 t_max
315 } else {
316 num_traits::NumCast::from(fv)
317 .expect("to_ldr_scale_gamma: cannot convert f32 to target type. NaN?")
318 }
319 }
320 Rgb([
321 sg(r, scale, gamma),
322 sg(g, scale, gamma),
323 sg(b, scale, gamma),
324 ])
325 }
326}
327
328impl<R: BufRead> HdrDecoder<R> {
329 /// Reads Radiance HDR image header from stream `r`
330 /// if the header is valid, creates HdrDecoder
331 /// strict mode is enabled
332 pub fn new(reader: R) -> ImageResult<HdrDecoder<R>> {
333 HdrDecoder::with_strictness(reader, true)
334 }
335
336 /// Reads Radiance HDR image header from stream `reader`,
337 /// if the header is valid, creates `HdrDecoder`.
338 ///
339 /// strict enables strict mode
340 ///
341 /// Warning! Reading wrong file in non-strict mode
342 /// could consume file size worth of memory in the process.
343 pub fn with_strictness(mut reader: R, strict: bool) -> ImageResult<HdrDecoder<R>> {
344 let mut attributes = HdrMetadata::new();
345
346 {
347 // scope to make borrowck happy
348 let r = &mut reader;
349 if strict {
350 let mut signature = [0; SIGNATURE_LENGTH];
351 r.read_exact(&mut signature)?;
352 if signature != SIGNATURE {
353 return Err(DecoderError::RadianceHdrSignatureInvalid.into());
354 } // no else
355 // skip signature line ending
356 read_line_u8(r)?;
357 } else {
358 // Old Radiance HDR files (*.pic) don't use signature
359 // Let them be parsed in non-strict mode
360 }
361 // read header data until empty line
362 loop {
363 match read_line_u8(r)? {
364 None => {
365 // EOF before end of header
366 return Err(DecoderError::TruncatedHeader.into());
367 }
368 Some(line) => {
369 if line.is_empty() {
370 // end of header
371 break;
372 } else if line[0] == b'#' {
373 // line[0] will not panic, line.len() == 0 is false here
374 // skip comments
375 continue;
376 } // no else
377 // process attribute line
378 let line = String::from_utf8_lossy(&line[..]);
379 attributes.update_header_info(&line, strict)?;
380 } // <= Some(line)
381 } // match read_line_u8()
382 } // loop
383 } // scope to end borrow of reader
384 // parse dimensions
385 let (width, height) = match read_line_u8(&mut reader)? {
386 None => {
387 // EOF instead of image dimensions
388 return Err(DecoderError::TruncatedDimensions.into());
389 }
390 Some(dimensions) => {
391 let dimensions = String::from_utf8_lossy(&dimensions[..]);
392 parse_dimensions_line(&dimensions, strict)?
393 }
394 };
395
396 // color type is always rgb8
397 if crate::utils::check_dimension_overflow(width, height, ColorType::Rgb8.bytes_per_pixel())
398 {
399 return Err(ImageError::Unsupported(
400 UnsupportedError::from_format_and_kind(
401 ImageFormat::Hdr.into(),
402 UnsupportedErrorKind::GenericFeature(format!(
403 "Image dimensions ({}x{}) are too large",
404 width, height
405 )),
406 ),
407 ));
408 }
409
410 Ok(HdrDecoder {
411 r: reader,
412
413 width,
414 height,
415 meta: HdrMetadata {
416 width,
417 height,
418 ..attributes
419 },
420 })
421 } // end with_strictness
422
423 /// Returns file metadata. Refer to `HdrMetadata` for details.
424 pub fn metadata(&self) -> HdrMetadata {
425 self.meta.clone()
426 }
427
428 /// Consumes decoder and returns a vector of RGBE8 pixels
429 pub fn read_image_native(mut self) -> ImageResult<Vec<Rgbe8Pixel>> {
430 // Don't read anything if image is empty
431 if self.width == 0 || self.height == 0 {
432 return Ok(vec![]);
433 }
434 // expression self.width > 0 && self.height > 0 is true from now to the end of this method
435 let pixel_count = self.width as usize * self.height as usize;
436 let mut ret = vec![Default::default(); pixel_count];
437 for chunk in ret.chunks_mut(self.width as usize) {
438 read_scanline(&mut self.r, chunk)?;
439 }
440 Ok(ret)
441 }
442
443 /// Consumes decoder and returns a vector of transformed pixels
444 pub fn read_image_transform<T: Send, F: Send + Sync + Fn(Rgbe8Pixel) -> T>(
445 mut self,
446 f: F,
447 output_slice: &mut [T],
448 ) -> ImageResult<()> {
449 assert_eq!(
450 output_slice.len(),
451 self.width as usize * self.height as usize
452 );
453
454 // Don't read anything if image is empty
455 if self.width == 0 || self.height == 0 {
456 return Ok(());
457 }
458
459 let chunks_iter = output_slice.chunks_mut(self.width as usize);
460
461 let mut buf = vec![Default::default(); self.width as usize];
462 for chunk in chunks_iter {
463 // read_scanline overwrites the entire buffer or returns an Err,
464 // so not resetting the buffer here is ok.
465 read_scanline(&mut self.r, &mut buf[..])?;
466 for (dst, &pix) in chunk.iter_mut().zip(buf.iter()) {
467 *dst = f(pix);
468 }
469 }
470 Ok(())
471 }
472
473 /// Consumes decoder and returns a vector of `Rgb<u8>` pixels.
474 /// scale = 1, gamma = 2.2
475 pub fn read_image_ldr(self) -> ImageResult<Vec<Rgb<u8>>> {
476 let mut ret = vec![Rgb([0, 0, 0]); self.width as usize * self.height as usize];
477 self.read_image_transform(|pix| pix.to_ldr(), &mut ret[..])?;
478 Ok(ret)
479 }
480
481 /// Consumes decoder and returns a vector of `Rgb<f32>` pixels.
482 ///
483 pub fn read_image_hdr(self) -> ImageResult<Vec<Rgb<f32>>> {
484 let mut ret = vec![Rgb([0.0, 0.0, 0.0]); self.width as usize * self.height as usize];
485 self.read_image_transform(|pix| pix.to_hdr(), &mut ret[..])?;
486 Ok(ret)
487 }
488}
489
490impl<R: Read> IntoIterator for HdrDecoder<R> {
491 type Item = ImageResult<Rgbe8Pixel>;
492 type IntoIter = HdrImageDecoderIterator<R>;
493
494 fn into_iter(self) -> Self::IntoIter {
495 HdrImageDecoderIterator {
496 r: self.r,
497 scanline_cnt: self.height as usize,
498 buf: vec![Default::default(); self.width as usize],
499 col: 0,
500 scanline: 0,
501 trouble: true, // make first call to `next()` read scanline
502 error_encountered: false,
503 }
504 }
505}
506
507/// Scanline buffered pixel by pixel iterator
508pub struct HdrImageDecoderIterator<R: Read> {
509 r: R,
510 scanline_cnt: usize,
511 buf: Vec<Rgbe8Pixel>, // scanline buffer
512 col: usize, // current position in scanline
513 scanline: usize, // current scanline
514 trouble: bool, // optimization, true indicates that we need to check something
515 error_encountered: bool,
516}
517
518impl<R: Read> HdrImageDecoderIterator<R> {
519 // Advances counter to the next pixel
520 #[inline]
521 fn advance(&mut self) {
522 self.col += 1;
523 if self.col == self.buf.len() {
524 self.col = 0;
525 self.scanline += 1;
526 self.trouble = true;
527 }
528 }
529}
530
531impl<R: Read> Iterator for HdrImageDecoderIterator<R> {
532 type Item = ImageResult<Rgbe8Pixel>;
533
534 fn next(&mut self) -> Option<Self::Item> {
535 if !self.trouble {
536 let ret = self.buf[self.col];
537 self.advance();
538 Some(Ok(ret))
539 } else {
540 // some condition is pending
541 if self.buf.is_empty() || self.scanline == self.scanline_cnt {
542 // No more pixels
543 return None;
544 } // no else
545 if self.error_encountered {
546 self.advance();
547 // Error was encountered. Keep producing errors.
548 // ImageError can't implement Clone, so just dump some error
549 return Some(Err(ImageError::Parameter(ParameterError::from_kind(
550 ParameterErrorKind::FailedAlready,
551 ))));
552 } // no else
553 if self.col == 0 {
554 // fill scanline buffer
555 match read_scanline(&mut self.r, &mut self.buf[..]) {
556 Ok(_) => {
557 // no action required
558 }
559 Err(err) => {
560 self.advance();
561 self.error_encountered = true;
562 self.trouble = true;
563 return Some(Err(err));
564 }
565 }
566 } // no else
567 self.trouble = false;
568 let ret = self.buf[0];
569 self.advance();
570 Some(Ok(ret))
571 }
572 }
573
574 fn size_hint(&self) -> (usize, Option<usize>) {
575 let total_cnt = self.buf.len() * self.scanline_cnt;
576 let cur_cnt = self.buf.len() * self.scanline + self.col;
577 let remaining = total_cnt - cur_cnt;
578 (remaining, Some(remaining))
579 }
580}
581
582impl<R: Read> ExactSizeIterator for HdrImageDecoderIterator<R> {}
583
584// Precondition: buf.len() > 0
585fn read_scanline<R: Read>(r: &mut R, buf: &mut [Rgbe8Pixel]) -> ImageResult<()> {
586 assert!(!buf.is_empty());
587 let width: usize = buf.len();
588 // first 4 bytes in scanline allow to determine compression method
589 let fb: Rgbe8Pixel = read_rgbe(r)?;
590 if fb.c[0] == 2 && fb.c[1] == 2 && fb.c[2] < 128 {
591 // denormalized pixel value (2,2,<128,_) indicates new per component RLE method
592 // decode_component guarantees that offset is within 0 .. width
593 // therefore we can skip bounds checking here, but we will not
594 decode_component(r, width, |offset: usize, value: u8| buf[offset].c[0] = value)?;
595 decode_component(r, width, |offset: usize, value: u8| buf[offset].c[1] = value)?;
596 decode_component(r, width, |offset: usize, value: u8| buf[offset].c[2] = value)?;
597 decode_component(r, width, |offset: usize, value: u8| buf[offset].e = value)?;
598 } else {
599 // old RLE method (it was considered old around 1991, should it be here?)
600 decode_old_rle(r, fb, buf)?;
601 }
602 Ok(())
603}
604
605#[inline(always)]
606fn read_byte<R: Read>(r: &mut R) -> io::Result<u8> {
607 let mut buf: [u8; 1] = [0u8];
608 r.read_exact(&mut buf[..])?;
609 Ok(buf[0])
610}
611
612// Guarantees that first parameter of set_component will be within pos .. pos+width
613#[inline]
614fn decode_component<R: Read, S: FnMut(usize, u8)>(
615 r: &mut R,
616 width: usize,
617 mut set_component: S,
618) -> ImageResult<()> {
619 let mut buf = [0; 128];
620 let mut pos = 0;
621 while pos < width {
622 // increment position by a number of decompressed values
623 pos += {
624 let rl = read_byte(r)?;
625 if rl <= 128 {
626 // sanity check
627 if pos + rl as usize > width {
628 return Err(DecoderError::WrongScanlineLength(pos + rl as usize, width).into());
629 }
630 // read values
631 r.read_exact(&mut buf[0..rl as usize])?;
632 for (offset, &value) in buf[0..rl as usize].iter().enumerate() {
633 set_component(pos + offset, value);
634 }
635 rl as usize
636 } else {
637 // run
638 let rl = rl - 128;
639 // sanity check
640 if pos + rl as usize > width {
641 return Err(DecoderError::WrongScanlineLength(pos + rl as usize, width).into());
642 }
643 // fill with same value
644 let value = read_byte(r)?;
645 for offset in 0..rl as usize {
646 set_component(pos + offset, value);
647 }
648 rl as usize
649 }
650 };
651 }
652 if pos != width {
653 return Err(DecoderError::WrongScanlineLength(pos, width).into());
654 }
655 Ok(())
656}
657
658// Decodes scanline, places it into buf
659// Precondition: buf.len() > 0
660// fb - first 4 bytes of scanline
661fn decode_old_rle<R: Read>(r: &mut R, fb: Rgbe8Pixel, buf: &mut [Rgbe8Pixel]) -> ImageResult<()> {
662 assert!(!buf.is_empty());
663 let width = buf.len();
664 // convenience function.
665 // returns run length if pixel is a run length marker
666 #[inline]
667 fn rl_marker(pix: Rgbe8Pixel) -> Option<usize> {
668 if pix.c == [1, 1, 1] {
669 Some(pix.e as usize)
670 } else {
671 None
672 }
673 }
674 // first pixel in scanline should not be run length marker
675 // it is error if it is
676 if rl_marker(fb).is_some() {
677 return Err(DecoderError::FirstPixelRlMarker.into());
678 }
679 buf[0] = fb; // set first pixel of scanline
680
681 let mut x_off = 1; // current offset from beginning of a scanline
682 let mut rl_mult = 1; // current run length multiplier
683 let mut prev_pixel = fb;
684 while x_off < width {
685 let pix = read_rgbe(r)?;
686 // it's harder to forget to increase x_off if I write this this way.
687 x_off += {
688 if let Some(rl) = rl_marker(pix) {
689 // rl_mult takes care of consecutive RL markers
690 let rl = rl * rl_mult;
691 rl_mult *= 256;
692 if x_off + rl <= width {
693 // do run
694 for b in &mut buf[x_off..x_off + rl] {
695 *b = prev_pixel;
696 }
697 } else {
698 return Err(DecoderError::WrongScanlineLength(x_off + rl, width).into());
699 };
700 rl // value to increase x_off by
701 } else {
702 rl_mult = 1; // chain of consecutive RL markers is broken
703 prev_pixel = pix;
704 buf[x_off] = pix;
705 1 // value to increase x_off by
706 }
707 };
708 }
709 if x_off != width {
710 return Err(DecoderError::WrongScanlineLength(x_off, width).into());
711 }
712 Ok(())
713}
714
715fn read_rgbe<R: Read>(r: &mut R) -> io::Result<Rgbe8Pixel> {
716 let mut buf: [u8; 4] = [0u8; 4];
717 r.read_exact(&mut buf[..])?;
718 Ok(Rgbe8Pixel {
719 c: [buf[0], buf[1], buf[2]],
720 e: buf[3],
721 })
722}
723
724/// Metadata for Radiance HDR image
725#[derive(Debug, Clone)]
726pub struct HdrMetadata {
727 /// Width of decoded image. It could be either scanline length,
728 /// or scanline count, depending on image orientation.
729 pub width: u32,
730 /// Height of decoded image. It depends on orientation too.
731 pub height: u32,
732 /// Orientation matrix. For standard orientation it is ((1,0),(0,1)) - left to right, top to bottom.
733 /// First pair tells how resulting pixel coordinates change along a scanline.
734 /// Second pair tells how they change from one scanline to the next.
735 pub orientation: ((i8, i8), (i8, i8)),
736 /// Divide color values by exposure to get to get physical radiance in
737 /// watts/steradian/m<sup>2</sup>
738 ///
739 /// Image may not contain physical data, even if this field is set.
740 pub exposure: Option<f32>,
741 /// Divide color values by corresponding tuple member (r, g, b) to get to get physical radiance
742 /// in watts/steradian/m<sup>2</sup>
743 ///
744 /// Image may not contain physical data, even if this field is set.
745 pub color_correction: Option<(f32, f32, f32)>,
746 /// Pixel height divided by pixel width
747 pub pixel_aspect_ratio: Option<f32>,
748 /// All lines contained in image header are put here. Ordering of lines is preserved.
749 /// Lines in the form "key=value" are represented as ("key", "value").
750 /// All other lines are ("", "line")
751 pub custom_attributes: Vec<(String, String)>,
752}
753
754impl HdrMetadata {
755 fn new() -> HdrMetadata {
756 HdrMetadata {
757 width: 0,
758 height: 0,
759 orientation: ((1, 0), (0, 1)),
760 exposure: None,
761 color_correction: None,
762 pixel_aspect_ratio: None,
763 custom_attributes: vec![],
764 }
765 }
766
767 // Updates header info, in strict mode returns error for malformed lines (no '=' separator)
768 // unknown attributes are skipped
769 fn update_header_info(&mut self, line: &str, strict: bool) -> ImageResult<()> {
770 // split line at first '='
771 // old Radiance HDR files (*.pic) feature tabs in key, so vvv trim
772 let maybe_key_value = split_at_first(line, "=").map(|(key, value)| (key.trim(), value));
773 // save all header lines in custom_attributes
774 match maybe_key_value {
775 Some((key, val)) => self
776 .custom_attributes
777 .push((key.to_owned(), val.to_owned())),
778 None => self.custom_attributes.push(("".into(), line.to_owned())),
779 }
780 // parse known attributes
781 match maybe_key_value {
782 Some(("FORMAT", val)) => {
783 if val.trim() != "32-bit_rle_rgbe" {
784 // XYZE isn't supported yet
785 return Err(ImageError::Unsupported(
786 UnsupportedError::from_format_and_kind(
787 ImageFormat::Hdr.into(),
788 UnsupportedErrorKind::Format(ImageFormatHint::Name(limit_string_len(
789 val, 20,
790 ))),
791 ),
792 ));
793 }
794 }
795 Some(("EXPOSURE", val)) => {
796 match val.trim().parse::<f32>() {
797 Ok(v) => {
798 self.exposure = Some(self.exposure.unwrap_or(1.0) * v); // all encountered exposure values should be multiplied
799 }
800 Err(parse_error) => {
801 if strict {
802 return Err(DecoderError::UnparsableF32(
803 LineType::Exposure,
804 parse_error,
805 )
806 .into());
807 } // no else, skip this line in non-strict mode
808 }
809 };
810 }
811 Some(("PIXASPECT", val)) => {
812 match val.trim().parse::<f32>() {
813 Ok(v) => {
814 self.pixel_aspect_ratio = Some(self.pixel_aspect_ratio.unwrap_or(1.0) * v);
815 // all encountered exposure values should be multiplied
816 }
817 Err(parse_error) => {
818 if strict {
819 return Err(DecoderError::UnparsableF32(
820 LineType::Pixaspect,
821 parse_error,
822 )
823 .into());
824 } // no else, skip this line in non-strict mode
825 }
826 };
827 }
828 Some(("COLORCORR", val)) => {
829 let mut rgbcorr = [1.0, 1.0, 1.0];
830 match parse_space_separated_f32(val, &mut rgbcorr, LineType::Colorcorr) {
831 Ok(extra_numbers) => {
832 if strict && extra_numbers {
833 return Err(DecoderError::ExtraneousColorcorrNumbers.into());
834 } // no else, just ignore extra numbers
835 let (rc, gc, bc) = self.color_correction.unwrap_or((1.0, 1.0, 1.0));
836 self.color_correction =
837 Some((rc * rgbcorr[0], gc * rgbcorr[1], bc * rgbcorr[2]));
838 }
839 Err(err) => {
840 if strict {
841 return Err(err);
842 } // no else, skip malformed line in non-strict mode
843 }
844 }
845 }
846 None => {
847 // old Radiance HDR files (*.pic) contain commands in a header
848 // just skip them
849 }
850 _ => {
851 // skip unknown attribute
852 }
853 } // match attributes
854 Ok(())
855 }
856}
857
858fn parse_space_separated_f32(line: &str, vals: &mut [f32], line_tp: LineType) -> ImageResult<bool> {
859 let mut nums: SplitWhitespace<'_> = line.split_whitespace();
860 for val: &mut f32 in vals.iter_mut() {
861 if let Some(num: &str) = nums.next() {
862 match num.parse::<f32>() {
863 Ok(v: f32) => *val = v,
864 Err(err: ParseFloatError) => return Err(DecoderError::UnparsableF32(line_tp, err).into()),
865 }
866 } else {
867 // not enough numbers in line
868 return Err(DecoderError::LineTooShort(line_tp).into());
869 }
870 }
871 Ok(nums.next().is_some())
872}
873
874// Parses dimension line "-Y height +X width"
875// returns (width, height) or error
876fn parse_dimensions_line(line: &str, strict: bool) -> ImageResult<(u32, u32)> {
877 const DIMENSIONS_COUNT: usize = 4;
878
879 let mut dim_parts = line.split_whitespace();
880 let c1_tag = dim_parts
881 .next()
882 .ok_or(DecoderError::DimensionsLineTooShort(0, DIMENSIONS_COUNT))?;
883 let c1_str = dim_parts
884 .next()
885 .ok_or(DecoderError::DimensionsLineTooShort(1, DIMENSIONS_COUNT))?;
886 let c2_tag = dim_parts
887 .next()
888 .ok_or(DecoderError::DimensionsLineTooShort(2, DIMENSIONS_COUNT))?;
889 let c2_str = dim_parts
890 .next()
891 .ok_or(DecoderError::DimensionsLineTooShort(3, DIMENSIONS_COUNT))?;
892 if strict && dim_parts.next().is_some() {
893 // extra data in dimensions line
894 return Err(DecoderError::DimensionsLineTooLong(DIMENSIONS_COUNT).into());
895 } // no else
896 // dimensions line is in the form "-Y 10 +X 20"
897 // There are 8 possible orientations: +Y +X, +X -Y and so on
898 match (c1_tag, c2_tag) {
899 ("-Y", "+X") => {
900 // Common orientation (left-right, top-down)
901 // c1_str is height, c2_str is width
902 let height = c1_str
903 .parse::<u32>()
904 .map_err(|pe| DecoderError::UnparsableU32(LineType::DimensionsHeight, pe))?;
905 let width = c2_str
906 .parse::<u32>()
907 .map_err(|pe| DecoderError::UnparsableU32(LineType::DimensionsWidth, pe))?;
908 Ok((width, height))
909 }
910 _ => Err(ImageError::Unsupported(
911 UnsupportedError::from_format_and_kind(
912 ImageFormat::Hdr.into(),
913 UnsupportedErrorKind::GenericFeature(format!(
914 "Orientation {} {}",
915 limit_string_len(c1_tag, 4),
916 limit_string_len(c2_tag, 4)
917 )),
918 ),
919 )),
920 } // final expression. Returns value
921}
922
923// Returns string with no more than len+3 characters
924fn limit_string_len(s: &str, len: usize) -> String {
925 let s_char_len: usize = s.chars().count();
926 if s_char_len > len {
927 s.chars().take(len).chain("...".chars()).collect()
928 } else {
929 s.into()
930 }
931}
932
933// Splits string into (before separator, after separator) tuple
934// or None if separator isn't found
935fn split_at_first<'a>(s: &'a str, separator: &str) -> Option<(&'a str, &'a str)> {
936 match s.find(separator) {
937 None | Some(0) => None,
938 Some(p: usize) if p >= s.len() - separator.len() => None,
939 Some(p: usize) => Some((&s[..p], &s[(p + separator.len())..])),
940 }
941}
942
943#[test]
944fn split_at_first_test() {
945 assert_eq!(split_at_first(&Cow::Owned("".into()), "="), None);
946 assert_eq!(split_at_first(&Cow::Owned("=".into()), "="), None);
947 assert_eq!(split_at_first(&Cow::Owned("= ".into()), "="), None);
948 assert_eq!(
949 split_at_first(&Cow::Owned(" = ".into()), "="),
950 Some((" ", " "))
951 );
952 assert_eq!(
953 split_at_first(&Cow::Owned("EXPOSURE= ".into()), "="),
954 Some(("EXPOSURE", " "))
955 );
956 assert_eq!(
957 split_at_first(&Cow::Owned("EXPOSURE= =".into()), "="),
958 Some(("EXPOSURE", " ="))
959 );
960 assert_eq!(
961 split_at_first(&Cow::Owned("EXPOSURE== =".into()), "=="),
962 Some(("EXPOSURE", " ="))
963 );
964 assert_eq!(split_at_first(&Cow::Owned("EXPOSURE".into()), ""), None);
965}
966
967// Reads input until b"\n" or EOF
968// Returns vector of read bytes NOT including end of line characters
969// or return None to indicate end of file
970fn read_line_u8<R: BufRead>(r: &mut R) -> ::std::io::Result<Option<Vec<u8>>> {
971 let mut ret: Vec = Vec::with_capacity(16);
972 match r.read_until(byte:b'\n', &mut ret) {
973 Ok(0) => Ok(None),
974 Ok(_) => {
975 if let Some(&b'\n') = ret[..].last() {
976 let _ = ret.pop();
977 }
978 Ok(Some(ret))
979 }
980 Err(err: Error) => Err(err),
981 }
982}
983
984#[test]
985fn read_line_u8_test() {
986 let buf: Vec<_> = (&b"One\nTwo\nThree\nFour\n\n\n"[..]).into();
987 let input: &mut Cursor> = &mut ::std::io::Cursor::new(inner:buf);
988 assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"One"[..]);
989 assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Two"[..]);
990 assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Three"[..]);
991 assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Four"[..]);
992 assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b""[..]);
993 assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b""[..]);
994 assert_eq!(read_line_u8(input).unwrap(), None);
995}
996
997/// Helper function for reading raw 3-channel f32 images
998pub fn read_raw_file<P: AsRef<Path>>(path: P) -> ::std::io::Result<Vec<Rgb<f32>>> {
999 use byteorder::{LittleEndian as LE, ReadBytesExt};
1000 use std::fs::File;
1001 use std::io::BufReader;
1002
1003 let mut r: BufReader = BufReader::new(inner:File::open(path)?);
1004 let w: usize = r.read_u32::<LE>()? as usize;
1005 let h: usize = r.read_u32::<LE>()? as usize;
1006 let c: usize = r.read_u32::<LE>()? as usize;
1007 assert_eq!(c, 3);
1008 let cnt: usize = w * h;
1009 let mut ret: Vec> = Vec::with_capacity(cnt);
1010 for _ in 0..cnt {
1011 let cr: f32 = r.read_f32::<LE>()?;
1012 let cg: f32 = r.read_f32::<LE>()?;
1013 let cb: f32 = r.read_f32::<LE>()?;
1014 ret.push(Rgb([cr, cg, cb]));
1015 }
1016 Ok(ret)
1017}
1018
1019#[cfg(test)]
1020mod test {
1021 use super::*;
1022
1023 #[test]
1024 fn dimension_overflow() {
1025 let data = b"#?RADIANCE\nFORMAT=32-bit_rle_rgbe\n\n -Y 4294967295 +X 4294967295";
1026
1027 assert!(HdrAdapter::new(Cursor::new(data)).is_err());
1028 assert!(HdrAdapter::new_nonstrict(Cursor::new(data)).is_err());
1029 }
1030}
1031