use alloc::collections::BTreeMap;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use core::iter::Iterator;
use core::option::Option::{self, *};
use core::result::Result::{self, *};
use zune_core::bytestream::{ZByteReaderTrait, ZReader};
use zune_core::colorspace::ColorSpace;
use zune_core::log::trace;
use zune_core::options::DecoderOptions;
use crate::errors::HdrDecodeErrors;
pub struct HdrDecoder<T: ZByteReaderTrait> {
buf: ZReader<T>,
options: DecoderOptions,
metadata: BTreeMap<String, String>,
width: usize,
height: usize,
decoded_headers: bool
}
impl<T> HdrDecoder<T>
where
T: ZByteReaderTrait
{
pub fn new(data: T) -> HdrDecoder<T> {
Self::new_with_options(data, DecoderOptions::default())
}
pub fn new_with_options(data: T, options: DecoderOptions) -> HdrDecoder<T> {
HdrDecoder {
buf: ZReader::new(data),
options,
width: 0,
height: 0,
metadata: BTreeMap::new(),
decoded_headers: false
}
}
pub const fn metadata(&self) -> &BTreeMap<String, String> {
&self.metadata
}
pub fn decode_headers(&mut self) -> Result<(), HdrDecodeErrors> {
let mut max_header_size = vec![0; 1024];
if self.decoded_headers {
return Ok(());
}
self.get_buffer_until(b'\n', &mut max_header_size)?;
if !(max_header_size.starts_with(b"#?RADIANCE\n")
|| max_header_size.starts_with(b"#?RGBE\n"))
{
return Err(HdrDecodeErrors::InvalidMagicBytes);
}
loop {
let size = self.get_buffer_until(b'\n', &mut max_header_size)?;
if max_header_size.starts_with(b"#")
{
continue;
}
if max_header_size[..size].contains(&b'=') {
let keys_and_values = String::from_utf8_lossy(&max_header_size[..size]);
let mut keys_and_values_split = keys_and_values.trim().split('=');
let key = keys_and_values_split.next().unwrap().trim().to_string();
let value = keys_and_values_split.next().unwrap().trim().to_string();
self.metadata.insert(key, value);
}
if size == 0 || max_header_size[0] == b'\n' {
trace!("Metadata: {:?}", self.metadata);
break;
}
}
let header_size = self.get_buffer_until(b' ', &mut max_header_size)?;
let first_type = String::from_utf8_lossy(&max_header_size[..header_size])
.trim()
.to_string();
let header_size = self.get_buffer_until(b' ', &mut max_header_size)?;
let coords1 = String::from_utf8_lossy(&max_header_size[..header_size])
.trim()
.to_string();
let header_size = self.get_buffer_until(b' ', &mut max_header_size)?;
let second_type = String::from_utf8_lossy(&max_header_size[..header_size])
.trim()
.to_string();
let header_size = self.get_buffer_until(b'\n', &mut max_header_size)?;
let coords2 = String::from_utf8_lossy(&max_header_size[..header_size])
.trim()
.to_string();
match (first_type.as_str(), second_type.as_str()) {
("-Y", "+X") => {
self.height = coords1.parse::<usize>()?;
self.width = coords2.parse::<usize>()?;
}
("+X", "-Y") => {
self.height = coords2.parse::<usize>()?;
self.width = coords1.parse::<usize>()?;
}
(_, _) => {
return Err(HdrDecodeErrors::UnsupportedOrientation(
first_type,
second_type
));
}
}
if self.height > self.options.max_height() {
return Err(HdrDecodeErrors::TooLargeDimensions(
"height",
self.options.max_height(),
self.height
));
}
if self.width > self.options.max_width() {
return Err(HdrDecodeErrors::TooLargeDimensions(
"width",
self.options.max_width(),
self.width
));
}
trace!("Width: {}", self.width);
trace!("Height: {}", self.height);
self.decoded_headers = true;
Ok(())
}
pub const fn dimensions(&self) -> Option<(usize, usize)> {
if self.decoded_headers {
Some((self.width, self.height))
} else {
None
}
}
pub fn get_colorspace(&self) -> Option<ColorSpace> {
if self.decoded_headers {
Some(ColorSpace::RGB)
} else {
None
}
}
pub fn decode(&mut self) -> Result<Vec<f32>, HdrDecodeErrors> {
self.decode_headers()?;
let mut buffer = vec![0.0f32; self.width * self.height * 3];
self.decode_into(&mut buffer)?;
Ok(buffer)
}
pub fn output_buffer_size(&self) -> Option<usize> {
if self.decoded_headers {
Some(self.width.checked_mul(self.height)?.checked_mul(3)?)
} else {
None
}
}
pub fn decode_into(&mut self, buffer: &mut [f32]) -> Result<(), HdrDecodeErrors> {
if !self.decoded_headers {
self.decode_headers()?;
}
let output_size = self.output_buffer_size().unwrap();
if buffer.len() < output_size {
return Err(HdrDecodeErrors::TooSmallOutputArray(
output_size,
buffer.len()
));
}
if self.width == 0 {
return Err(HdrDecodeErrors::Generic("Width cannot be 0"));
}
let mut scanline = vec![0_u8; self.width * 4];
let output_scanline_size = self.width * 3;
for out_scanline in buffer
.chunks_exact_mut(output_scanline_size)
.take(self.height)
{
if self.width < 8 || self.width > 0x7fff {
self.decompress(&mut scanline, self.width as i32, 0)?;
convert_scanline(&scanline, out_scanline);
continue;
}
let mut i = self.buf.read_u8();
if i != 2 {
self.buf.rewind(1)?;
self.decompress(&mut scanline, self.width as i32, 0)?;
convert_scanline(&scanline, out_scanline);
continue;
}
scanline[1] = self.buf.read_u8_err()?;
scanline[2] = self.buf.read_u8_err()?;
i = self.buf.read_u8_err()?;
if scanline[1] != 2 || (scanline[2] & 128) != 0 {
scanline[0] = 2;
scanline[3] = i;
self.decompress(&mut scanline[4..], self.width as i32 - 1, 0)?;
convert_scanline(&scanline, out_scanline);
continue;
}
for i in 0..4 {
let new_scanline = &mut scanline[i..];
let mut j = 0;
loop {
if j >= self.width * 4 {
break;
}
let mut run = i32::from(self.buf.read_u8_err()?);
if run > 128 {
let val = self.buf.read_u8();
run &= 127;
while run > 0 {
run -= 1;
if j >= self.width * 4 {
break;
}
new_scanline[j] = val;
j += 4;
}
} else if run > 0 {
while run > 0 {
run -= 1;
if j >= self.width * 4 {
break;
}
new_scanline[j] = self.buf.read_u8();
j += 4;
}
}
}
}
convert_scanline(&scanline, out_scanline);
}
Ok(())
}
fn decompress(
&mut self, scanline: &mut [u8], mut width: i32, mut scanline_offset: usize
) -> Result<(), HdrDecodeErrors> {
let mut shift = 0;
while width > 0 {
scanline[scanline_offset] = self.buf.read_u8_err()?;
scanline[scanline_offset + 1] = self.buf.read_u8_err()?;
scanline[scanline_offset + 2] = self.buf.read_u8_err()?;
scanline[scanline_offset + 3] = self.buf.read_u8_err()?;
if scanline[scanline_offset] == 1
&& scanline[scanline_offset + 1] == 1
&& scanline[scanline_offset + 2] == 1
{
let run = scanline[scanline_offset + 3];
let mut i = i32::from(run) << shift;
while width > 0 && scanline_offset > 4 && i > 0 {
scanline.copy_within(scanline_offset - 4..scanline_offset, 4);
scanline_offset += 4;
i -= 1;
width -= 4;
}
shift += 8;
if shift > 16 {
break;
}
} else {
scanline_offset += 4;
width -= 1;
shift = 0;
}
}
Ok(())
}
fn get_buffer_until(
&mut self, needle: u8, write_to: &mut Vec<u8>
) -> Result<usize, HdrDecodeErrors> {
write_to.clear();
let start = self.buf.position()?;
while !self.buf.eof()? {
let byte = self.buf.read_u8_err()?;
write_to.push(byte);
if byte == needle {
break;
}
}
let end = self.buf.position()?;
Ok(usize::try_from(end - start).unwrap())
}
}
fn convert_scanline(in_scanline: &[u8], out_scanline: &mut [f32]) {
for (rgbe, out) in in_scanline
.chunks_exact(4)
.zip(out_scanline.chunks_exact_mut(3))
{
if rgbe[3] == 0 {
out[0..3].fill(0.0);
} else {
let epxo = i32::from(rgbe[3]) - 128;
if epxo.is_positive() {
out[0] = convert_pos(i32::from(rgbe[0]), epxo);
out[1] = convert_pos(i32::from(rgbe[1]), epxo);
out[2] = convert_pos(i32::from(rgbe[2]), epxo);
} else {
out[0] = convert_neg(i32::from(rgbe[0]), epxo);
out[1] = convert_neg(i32::from(rgbe[1]), epxo);
out[2] = convert_neg(i32::from(rgbe[2]), epxo);
}
}
}
}
fn ldexp_pos(x: f32, exp: u32) -> f32 {
let pow = 1_u32.wrapping_shl(exp) as f32;
x * pow
}
fn ldexp_neg(x: f32, exp: u32) -> f32 {
let pow = 1_u32.wrapping_shl(exp) as f32;
x / pow
}
#[inline]
fn convert_pos(val: i32, exponent: i32) -> f32 {
let v = (val as f32) / 256.0;
ldexp_pos(v, exponent.unsigned_abs() & 31)
}
#[inline]
fn convert_neg(val: i32, exponent: i32) -> f32 {
let v = (val as f32) / 256.0;
ldexp_neg(v, exponent.unsigned_abs() & 31)
}