1use alloc::collections::BTreeMap;
10use alloc::string::{String, ToString};
11use alloc::vec;
12use alloc::vec::Vec;
13use core::iter::Iterator;
14use core::option::Option::{self, *};
15use core::result::Result::{self, *};
16
17use zune_core::bytestream::{ZByteReaderTrait, ZReader};
18use zune_core::colorspace::ColorSpace;
19use zune_core::log::trace;
20use zune_core::options::DecoderOptions;
21
22use crate::errors::HdrDecodeErrors;
23
24pub struct HdrDecoder<T: ZByteReaderTrait> {
34 buf: ZReader<T>,
35 options: DecoderOptions,
36 metadata: BTreeMap<String, String>,
37 width: usize,
38 height: usize,
39 decoded_headers: bool
40}
41
42impl<T> HdrDecoder<T>
43where
44 T: ZByteReaderTrait
45{
46 pub fn new(data: T) -> HdrDecoder<T> {
64 Self::new_with_options(data, DecoderOptions::default())
65 }
66
67 pub fn new_with_options(data: T, options: DecoderOptions) -> HdrDecoder<T> {
91 HdrDecoder {
92 buf: ZReader::new(data),
93 options,
94 width: 0,
95 height: 0,
96 metadata: BTreeMap::new(),
97 decoded_headers: false
98 }
99 }
100 pub const fn metadata(&self) -> &BTreeMap<String, String> {
106 &self.metadata
107 }
108 pub fn decode_headers(&mut self) -> Result<(), HdrDecodeErrors> {
113 let mut max_header_size = vec![0; 1024];
115
116 if self.decoded_headers {
117 return Ok(());
118 }
119 self.get_buffer_until(b'\n', &mut max_header_size)?;
120
121 if !(max_header_size.starts_with(b"#?RADIANCE\n")
122 || max_header_size.starts_with(b"#?RGBE\n"))
123 {
124 return Err(HdrDecodeErrors::InvalidMagicBytes);
125 }
126
127 loop {
128 let size = self.get_buffer_until(b'\n', &mut max_header_size)?;
129 if max_header_size.starts_with(b"#")
130 {
132 continue;
133 }
134 if max_header_size[..size].contains(&b'=') {
135 let keys_and_values = String::from_utf8_lossy(&max_header_size[..size]);
138
139 let mut keys_and_values_split = keys_and_values.trim().split('=');
140 let key = keys_and_values_split.next().unwrap().trim().to_string();
141 let value = keys_and_values_split.next().unwrap().trim().to_string();
142 self.metadata.insert(key, value);
143 }
144
145 if size == 0 || max_header_size[0] == b'\n' {
146 trace!("Metadata: {:?}", self.metadata);
147 break;
148 }
149 }
150 let header_size = self.get_buffer_until(b' ', &mut max_header_size)?;
151
152 let first_type = String::from_utf8_lossy(&max_header_size[..header_size])
153 .trim()
154 .to_string();
155
156 let header_size = self.get_buffer_until(b' ', &mut max_header_size)?;
157
158 let coords1 = String::from_utf8_lossy(&max_header_size[..header_size])
159 .trim()
160 .to_string();
161
162 let header_size = self.get_buffer_until(b' ', &mut max_header_size)?;
163
164 let second_type = String::from_utf8_lossy(&max_header_size[..header_size])
165 .trim()
166 .to_string();
167
168 let header_size = self.get_buffer_until(b'\n', &mut max_header_size)?;
169
170 let coords2 = String::from_utf8_lossy(&max_header_size[..header_size])
171 .trim()
172 .to_string();
173
174 match (first_type.as_str(), second_type.as_str()) {
175 ("-Y", "+X") => {
176 self.height = coords1.parse::<usize>()?;
177 self.width = coords2.parse::<usize>()?;
178 }
179 ("+X", "-Y") => {
180 self.height = coords2.parse::<usize>()?;
181 self.width = coords1.parse::<usize>()?;
182 }
183 (_, _) => {
184 return Err(HdrDecodeErrors::UnsupportedOrientation(
185 first_type,
186 second_type
187 ));
188 }
189 }
190 if self.height > self.options.max_height() {
191 return Err(HdrDecodeErrors::TooLargeDimensions(
192 "height",
193 self.options.max_height(),
194 self.height
195 ));
196 }
197
198 if self.width > self.options.max_width() {
199 return Err(HdrDecodeErrors::TooLargeDimensions(
200 "width",
201 self.options.max_width(),
202 self.width
203 ));
204 }
205
206 trace!("Width: {}", self.width);
207 trace!("Height: {}", self.height);
208
209 self.decoded_headers = true;
210
211 Ok(())
212 }
213
214 pub const fn dimensions(&self) -> Option<(usize, usize)> {
221 if self.decoded_headers {
222 Some((self.width, self.height))
223 } else {
224 None
225 }
226 }
227
228 pub fn get_colorspace(&self) -> Option<ColorSpace> {
234 if self.decoded_headers {
235 Some(ColorSpace::RGB)
236 } else {
237 None
238 }
239 }
240
241 pub fn decode(&mut self) -> Result<Vec<f32>, HdrDecodeErrors> {
249 self.decode_headers()?;
250 let mut buffer = vec![0.0f32; self.width * self.height * 3];
251
252 self.decode_into(&mut buffer)?;
253
254 Ok(buffer)
255 }
256 pub fn output_buffer_size(&self) -> Option<usize> {
265 if self.decoded_headers {
266 Some(self.width.checked_mul(self.height)?.checked_mul(3)?)
267 } else {
268 None
269 }
270 }
271
272 pub fn decode_into(&mut self, buffer: &mut [f32]) -> Result<(), HdrDecodeErrors> {
296 if !self.decoded_headers {
297 self.decode_headers()?;
298 }
299
300 let output_size = self.output_buffer_size().unwrap();
301
302 if buffer.len() < output_size {
303 return Err(HdrDecodeErrors::TooSmallOutputArray(
304 output_size,
305 buffer.len()
306 ));
307 }
308 if self.width == 0 {
309 return Err(HdrDecodeErrors::Generic("Width cannot be 0"));
310 }
311
312 let mut scanline = vec![0_u8; self.width * 4]; let output_scanline_size = self.width * 3; for out_scanline in buffer
319 .chunks_exact_mut(output_scanline_size)
320 .take(self.height)
321 {
322 if self.width < 8 || self.width > 0x7fff {
323 self.decompress(&mut scanline, self.width as i32, 0)?;
324 convert_scanline(&scanline, out_scanline);
325 continue;
326 }
327
328 let mut i = self.buf.read_u8();
329
330 if i != 2 {
331 self.buf.rewind(1)?;
333
334 self.decompress(&mut scanline, self.width as i32, 0)?;
335 convert_scanline(&scanline, out_scanline);
336 continue;
337 }
338
339 scanline[1] = self.buf.read_u8_err()?;
340 scanline[2] = self.buf.read_u8_err()?;
341 i = self.buf.read_u8_err()?;
342
343 if scanline[1] != 2 || (scanline[2] & 128) != 0 {
344 scanline[0] = 2;
345 scanline[3] = i;
346
347 self.decompress(&mut scanline[4..], self.width as i32 - 1, 0)?;
348 convert_scanline(&scanline, out_scanline);
349 continue;
350 }
351
352 for i in 0..4 {
353 let new_scanline = &mut scanline[i..];
354
355 let mut j = 0;
356
357 loop {
358 if j >= self.width * 4 {
359 break;
360 }
361 let mut run = i32::from(self.buf.read_u8_err()?);
362
363 if run > 128 {
364 let val = self.buf.read_u8();
365 run &= 127;
366
367 while run > 0 {
368 run -= 1;
369
370 if j >= self.width * 4 {
371 break;
372 }
373 new_scanline[j] = val;
374 j += 4;
375 }
376 } else if run > 0 {
377 while run > 0 {
378 run -= 1;
379
380 if j >= self.width * 4 {
381 break;
382 }
383
384 new_scanline[j] = self.buf.read_u8();
385 j += 4;
386 }
387 }
388 }
389 }
390 convert_scanline(&scanline, out_scanline);
391 }
392
393 Ok(())
394 }
395
396 fn decompress(
397 &mut self, scanline: &mut [u8], mut width: i32, mut scanline_offset: usize
398 ) -> Result<(), HdrDecodeErrors> {
399 let mut shift = 0;
400
401 while width > 0 {
402 scanline[scanline_offset] = self.buf.read_u8_err()?;
403 scanline[scanline_offset + 1] = self.buf.read_u8_err()?;
404 scanline[scanline_offset + 2] = self.buf.read_u8_err()?;
405 scanline[scanline_offset + 3] = self.buf.read_u8_err()?;
406
407 if scanline[scanline_offset] == 1
408 && scanline[scanline_offset + 1] == 1
409 && scanline[scanline_offset + 2] == 1
410 {
411 let run = scanline[scanline_offset + 3];
412
413 let mut i = i32::from(run) << shift;
414
415 while width > 0 && scanline_offset > 4 && i > 0 {
416 scanline.copy_within(scanline_offset - 4..scanline_offset, 4);
417 scanline_offset += 4;
418 i -= 1;
419 width -= 4;
420 }
421 shift += 8;
422
423 if shift > 16 {
424 break;
425 }
426 } else {
427 scanline_offset += 4;
428 width -= 1;
429 shift = 0;
430 }
431 }
432 Ok(())
433 }
434
435 fn get_buffer_until(
441 &mut self, needle: u8, write_to: &mut Vec<u8>
442 ) -> Result<usize, HdrDecodeErrors> {
443 write_to.clear();
444 let start = self.buf.position()?;
445
446 while !self.buf.eof()? {
447 let byte = self.buf.read_u8_err()?;
448 write_to.push(byte);
449
450 if byte == needle {
451 break;
452 }
453 }
454 let end = self.buf.position()?;
455
456 Ok(usize::try_from(end - start).unwrap())
457 }
458}
459
460fn convert_scanline(in_scanline: &[u8], out_scanline: &mut [f32]) {
461 for (rgbe, out) in in_scanline
462 .chunks_exact(4)
463 .zip(out_scanline.chunks_exact_mut(3))
464 {
465 if rgbe[3] == 0 {
466 out[0..3].fill(0.0);
467 } else {
468 let epxo = i32::from(rgbe[3]) - 128;
471
472 if epxo.is_positive() {
473 out[0] = convert_pos(i32::from(rgbe[0]), epxo);
474 out[1] = convert_pos(i32::from(rgbe[1]), epxo);
475 out[2] = convert_pos(i32::from(rgbe[2]), epxo);
476 } else {
477 out[0] = convert_neg(i32::from(rgbe[0]), epxo);
478 out[1] = convert_neg(i32::from(rgbe[1]), epxo);
479 out[2] = convert_neg(i32::from(rgbe[2]), epxo);
480 }
481 }
482 }
483}
484
485fn ldexp_pos(x: f32, exp: u32) -> f32 {
486 let pow = 1_u32.wrapping_shl(exp) as f32;
487 x * pow
488}
489fn ldexp_neg(x: f32, exp: u32) -> f32 {
490 let pow = 1_u32.wrapping_shl(exp) as f32;
491 x / pow
492}
493#[inline]
510fn convert_pos(val: i32, exponent: i32) -> f32 {
511 let v = (val as f32) / 256.0;
512 ldexp_pos(v, exponent.unsigned_abs() & 31)
513}
514
515#[inline]
516fn convert_neg(val: i32, exponent: i32) -> f32 {
517 let v = (val as f32) / 256.0;
518 ldexp_neg(v, exponent.unsigned_abs() & 31)
519}