1use std::io::{self, Read};
2
3use std::num::{ParseFloatError, ParseIntError};
4use std::{error, fmt};
5
6use crate::color::{ColorType, Rgb};
7use crate::error::{
8 DecodingError, ImageError, ImageFormatHint, ImageResult, UnsupportedError, UnsupportedErrorKind,
9};
10use crate::image::{ImageDecoder, ImageFormat};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14enum DecoderError {
15 RadianceHdrSignatureInvalid,
17 TruncatedHeader,
19 TruncatedDimensions,
21
22 UnparsableF32(LineType, ParseFloatError),
24 UnparsableU32(LineType, ParseIntError),
26 LineTooShort(LineType),
28
29 ExtraneousColorcorrNumbers,
31
32 DimensionsLineTooShort(usize, usize),
34 DimensionsLineTooLong(usize),
36
37 WrongScanlineLength(usize, usize),
39 FirstPixelRlMarker,
41}
42
43impl fmt::Display for DecoderError {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 match self {
46 DecoderError::RadianceHdrSignatureInvalid => {
47 f.write_str("Radiance HDR signature not found")
48 }
49 DecoderError::TruncatedHeader => f.write_str("EOF in header"),
50 DecoderError::TruncatedDimensions => f.write_str("EOF in dimensions line"),
51 DecoderError::UnparsableF32(line, pe) => {
52 f.write_fmt(format_args!("Cannot parse {line} value as f32: {pe}"))
53 }
54 DecoderError::UnparsableU32(line, pe) => {
55 f.write_fmt(format_args!("Cannot parse {line} value as u32: {pe}"))
56 }
57 DecoderError::LineTooShort(line) => {
58 f.write_fmt(format_args!("Not enough numbers in {line}"))
59 }
60 DecoderError::ExtraneousColorcorrNumbers => f.write_str("Extra numbers in COLORCORR"),
61 DecoderError::DimensionsLineTooShort(elements, expected) => f.write_fmt(format_args!(
62 "Dimensions line too short: have {elements} elements, expected {expected}"
63 )),
64 DecoderError::DimensionsLineTooLong(expected) => f.write_fmt(format_args!(
65 "Dimensions line too long, expected {expected} elements"
66 )),
67 DecoderError::WrongScanlineLength(len, expected) => f.write_fmt(format_args!(
68 "Wrong length of decoded scanline: got {len}, expected {expected}"
69 )),
70 DecoderError::FirstPixelRlMarker => {
71 f.write_str("First pixel of a scanline shouldn't be run length marker")
72 }
73 }
74 }
75}
76
77impl From<DecoderError> for ImageError {
78 fn from(e: DecoderError) -> ImageError {
79 ImageError::Decoding(DecodingError::new(ImageFormat::Hdr.into(), e))
80 }
81}
82
83impl error::Error for DecoderError {
84 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
85 match self {
86 DecoderError::UnparsableF32(_, err) => Some(err),
87 DecoderError::UnparsableU32(_, err) => Some(err),
88 _ => None,
89 }
90 }
91}
92
93#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
95enum LineType {
96 Exposure,
97 Pixaspect,
98 Colorcorr,
99 DimensionsHeight,
100 DimensionsWidth,
101}
102
103impl fmt::Display for LineType {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 f.write_str(match self {
106 LineType::Exposure => "EXPOSURE",
107 LineType::Pixaspect => "PIXASPECT",
108 LineType::Colorcorr => "COLORCORR",
109 LineType::DimensionsHeight => "height dimension",
110 LineType::DimensionsWidth => "width dimension",
111 })
112 }
113}
114
115pub const SIGNATURE: &[u8] = b"#?RADIANCE";
117const SIGNATURE_LENGTH: usize = 10;
118
119#[derive(Debug)]
121pub struct HdrDecoder<R> {
122 r: R,
123 width: u32,
124 height: u32,
125 meta: HdrMetadata,
126}
127
128#[repr(C)]
130#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
131pub(crate) struct Rgbe8Pixel {
132 pub(crate) c: [u8; 3],
134 pub(crate) e: u8,
136}
137
138pub(crate) fn rgbe8(r: u8, g: u8, b: u8, e: u8) -> Rgbe8Pixel {
140 Rgbe8Pixel { c: [r, g, b], e }
141}
142
143impl Rgbe8Pixel {
144 #[inline]
146 pub(crate) fn to_hdr(self) -> Rgb<f32> {
147 if self.e == 0 {
148 Rgb([0.0, 0.0, 0.0])
149 } else {
150 let exp = f32::exp2(<f32 as From<_>>::from(self.e) - (128.0 + 8.0));
152 Rgb([
153 exp * <f32 as From<_>>::from(self.c[0]),
154 exp * <f32 as From<_>>::from(self.c[1]),
155 exp * <f32 as From<_>>::from(self.c[2]),
156 ])
157 }
158 }
159}
160
161impl<R: Read> HdrDecoder<R> {
162 pub fn new(reader: R) -> ImageResult<Self> {
166 HdrDecoder::with_strictness(reader, true)
167 }
168
169 pub fn new_nonstrict(reader: R) -> ImageResult<Self> {
171 Self::with_strictness(reader, false)
172 }
173
174 pub fn with_strictness(mut reader: R, strict: bool) -> ImageResult<HdrDecoder<R>> {
182 let mut attributes = HdrMetadata::new();
183
184 {
185 let r = &mut reader;
187 if strict {
188 let mut signature = [0; SIGNATURE_LENGTH];
189 r.read_exact(&mut signature)?;
190 if signature != SIGNATURE {
191 return Err(DecoderError::RadianceHdrSignatureInvalid.into());
192 } read_line_u8(r)?;
195 } else {
196 }
199 loop {
201 match read_line_u8(r)? {
202 None => {
203 return Err(DecoderError::TruncatedHeader.into());
205 }
206 Some(line) => {
207 if line.is_empty() {
208 break;
210 } else if line[0] == b'#' {
211 continue;
214 } let line = String::from_utf8_lossy(&line[..]);
217 attributes.update_header_info(&line, strict)?;
218 } } } } let (width, height) = match read_line_u8(&mut reader)? {
224 None => {
225 return Err(DecoderError::TruncatedDimensions.into());
227 }
228 Some(dimensions) => {
229 let dimensions = String::from_utf8_lossy(&dimensions[..]);
230 parse_dimensions_line(&dimensions, strict)?
231 }
232 };
233
234 if crate::utils::check_dimension_overflow(width, height, ColorType::Rgb8.bytes_per_pixel())
236 {
237 return Err(ImageError::Unsupported(
238 UnsupportedError::from_format_and_kind(
239 ImageFormat::Hdr.into(),
240 UnsupportedErrorKind::GenericFeature(format!(
241 "Image dimensions ({width}x{height}) are too large"
242 )),
243 ),
244 ));
245 }
246
247 Ok(HdrDecoder {
248 r: reader,
249
250 width,
251 height,
252 meta: HdrMetadata {
253 width,
254 height,
255 ..attributes
256 },
257 })
258 } pub fn metadata(&self) -> HdrMetadata {
262 self.meta.clone()
263 }
264
265 fn read_image_transform<T: Send, F: Send + Sync + Fn(Rgbe8Pixel) -> T>(
267 mut self,
268 f: F,
269 output_slice: &mut [T],
270 ) -> ImageResult<()> {
271 assert_eq!(
272 output_slice.len(),
273 self.width as usize * self.height as usize
274 );
275
276 if self.width == 0 || self.height == 0 {
278 return Ok(());
279 }
280
281 let chunks_iter = output_slice.chunks_mut(self.width as usize);
282
283 let mut buf = vec![Default::default(); self.width as usize];
284 for chunk in chunks_iter {
285 read_scanline(&mut self.r, &mut buf[..])?;
288 for (dst, &pix) in chunk.iter_mut().zip(buf.iter()) {
289 *dst = f(pix);
290 }
291 }
292 Ok(())
293 }
294}
295
296impl<R: Read> ImageDecoder for HdrDecoder<R> {
297 fn dimensions(&self) -> (u32, u32) {
298 (self.meta.width, self.meta.height)
299 }
300
301 fn color_type(&self) -> ColorType {
302 ColorType::Rgb32F
303 }
304
305 fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
306 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
307
308 let mut img = vec![Rgb([0.0, 0.0, 0.0]); self.width as usize * self.height as usize];
309 self.read_image_transform(|pix| pix.to_hdr(), &mut img[..])?;
310
311 for (i, Rgb(data)) in img.into_iter().enumerate() {
312 buf[(i * 12)..][..12].copy_from_slice(bytemuck::cast_slice(&data));
313 }
314
315 Ok(())
316 }
317
318 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
319 (*self).read_image(buf)
320 }
321}
322
323fn read_scanline<R: Read>(r: &mut R, buf: &mut [Rgbe8Pixel]) -> ImageResult<()> {
325 assert!(!buf.is_empty());
326 let width = buf.len();
327 let fb = read_rgbe(r)?;
329 if fb.c[0] == 2 && fb.c[1] == 2 && fb.c[2] < 128 {
330 decode_component(r, width, |offset, value| buf[offset].c[0] = value)?;
334 decode_component(r, width, |offset, value| buf[offset].c[1] = value)?;
335 decode_component(r, width, |offset, value| buf[offset].c[2] = value)?;
336 decode_component(r, width, |offset, value| buf[offset].e = value)?;
337 } else {
338 decode_old_rle(r, fb, buf)?;
340 }
341 Ok(())
342}
343
344#[inline(always)]
345fn read_byte<R: Read>(r: &mut R) -> io::Result<u8> {
346 let mut buf = [0u8];
347 r.read_exact(&mut buf[..])?;
348 Ok(buf[0])
349}
350
351#[inline]
353fn decode_component<R: Read, S: FnMut(usize, u8)>(
354 r: &mut R,
355 width: usize,
356 mut set_component: S,
357) -> ImageResult<()> {
358 let mut buf = [0; 128];
359 let mut pos = 0;
360 while pos < width {
361 pos += {
363 let rl = read_byte(r)?;
364 if rl <= 128 {
365 if pos + rl as usize > width {
367 return Err(DecoderError::WrongScanlineLength(pos + rl as usize, width).into());
368 }
369 r.read_exact(&mut buf[0..rl as usize])?;
371 for (offset, &value) in buf[0..rl as usize].iter().enumerate() {
372 set_component(pos + offset, value);
373 }
374 rl as usize
375 } else {
376 let rl = rl - 128;
378 if pos + rl as usize > width {
380 return Err(DecoderError::WrongScanlineLength(pos + rl as usize, width).into());
381 }
382 let value = read_byte(r)?;
384 for offset in 0..rl as usize {
385 set_component(pos + offset, value);
386 }
387 rl as usize
388 }
389 };
390 }
391 if pos != width {
392 return Err(DecoderError::WrongScanlineLength(pos, width).into());
393 }
394 Ok(())
395}
396
397fn decode_old_rle<R: Read>(r: &mut R, fb: Rgbe8Pixel, buf: &mut [Rgbe8Pixel]) -> ImageResult<()> {
401 assert!(!buf.is_empty());
402 let width = buf.len();
403 #[inline]
406 fn rl_marker(pix: Rgbe8Pixel) -> Option<usize> {
407 if pix.c == [1, 1, 1] {
408 Some(pix.e as usize)
409 } else {
410 None
411 }
412 }
413 if rl_marker(fb).is_some() {
416 return Err(DecoderError::FirstPixelRlMarker.into());
417 }
418 buf[0] = fb; let mut x_off = 1; let mut rl_mult = 1; let mut prev_pixel = fb;
423 while x_off < width {
424 let pix = read_rgbe(r)?;
425 x_off += {
427 if let Some(rl) = rl_marker(pix) {
428 let rl = rl * rl_mult;
430 rl_mult *= 256;
431 if x_off + rl <= width {
432 for b in &mut buf[x_off..x_off + rl] {
434 *b = prev_pixel;
435 }
436 } else {
437 return Err(DecoderError::WrongScanlineLength(x_off + rl, width).into());
438 };
439 rl } else {
441 rl_mult = 1; prev_pixel = pix;
443 buf[x_off] = pix;
444 1 }
446 };
447 }
448 if x_off != width {
449 return Err(DecoderError::WrongScanlineLength(x_off, width).into());
450 }
451 Ok(())
452}
453
454fn read_rgbe<R: Read>(r: &mut R) -> io::Result<Rgbe8Pixel> {
455 let mut buf = [0u8; 4];
456 r.read_exact(&mut buf[..])?;
457 Ok(Rgbe8Pixel {
458 c: [buf[0], buf[1], buf[2]],
459 e: buf[3],
460 })
461}
462
463#[derive(Debug, Clone)]
465pub struct HdrMetadata {
466 pub width: u32,
469 pub height: u32,
471 pub orientation: ((i8, i8), (i8, i8)),
475 pub exposure: Option<f32>,
480 pub color_correction: Option<(f32, f32, f32)>,
485 pub pixel_aspect_ratio: Option<f32>,
487 pub custom_attributes: Vec<(String, String)>,
491}
492
493impl HdrMetadata {
494 fn new() -> HdrMetadata {
495 HdrMetadata {
496 width: 0,
497 height: 0,
498 orientation: ((1, 0), (0, 1)),
499 exposure: None,
500 color_correction: None,
501 pixel_aspect_ratio: None,
502 custom_attributes: vec![],
503 }
504 }
505
506 fn update_header_info(&mut self, line: &str, strict: bool) -> ImageResult<()> {
509 let maybe_key_value = split_at_first(line, "=").map(|(key, value)| (key.trim(), value));
512 match maybe_key_value {
514 Some((key, val)) => self
515 .custom_attributes
516 .push((key.to_owned(), val.to_owned())),
517 None => self
518 .custom_attributes
519 .push((String::new(), line.to_owned())),
520 }
521 match maybe_key_value {
523 Some(("FORMAT", val)) => {
524 if val.trim() != "32-bit_rle_rgbe" {
525 return Err(ImageError::Unsupported(
527 UnsupportedError::from_format_and_kind(
528 ImageFormat::Hdr.into(),
529 UnsupportedErrorKind::Format(ImageFormatHint::Name(limit_string_len(
530 val, 20,
531 ))),
532 ),
533 ));
534 }
535 }
536 Some(("EXPOSURE", val)) => {
537 match val.trim().parse::<f32>() {
538 Ok(v) => {
539 self.exposure = Some(self.exposure.unwrap_or(1.0) * v); }
541 Err(parse_error) => {
542 if strict {
543 return Err(DecoderError::UnparsableF32(
544 LineType::Exposure,
545 parse_error,
546 )
547 .into());
548 } }
550 };
551 }
552 Some(("PIXASPECT", val)) => {
553 match val.trim().parse::<f32>() {
554 Ok(v) => {
555 self.pixel_aspect_ratio = Some(self.pixel_aspect_ratio.unwrap_or(1.0) * v);
556 }
558 Err(parse_error) => {
559 if strict {
560 return Err(DecoderError::UnparsableF32(
561 LineType::Pixaspect,
562 parse_error,
563 )
564 .into());
565 } }
567 };
568 }
569 Some(("COLORCORR", val)) => {
570 let mut rgbcorr = [1.0, 1.0, 1.0];
571 match parse_space_separated_f32(val, &mut rgbcorr, LineType::Colorcorr) {
572 Ok(extra_numbers) => {
573 if strict && extra_numbers {
574 return Err(DecoderError::ExtraneousColorcorrNumbers.into());
575 } let (rc, gc, bc) = self.color_correction.unwrap_or((1.0, 1.0, 1.0));
577 self.color_correction =
578 Some((rc * rgbcorr[0], gc * rgbcorr[1], bc * rgbcorr[2]));
579 }
580 Err(err) => {
581 if strict {
582 return Err(err);
583 } }
585 }
586 }
587 None => {
588 }
591 _ => {
592 }
594 } Ok(())
596 }
597}
598
599fn parse_space_separated_f32(line: &str, vals: &mut [f32], line_tp: LineType) -> ImageResult<bool> {
600 let mut nums = line.split_whitespace();
601 for val in vals.iter_mut() {
602 if let Some(num) = nums.next() {
603 match num.parse::<f32>() {
604 Ok(v) => *val = v,
605 Err(err) => return Err(DecoderError::UnparsableF32(line_tp, err).into()),
606 }
607 } else {
608 return Err(DecoderError::LineTooShort(line_tp).into());
610 }
611 }
612 Ok(nums.next().is_some())
613}
614
615fn parse_dimensions_line(line: &str, strict: bool) -> ImageResult<(u32, u32)> {
618 const DIMENSIONS_COUNT: usize = 4;
619
620 let mut dim_parts = line.split_whitespace();
621 let c1_tag = dim_parts
622 .next()
623 .ok_or(DecoderError::DimensionsLineTooShort(0, DIMENSIONS_COUNT))?;
624 let c1_str = dim_parts
625 .next()
626 .ok_or(DecoderError::DimensionsLineTooShort(1, DIMENSIONS_COUNT))?;
627 let c2_tag = dim_parts
628 .next()
629 .ok_or(DecoderError::DimensionsLineTooShort(2, DIMENSIONS_COUNT))?;
630 let c2_str = dim_parts
631 .next()
632 .ok_or(DecoderError::DimensionsLineTooShort(3, DIMENSIONS_COUNT))?;
633 if strict && dim_parts.next().is_some() {
634 return Err(DecoderError::DimensionsLineTooLong(DIMENSIONS_COUNT).into());
636 } match (c1_tag, c2_tag) {
640 ("-Y", "+X") => {
641 let height = c1_str
644 .parse::<u32>()
645 .map_err(|pe| DecoderError::UnparsableU32(LineType::DimensionsHeight, pe))?;
646 let width = c2_str
647 .parse::<u32>()
648 .map_err(|pe| DecoderError::UnparsableU32(LineType::DimensionsWidth, pe))?;
649 Ok((width, height))
650 }
651 _ => Err(ImageError::Unsupported(
652 UnsupportedError::from_format_and_kind(
653 ImageFormat::Hdr.into(),
654 UnsupportedErrorKind::GenericFeature(format!(
655 "Orientation {} {}",
656 limit_string_len(c1_tag, 4),
657 limit_string_len(c2_tag, 4)
658 )),
659 ),
660 )),
661 } }
663
664fn limit_string_len(s: &str, len: usize) -> String {
666 let s_char_len = s.chars().count();
667 if s_char_len > len {
668 s.chars().take(len).chain("...".chars()).collect()
669 } else {
670 s.into()
671 }
672}
673
674fn split_at_first<'a>(s: &'a str, separator: &str) -> Option<(&'a str, &'a str)> {
677 match s.find(separator) {
678 None | Some(0) => None,
679 Some(p) if p >= s.len() - separator.len() => None,
680 Some(p) => Some((&s[..p], &s[(p + separator.len())..])),
681 }
682}
683
684fn read_line_u8<R: Read>(r: &mut R) -> io::Result<Option<Vec<u8>>> {
688 let mut ret = Vec::with_capacity(16);
689 loop {
690 let mut byte = [0];
691 if r.read(&mut byte)? == 0 || byte[0] == b'\n' {
692 if ret.is_empty() && byte[0] != b'\n' {
693 return Ok(None);
694 } else {
695 return Ok(Some(ret));
696 }
697 }
698 ret.push(byte[0]);
699 }
700}
701
702#[cfg(test)]
703mod tests {
704 use std::{borrow::Cow, io::Cursor};
705
706 use super::*;
707
708 #[test]
709 fn split_at_first_test() {
710 assert_eq!(split_at_first(&Cow::Owned("".into()), "="), None);
711 assert_eq!(split_at_first(&Cow::Owned("=".into()), "="), None);
712 assert_eq!(split_at_first(&Cow::Owned("= ".into()), "="), None);
713 assert_eq!(
714 split_at_first(&Cow::Owned(" = ".into()), "="),
715 Some((" ", " "))
716 );
717 assert_eq!(
718 split_at_first(&Cow::Owned("EXPOSURE= ".into()), "="),
719 Some(("EXPOSURE", " "))
720 );
721 assert_eq!(
722 split_at_first(&Cow::Owned("EXPOSURE= =".into()), "="),
723 Some(("EXPOSURE", " ="))
724 );
725 assert_eq!(
726 split_at_first(&Cow::Owned("EXPOSURE== =".into()), "=="),
727 Some(("EXPOSURE", " ="))
728 );
729 assert_eq!(split_at_first(&Cow::Owned("EXPOSURE".into()), ""), None);
730 }
731
732 #[test]
733 fn read_line_u8_test() {
734 let buf: Vec<_> = (&b"One\nTwo\nThree\nFour\n\n\n"[..]).into();
735 let input = &mut Cursor::new(buf);
736 assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"One"[..]);
737 assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Two"[..]);
738 assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Three"[..]);
739 assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Four"[..]);
740 assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b""[..]);
741 assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b""[..]);
742 assert_eq!(read_line_u8(input).unwrap(), None);
743 }
744
745 #[test]
746 fn dimension_overflow() {
747 let data = b"#?RADIANCE\nFORMAT=32-bit_rle_rgbe\n\n -Y 4294967295 +X 4294967295";
748
749 assert!(HdrDecoder::new(Cursor::new(data)).is_err());
750 assert!(HdrDecoder::new_nonstrict(Cursor::new(data)).is_err());
751 }
752}