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