use scoped_threadpool::Pool;
use num_traits::cast::NumCast;
use num_traits::identities::Zero;
use std::{mem, ptr};
#[cfg(test)]
use std::borrow::Cow;
use std::error::Error;
use std::io::{self, BufRead, Cursor, Seek};
use std::iter::Iterator;
use std::path::Path;
use Primitive;
use color::{ColorType, Rgb};
use image::{self, ImageDecoder, ImageDecoderExt, ImageError, ImageResult, Progress};
#[derive(Debug)]
pub struct HDRAdapter<R: BufRead> {
inner: Option<HDRDecoder<R>>,
data: Option<Vec<u8>>,
meta: HDRMetadata,
}
impl<R: BufRead> HDRAdapter<R> {
pub fn new(r: R) -> ImageResult<HDRAdapter<R>> {
let decoder = try!(HDRDecoder::new(r));
let meta = decoder.metadata();
Ok(HDRAdapter {
inner: Some(decoder),
data: None,
meta,
})
}
pub fn new_nonstrict(r: R) -> ImageResult<HDRAdapter<R>> {
let decoder = try!(HDRDecoder::with_strictness(r, false));
let meta = decoder.metadata();
Ok(HDRAdapter {
inner: Some(decoder),
data: None,
meta,
})
}
fn read_image_data(&mut self) -> ImageResult<()> {
match self.inner.take() {
Some(decoder) => {
let img: Vec<Rgb<u8>> = decoder.read_image_ldr()?;
let len = img.len() * mem::size_of::<Rgb<u8>>();
let target = self.data.get_or_insert_with(|| Vec::with_capacity(len));
target.clear();
for Rgb { data } in img {
target.extend_from_slice(&data);
}
Ok(())
}
None => Err(ImageError::ImageEnd),
}
}
}
impl<R: BufRead> ImageDecoder for HDRAdapter<R> {
type Reader = Cursor<Vec<u8>>;
fn dimensions(&self) -> (u64, u64) {
(self.meta.width as u64, self.meta.height as u64)
}
fn colortype(&self) -> ColorType {
ColorType::RGB(8)
}
fn into_reader(self) -> ImageResult<Self::Reader> {
Ok(Cursor::new(self.read_image()?))
}
fn read_image(mut self) -> ImageResult<Vec<u8>> {
if let Some(data) = self.data {
return Ok(data);
}
self.read_image_data()?;
Ok(self.data.unwrap())
}
}
impl<R: BufRead + Seek> ImageDecoderExt for HDRAdapter<R> {
fn read_rect_with_progress<F: Fn(Progress)>(
&mut self,
x: u64,
y: u64,
width: u64,
height: u64,
buf: &mut [u8],
progress_callback: F,
) -> ImageResult<()> {
if self.data.is_none() {
self.read_image_data()?;
}
image::load_rect(x, y, width, height, buf, progress_callback, self, |_, _| unreachable!(),
|s, buf| {
buf.copy_from_slice(&*s.data.as_ref().unwrap());
Ok(buf.len())
})
}
}
pub const SIGNATURE: &[u8] = b"#?RADIANCE";
const SIGNATURE_LENGTH: usize = 10;
#[derive(Debug)]
pub struct HDRDecoder<R> {
r: R,
width: u32,
height: u32,
meta: HDRMetadata,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct RGBE8Pixel {
pub c: [u8; 3],
pub e: u8,
}
pub fn rgbe8(r: u8, g: u8, b: u8, e: u8) -> RGBE8Pixel {
RGBE8Pixel { c: [r, g, b], e }
}
impl RGBE8Pixel {
#[inline]
pub fn to_hdr(self) -> Rgb<f32> {
if self.e == 0 {
Rgb([0.0, 0.0, 0.0])
} else {
let exp = f32::exp2(<f32 as From<_>>::from(self.e) - (128.0 + 8.0));
Rgb([
exp * <f32 as From<_>>::from(self.c[0]),
exp * <f32 as From<_>>::from(self.c[1]),
exp * <f32 as From<_>>::from(self.c[2]),
])
}
}
#[inline]
pub fn to_ldr<T: Primitive + Zero>(self) -> Rgb<T> {
self.to_ldr_scale_gamma(1.0, 2.2)
}
#[inline]
pub fn to_ldr_scale_gamma<T: Primitive + Zero>(self, scale: f32, gamma: f32) -> Rgb<T> {
let Rgb { data } = self.to_hdr();
let (r, g, b) = (data[0], data[1], data[2]);
#[inline]
fn sg<T: Primitive + Zero>(v: f32, scale: f32, gamma: f32) -> T {
let t_max = T::max_value();
let t_max_f32: f32 = NumCast::from(t_max)
.expect("to_ldr_scale_gamma: maximum value of type is not representable as f32");
let fv = f32::powf(v * scale, gamma) * t_max_f32 + 0.5;
if fv < 0.0 {
T::zero()
} else if fv > t_max_f32 {
t_max
} else {
NumCast::from(fv)
.expect("to_ldr_scale_gamma: cannot convert f32 to target type. NaN?")
}
}
Rgb([
sg(r, scale, gamma),
sg(g, scale, gamma),
sg(b, scale, gamma),
])
}
}
impl<R: BufRead> HDRDecoder<R> {
pub fn new(reader: R) -> ImageResult<HDRDecoder<R>> {
HDRDecoder::with_strictness(reader, true)
}
pub fn with_strictness(mut reader: R, strict: bool) -> ImageResult<HDRDecoder<R>> {
let mut attributes = HDRMetadata::new();
{
let r = &mut reader;
if strict {
let mut signature = [0; SIGNATURE_LENGTH];
try!(r.read_exact(&mut signature));
if signature != SIGNATURE {
return Err(ImageError::FormatError(
"Radiance HDR signature not found".to_string(),
));
}
try!(read_line_u8(r));
} else {
}
loop {
match try!(read_line_u8(r)) {
None => {
return Err(ImageError::FormatError("EOF in header".into()));
}
Some(line) => {
if line.is_empty() {
break;
} else if line[0] == b'#' {
continue;
}
let line = String::from_utf8_lossy(&line[..]);
try!(attributes.update_header_info(&line, strict));
}
}
}
}
let (width, height) = match try!(read_line_u8(&mut reader)) {
None => {
return Err(ImageError::FormatError("EOF in dimensions line".into()));
}
Some(dimensions) => {
let dimensions = String::from_utf8_lossy(&dimensions[..]);
try!(parse_dimensions_line(&dimensions, strict))
}
};
Ok(HDRDecoder {
r: reader,
width,
height,
meta: HDRMetadata {
width,
height,
..attributes
},
})
}
pub fn metadata(&self) -> HDRMetadata {
self.meta.clone()
}
pub fn read_image_native(mut self) -> ImageResult<Vec<RGBE8Pixel>> {
if self.width == 0 || self.height == 0 {
return Ok(vec![]);
}
let pixel_count = self.width as usize * self.height as usize;
let mut ret = vec![Default::default(); pixel_count];
for chunk in ret.chunks_mut(self.width as usize) {
try!(read_scanline(&mut self.r, chunk));
}
Ok(ret)
}
#[deprecated(
since = "0.21.3",
note = "For trivial types `T` this interface is less safe and less efficient than one taking\
an output slice. Semantics may be confusing for non-trivial types. Consider upgrading to v0.22.")]
pub fn read_image_transform<T: Send, F: Send + Sync + Fn(RGBE8Pixel) -> T>(
mut self,
f: F,
) -> ImageResult<Vec<T>> {
if self.width == 0 || self.height == 0 {
return Ok(vec![]);
}
let uszwidth = self.width as usize;
let uszheight = self.height as usize;
let pixel_count = uszwidth.checked_mul(uszheight);
if uszwidth as u32 != self.width || uszheight as u32 != self.height || pixel_count.is_none() {
return Err(ImageError::DimensionError)
}
let pixel_count = pixel_count.unwrap();
assert!(uszheight > 0);
assert!(uszwidth > 0);
assert!(uszwidth <= pixel_count);
assert!(pixel_count % uszwidth == 0);
assert!(pixel_count / uszwidth == uszheight);
assert!(pixel_count <= isize::max_value() as usize);
let mut ret = Vec::with_capacity(pixel_count);
struct UninitSlice<U: Send>(*mut U, isize);
unsafe impl<U: Send> Send for UninitSlice<U> { }
{
let chunks_base: *mut T = ret.as_mut_ptr();
let mut pool = Pool::new(8);
try!(pool.scoped(|scope| {
for chunk_index in 0..uszheight {
let mut buf = vec![RGBE8Pixel{ c: [0, 0, 0], e: 0, }; uszwidth];
try!(read_scanline(&mut self.r, &mut buf[..]));
let f = &f;
let chunk = unsafe {
let start: *mut T = chunks_base.offset((chunk_index * uszwidth) as isize);
UninitSlice(start, uszwidth as isize)
};
scope.execute(move || {
let UninitSlice(base, len) = chunk;
for (dst, &pix) in (0..len).zip(buf.iter()) {
unsafe {
ptr::write(base.offset(dst), f(pix))
}
}
});
}
Ok(())
}) as Result<(), ImageError>);
}
unsafe {
ret.set_len(pixel_count);
}
Ok(ret)
}
pub fn read_image_ldr(self) -> ImageResult<Vec<Rgb<u8>>> {
#[allow(deprecated)]
self.read_image_transform(|pix| pix.to_ldr())
}
pub fn read_image_hdr(self) -> ImageResult<Vec<Rgb<f32>>> {
#[allow(deprecated)]
self.read_image_transform(|pix| pix.to_hdr())
}
}
impl<R: BufRead> IntoIterator for HDRDecoder<R> {
type Item = ImageResult<RGBE8Pixel>;
type IntoIter = HDRImageDecoderIterator<R>;
fn into_iter(self) -> Self::IntoIter {
HDRImageDecoderIterator {
r: self.r,
scanline_cnt: self.height as usize,
buf: vec![Default::default(); self.width as usize],
col: 0,
scanline: 0,
trouble: true,
error_encountered: false,
}
}
}
pub struct HDRImageDecoderIterator<R: BufRead> {
r: R,
scanline_cnt: usize,
buf: Vec<RGBE8Pixel>,
col: usize,
scanline: usize,
trouble: bool,
error_encountered: bool,
}
impl<R: BufRead> HDRImageDecoderIterator<R> {
#[inline]
fn advance(&mut self) {
self.col += 1;
if self.col == self.buf.len() {
self.col = 0;
self.scanline += 1;
self.trouble = true;
}
}
}
impl<R: BufRead> Iterator for HDRImageDecoderIterator<R> {
type Item = ImageResult<RGBE8Pixel>;
fn next(&mut self) -> Option<Self::Item> {
if !self.trouble {
let ret = self.buf[self.col];
self.advance();
Some(Ok(ret))
} else {
if self.buf.is_empty() || self.scanline == self.scanline_cnt {
return None;
}
if self.error_encountered {
self.advance();
return Some(Err(ImageError::ImageEnd));
}
if self.col == 0 {
match read_scanline(&mut self.r, &mut self.buf[..]) {
Ok(_) => {
}
Err(err) => {
self.advance();
self.error_encountered = true;
self.trouble = true;
return Some(Err(err));
}
}
}
self.trouble = false;
let ret = self.buf[0];
self.advance();
Some(Ok(ret))
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let total_cnt = self.buf.len() * self.scanline_cnt;
let cur_cnt = self.buf.len() * self.scanline + self.col;
let remaining = total_cnt - cur_cnt;
(remaining, Some(remaining))
}
}
impl<R: BufRead> ExactSizeIterator for HDRImageDecoderIterator<R> {}
fn read_scanline<R: BufRead>(r: &mut R, buf: &mut [RGBE8Pixel]) -> ImageResult<()> {
assert!(!buf.is_empty());
let width = buf.len();
let fb = try!(read_rgbe(r));
if fb.c[0] == 2 && fb.c[1] == 2 && fb.c[2] < 128 {
try!(decode_component(r, width, |offset, value| buf[offset].c[0] = value));
try!(decode_component(r, width, |offset, value| buf[offset].c[1] = value));
try!(decode_component(r, width, |offset, value| buf[offset].c[2] = value));
try!(decode_component(r, width, |offset, value| buf[offset].e = value));
} else {
try!(decode_old_rle(r, fb, buf));
}
Ok(())
}
#[inline(always)]
fn read_byte<R: BufRead>(r: &mut R) -> io::Result<u8> {
let mut buf = [0u8];
try!(r.read_exact(&mut buf[..]));
Ok(buf[0])
}
#[inline]
fn decode_component<R: BufRead, S: FnMut(usize, u8)>(
r: &mut R,
width: usize,
mut set_component: S,
) -> ImageResult<()> {
let mut buf = [0; 128];
let mut pos = 0;
while pos < width {
pos += {
let rl = try!(read_byte(r));
if rl <= 128 {
if pos + rl as usize > width {
return Err(ImageError::FormatError(
"Wrong length of decoded scanline".into(),
));
}
try!(r.read_exact(&mut buf[0..rl as usize]));
for (offset, &value) in buf[0..rl as usize].iter().enumerate() {
set_component(pos + offset, value);
}
rl as usize
} else {
let rl = rl - 128;
if pos + rl as usize > width {
return Err(ImageError::FormatError(
"Wrong length of decoded scanline".into(),
));
}
let value = try!(read_byte(r));
for offset in 0..rl as usize {
set_component(pos + offset, value);
}
rl as usize
}
};
}
if pos != width {
return Err(ImageError::FormatError(
"Wrong length of decoded scanline".into(),
));
}
Ok(())
}
fn decode_old_rle<R: BufRead>(
r: &mut R,
fb: RGBE8Pixel,
buf: &mut [RGBE8Pixel],
) -> ImageResult<()> {
assert!(!buf.is_empty());
let width = buf.len();
#[inline]
fn rl_marker(pix: RGBE8Pixel) -> Option<usize> {
if pix.c == [1, 1, 1] {
Some(pix.e as usize)
} else {
None
}
}
if rl_marker(fb).is_some() {
return Err(ImageError::FormatError(
"First pixel of a scanline shouldn't be run length marker".into(),
));
}
buf[0] = fb;
let mut x_off = 1;
let mut rl_mult = 1;
let mut prev_pixel = fb;
while x_off < width {
let pix = try!(read_rgbe(r));
x_off += {
if let Some(rl) = rl_marker(pix) {
let rl = rl * rl_mult;
rl_mult *= 256;
if x_off + rl <= width {
for b in &mut buf[x_off..x_off + rl] {
*b = prev_pixel;
}
} else {
return Err(ImageError::FormatError(
"Wrong length of decoded scanline".into(),
));
};
rl
} else {
rl_mult = 1;
prev_pixel = pix;
buf[x_off] = pix;
1
}
};
}
if x_off != width {
return Err(ImageError::FormatError(
"Wrong length of decoded scanline".into(),
));
}
Ok(())
}
fn read_rgbe<R: BufRead>(r: &mut R) -> io::Result<RGBE8Pixel> {
let mut buf = [0u8; 4];
try!(r.read_exact(&mut buf[..]));
Ok(RGBE8Pixel {c: [buf[0], buf[1], buf[2]], e: buf[3] })
}
#[derive(Debug, Clone)]
pub struct HDRMetadata {
pub width: u32,
pub height: u32,
pub orientation: ((i8, i8), (i8, i8)),
pub exposure: Option<f32>,
pub color_correction: Option<(f32, f32, f32)>,
pub pixel_aspect_ratio: Option<f32>,
pub custom_attributes: Vec<(String, String)>,
}
impl HDRMetadata {
fn new() -> HDRMetadata {
HDRMetadata {
width: 0,
height: 0,
orientation: ((1, 0), (0, 1)),
exposure: None,
color_correction: None,
pixel_aspect_ratio: None,
custom_attributes: vec![],
}
}
fn update_header_info(&mut self, line: &str, strict: bool) -> ImageResult<()> {
let maybe_key_value = split_at_first(line, "=").map(|(key, value)| (key.trim(), value));
match maybe_key_value {
Some((key, val)) => self.custom_attributes
.push((key.to_owned(), val.to_owned())),
None => self.custom_attributes.push(("".into(), line.to_owned())),
}
match maybe_key_value {
Some(("FORMAT", val)) => {
if val.trim() != "32-bit_rle_rgbe" {
return Err(ImageError::UnsupportedError(limit_string_len(val, 20)));
}
}
Some(("EXPOSURE", val)) => {
match val.trim().parse::<f32>() {
Ok(v) => {
self.exposure = Some(self.exposure.unwrap_or(1.0) * v);
}
Err(parse_error) => {
if strict {
return Err(ImageError::FormatError(format!(
"Cannot parse EXPOSURE value: {}",
parse_error.description()
)));
}
}
};
}
Some(("PIXASPECT", val)) => {
match val.trim().parse::<f32>() {
Ok(v) => {
self.pixel_aspect_ratio = Some(self.pixel_aspect_ratio.unwrap_or(1.0) * v);
}
Err(parse_error) => {
if strict {
return Err(ImageError::FormatError(format!(
"Cannot parse PIXASPECT value: {}",
parse_error.description()
)));
}
}
};
}
Some(("COLORCORR", val)) => {
let mut rgbcorr = [1.0, 1.0, 1.0];
match parse_space_separated_f32(val, &mut rgbcorr, "COLORCORR") {
Ok(extra_numbers) => {
if strict && extra_numbers {
return Err(ImageError::FormatError(
"Extra numbers in COLORCORR".into(),
));
}
let (rc, gc, bc) = self.color_correction.unwrap_or((1.0, 1.0, 1.0));
self.color_correction =
Some((rc * rgbcorr[0], gc * rgbcorr[1], bc * rgbcorr[2]));
}
Err(err) => {
if strict {
return Err(err);
}
}
}
}
None => {
}
_ => {
}
}
Ok(())
}
}
fn parse_space_separated_f32(line: &str, vals: &mut [f32], name: &str) -> ImageResult<bool> {
let mut nums = line.split_whitespace();
for val in vals.iter_mut() {
if let Some(num) = nums.next() {
match num.parse::<f32>() {
Ok(v) => *val = v,
Err(err) => {
return Err(ImageError::FormatError(format!(
"f32 parse error in {}: {}",
name,
err.description()
)));
}
}
} else {
return Err(ImageError::FormatError(format!(
"Not enough numbers in {}",
name
)));
}
}
Ok(nums.next().is_some())
}
fn parse_dimensions_line(line: &str, strict: bool) -> ImageResult<(u32, u32)> {
let mut dim_parts = line.split_whitespace();
let err = "Malformed dimensions line";
let c1_tag = try!(
dim_parts
.next()
.ok_or_else(|| ImageError::FormatError(err.into()))
);
let c1_str = try!(
dim_parts
.next()
.ok_or_else(|| ImageError::FormatError(err.into()))
);
let c2_tag = try!(
dim_parts
.next()
.ok_or_else(|| ImageError::FormatError(err.into()))
);
let c2_str = try!(
dim_parts
.next()
.ok_or_else(|| ImageError::FormatError(err.into()))
);
if strict && dim_parts.next().is_some() {
return Err(ImageError::FormatError(err.into()));
}
match (c1_tag, c2_tag) {
("-Y", "+X") => {
let height = try!(c1_str.parse::<u32>().into_image_error(err));
let width = try!(c2_str.parse::<u32>().into_image_error(err));
Ok((width, height))
}
_ => Err(ImageError::FormatError(format!(
"Unsupported orientation {} {}",
limit_string_len(c1_tag, 4),
limit_string_len(c2_tag, 4)
))),
}
}
trait IntoImageError<T> {
fn into_image_error(self, description: &str) -> ImageResult<T>;
}
impl<T> IntoImageError<T> for ::std::result::Result<T, ::std::num::ParseFloatError> {
fn into_image_error(self, description: &str) -> ImageResult<T> {
self.map_err(|err| {
ImageError::FormatError(format!("{} {}", description, err.description()))
})
}
}
impl<T> IntoImageError<T> for ::std::result::Result<T, ::std::num::ParseIntError> {
fn into_image_error(self, description: &str) -> ImageResult<T> {
self.map_err(|err| {
ImageError::FormatError(format!("{} {}", description, err.description()))
})
}
}
fn limit_string_len(s: &str, len: usize) -> String {
let s_char_len = s.chars().count();
if s_char_len > len {
s.chars().take(len).chain("...".chars()).collect()
} else {
s.into()
}
}
fn split_at_first<'a>(s: &'a str, separator: &str) -> Option<(&'a str, &'a str)> {
match s.find(separator) {
None | Some(0) => None,
Some(p) if p >= s.len() - separator.len() => None,
Some(p) => Some((&s[..p], &s[(p + separator.len())..])),
}
}
#[test]
fn split_at_first_test() {
assert_eq!(split_at_first(&Cow::Owned("".into()), "="), None);
assert_eq!(split_at_first(&Cow::Owned("=".into()), "="), None);
assert_eq!(split_at_first(&Cow::Owned("= ".into()), "="), None);
assert_eq!(
split_at_first(&Cow::Owned(" = ".into()), "="),
Some((" ", " "))
);
assert_eq!(
split_at_first(&Cow::Owned("EXPOSURE= ".into()), "="),
Some(("EXPOSURE", " "))
);
assert_eq!(
split_at_first(&Cow::Owned("EXPOSURE= =".into()), "="),
Some(("EXPOSURE", " ="))
);
assert_eq!(
split_at_first(&Cow::Owned("EXPOSURE== =".into()), "=="),
Some(("EXPOSURE", " ="))
);
assert_eq!(split_at_first(&Cow::Owned("EXPOSURE".into()), ""), None);
}
fn read_line_u8<R: BufRead>(r: &mut R) -> ::std::io::Result<Option<Vec<u8>>> {
let mut ret = Vec::with_capacity(16);
match r.read_until(b'\n', &mut ret) {
Ok(0) => Ok(None),
Ok(_) => {
if let Some(&b'\n') = ret[..].last() {
let _ = ret.pop();
}
Ok(Some(ret))
}
Err(err) => Err(err),
}
}
#[test]
fn read_line_u8_test() {
let buf: Vec<_> = (&b"One\nTwo\nThree\nFour\n\n\n"[..]).into();
let input = &mut ::std::io::Cursor::new(buf);
assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"One"[..]);
assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Two"[..]);
assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Three"[..]);
assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b"Four"[..]);
assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b""[..]);
assert_eq!(&read_line_u8(input).unwrap().unwrap()[..], &b""[..]);
assert_eq!(read_line_u8(input).unwrap(), None);
}
pub fn read_raw_file<P: AsRef<Path>>(path: P) -> ::std::io::Result<Vec<Rgb<f32>>> {
use byteorder::{LittleEndian as LE, ReadBytesExt};
use std::fs::File;
use std::io::BufReader;
let mut r = BufReader::new(try!(File::open(path)));
let w = try!(r.read_u32::<LE>()) as usize;
let h = try!(r.read_u32::<LE>()) as usize;
let c = try!(r.read_u32::<LE>()) as usize;
assert_eq!(c, 3);
let cnt = w * h;
let mut ret = Vec::with_capacity(cnt);
for _ in 0..cnt {
let cr = try!(r.read_f32::<LE>());
let cg = try!(r.read_f32::<LE>());
let cb = try!(r.read_f32::<LE>());
ret.push(Rgb([cr, cg, cb]));
}
Ok(ret)
}