1 | use crate::Primitive; |
2 | use num_traits::identities::Zero; |
3 | #[cfg (test)] |
4 | use std::borrow::Cow; |
5 | use std::io::{self, BufRead, Cursor, Read, Seek}; |
6 | use std::marker::PhantomData; |
7 | use std::num::{ParseFloatError, ParseIntError}; |
8 | use std::path::Path; |
9 | use std::{error, fmt, mem}; |
10 | |
11 | use crate::color::{ColorType, Rgb}; |
12 | use crate::error::{ |
13 | DecodingError, ImageError, ImageFormatHint, ImageResult, ParameterError, ParameterErrorKind, |
14 | UnsupportedError, UnsupportedErrorKind, |
15 | }; |
16 | use 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)] |
20 | enum 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 | |
49 | impl 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 | |
86 | impl From<DecoderError> for ImageError { |
87 | fn from(e: DecoderError) -> ImageError { |
88 | ImageError::Decoding(DecodingError::new(format:ImageFormat::Hdr.into(), err:e)) |
89 | } |
90 | } |
91 | |
92 | impl 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)] |
104 | enum LineType { |
105 | Exposure, |
106 | Pixaspect, |
107 | Colorcorr, |
108 | DimensionsHeight, |
109 | DimensionsWidth, |
110 | } |
111 | |
112 | impl 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)] |
126 | pub struct HdrAdapter<R: Read> { |
127 | inner: Option<HdrDecoder<R>>, |
128 | // data: Option<Vec<u8>>, |
129 | meta: HdrMetadata, |
130 | } |
131 | |
132 | impl<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>>` |
173 | pub struct HdrReader<R>(Cursor<Vec<u8>>, PhantomData<R>); |
174 | impl<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 | |
188 | impl<'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 | |
211 | impl<'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 |
236 | pub const SIGNATURE: &[u8] = b"#?RADIANCE" ; |
237 | const SIGNATURE_LENGTH: usize = 10; |
238 | |
239 | /// An Radiance HDR decoder |
240 | #[derive (Debug)] |
241 | pub 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)] |
251 | pub struct Rgbe8Pixel { |
252 | /// Color components |
253 | pub c: [u8; 3], |
254 | /// Exponent |
255 | pub e: u8, |
256 | } |
257 | |
258 | /// Creates `Rgbe8Pixel` from components |
259 | pub fn rgbe8(r: u8, g: u8, b: u8, e: u8) -> Rgbe8Pixel { |
260 | Rgbe8Pixel { c: [r, g, b], e } |
261 | } |
262 | |
263 | impl 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 | |
328 | impl<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 | |
490 | impl<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 |
508 | pub 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 | |
518 | impl<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 | |
531 | impl<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 | |
582 | impl<R: Read> ExactSizeIterator for HdrImageDecoderIterator<R> {} |
583 | |
584 | // Precondition: buf.len() > 0 |
585 | fn 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)] |
606 | fn 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 ] |
614 | fn 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 |
661 | fn 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 | |
715 | fn 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)] |
726 | pub 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 | |
754 | impl 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 | |
858 | fn 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 |
876 | fn 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 |
924 | fn 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 |
935 | fn 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 ] |
944 | fn 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 |
970 | fn 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 ] |
985 | fn 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 |
998 | pub 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)] |
1020 | mod 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 | |