Skip to content

Commit 3a0e1fa

Browse files
committed
Remove some allocations from HDR image
1 parent 2a5c5bf commit 3a0e1fa

File tree

2 files changed

+41
-28
lines changed

2 files changed

+41
-28
lines changed

src/codecs/hdr/decoder.rs

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,7 @@ impl<R: BufRead> HdrAdapter<R> {
156156
fn read_image_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
157157
assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
158158
match self.inner.take() {
159-
Some(decoder) => {
160-
let img: Vec<Rgb<u8>> = decoder.read_image_ldr()?;
161-
for (i, Rgb(data)) in img.into_iter().enumerate() {
162-
buf[(i * 3)..][..3].copy_from_slice(&data);
163-
}
164-
165-
Ok(())
166-
}
159+
Some(decoder) => decoder.read_image_ldr_buf(buf),
167160
None => Err(ImageError::Parameter(ParameterError::from_kind(
168161
ParameterErrorKind::NoMoreData,
169162
))),
@@ -444,29 +437,46 @@ impl<R: BufRead> HdrDecoder<R> {
444437

445438
/// Consumes decoder and returns a vector of transformed pixels
446439
pub fn read_image_transform<T: Send, F: Send + Sync + Fn(Rgbe8Pixel) -> T>(
440+
self,
441+
f: F,
442+
output_slice: &mut [T],
443+
) -> ImageResult<()> {
444+
// a bit hacky, but this function was in the public API and
445+
// thus needs to preserve it's signature.
446+
self.read_image_transform_flat(move |v| [f(v)], output_slice)
447+
}
448+
449+
/// Consumes decoder and returns a vector of transformed pixels
450+
fn read_image_transform_flat<
451+
T: Send,
452+
F: Send + Sync + Fn(Rgbe8Pixel) -> [T; N],
453+
const N: usize,
454+
>(
447455
mut self,
448456
f: F,
449457
output_slice: &mut [T],
450458
) -> ImageResult<()> {
451459
assert_eq!(
452460
output_slice.len(),
453-
self.width as usize * self.height as usize
461+
self.width as usize * self.height as usize * N
454462
);
455463

456464
// Don't read anything if image is empty
457465
if self.width == 0 || self.height == 0 {
458466
return Ok(());
459467
}
460468

461-
let chunks_iter = output_slice.chunks_mut(self.width as usize);
469+
let chunks_iter = output_slice.chunks_mut(self.width as usize * N);
462470

463471
let mut buf = vec![Default::default(); self.width as usize];
464472
for chunk in chunks_iter {
465473
// read_scanline overwrites the entire buffer or returns an Err,
466474
// so not resetting the buffer here is ok.
467475
read_scanline(&mut self.r, &mut buf[..])?;
468-
for (dst, &pix) in chunk.iter_mut().zip(buf.iter()) {
469-
*dst = f(pix);
476+
for (dst, &pix) in chunk.chunks_mut(N).zip(buf.iter()) {
477+
for (d, s) in dst.iter_mut().zip(IntoIterator::into_iter(f(pix))) {
478+
*d = s;
479+
}
470480
}
471481
}
472482
Ok(())
@@ -475,17 +485,22 @@ impl<R: BufRead> HdrDecoder<R> {
475485
/// Consumes decoder and returns a vector of `Rgb<u8>` pixels.
476486
/// scale = 1, gamma = 2.2
477487
pub fn read_image_ldr(self) -> ImageResult<Vec<Rgb<u8>>> {
478-
let mut ret = vec![Rgb([0, 0, 0]); self.width as usize * self.height as usize];
479-
self.read_image_transform(|pix| pix.to_ldr(), &mut ret[..])?;
480-
Ok(ret)
488+
let sz = (self.width * self.height) as usize;
489+
let mut buf = vec![Rgb([0; 3]); sz];
490+
self.read_image_transform(|pix| pix.to_ldr(), &mut buf[..])?;
491+
Ok(buf)
492+
}
493+
fn read_image_ldr_buf(self, buf: &mut [u8]) -> ImageResult<()> {
494+
let sz = (self.width * self.height * 3) as usize;
495+
assert!(buf.len() >= sz);
496+
self.read_image_transform_flat(|pix| pix.to_ldr().0, &mut buf[..sz])
481497
}
482-
483498
/// Consumes decoder and returns a vector of `Rgb<f32>` pixels.
484-
///
485499
pub fn read_image_hdr(self) -> ImageResult<Vec<Rgb<f32>>> {
486-
let mut ret = vec![Rgb([0.0, 0.0, 0.0]); self.width as usize * self.height as usize];
487-
self.read_image_transform(|pix| pix.to_hdr(), &mut ret[..])?;
488-
Ok(ret)
500+
let sz = (self.width * self.height) as usize;
501+
let mut buf = vec![Rgb([0.0f32; 3]); sz];
502+
self.read_image_transform(|pix| pix.to_hdr(), &mut buf[..])?;
503+
Ok(buf)
489504
}
490505
}
491506

@@ -971,15 +986,14 @@ fn split_at_first_test() {
971986
// or return None to indicate end of file
972987
fn read_line_u8<R: BufRead>(r: &mut R) -> ::std::io::Result<Option<Vec<u8>>> {
973988
let mut ret = Vec::with_capacity(16);
974-
match r.read_until(b'\n', &mut ret) {
975-
Ok(0) => Ok(None),
976-
Ok(_) => {
977-
if let Some(&b'\n') = ret[..].last() {
989+
match r.read_until(b'\n', &mut ret)? {
990+
0 => Ok(None),
991+
_ => {
992+
if let Some(&b'\n') = ret.last() {
978993
let _ = ret.pop();
979994
}
980995
Ok(Some(ret))
981996
}
982-
Err(err) => Err(err),
983997
}
984998
}
985999

src/codecs/openexr.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -467,12 +467,11 @@ mod test {
467467
.clone()
468468
.join("overexposed gradient - data window equals display window.exr");
469469

470-
let hdr: Vec<Rgb<f32>> = crate::codecs::hdr::HdrDecoder::new(std::io::BufReader::new(
470+
let hdr_decoder = crate::codecs::hdr::HdrDecoder::new(std::io::BufReader::new(
471471
std::fs::File::open(&reference_path).unwrap(),
472472
))
473-
.unwrap()
474-
.read_image_hdr()
475473
.unwrap();
474+
let hdr = hdr_decoder.read_image_hdr().unwrap();
476475

477476
let exr_pixels: Rgb32FImage = read_as_rgb_image_from_file(exr_path).unwrap();
478477
assert_eq!(

0 commit comments

Comments
 (0)