image/codecs/hdr/
decoder.rs

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(ImageFormat::Hdr.into(), 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) => Some(err),
96            DecoderError::UnparsableU32(_, err) => 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(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(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(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, buf| 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 = buf.len();
588    // first 4 bytes in scanline allow to determine compression method
589    let fb = 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, value| buf[offset].c[0] = value)?;
595        decode_component(r, width, |offset, value| buf[offset].c[1] = value)?;
596        decode_component(r, width, |offset, value| buf[offset].c[2] = value)?;
597        decode_component(r, width, |offset, value| 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 = [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 = [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 = line.split_whitespace();
860    for val in vals.iter_mut() {
861        if let Some(num) = nums.next() {
862            match num.parse::<f32>() {
863                Ok(v) => *val = v,
864                Err(err) => 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 = 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) if p >= s.len() - separator.len() => None,
939        Some(p) => 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::with_capacity(16);
972    match r.read_until(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) => 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 ::std::io::Cursor::new(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::new(File::open(path)?);
1004    let w = r.read_u32::<LE>()? as usize;
1005    let h = r.read_u32::<LE>()? as usize;
1006    let c = r.read_u32::<LE>()? as usize;
1007    assert_eq!(c, 3);
1008    let cnt = w * h;
1009    let mut ret = Vec::with_capacity(cnt);
1010    for _ in 0..cnt {
1011        let cr = r.read_f32::<LE>()?;
1012        let cg = r.read_f32::<LE>()?;
1013        let cb = 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}