diff --git a/src/filters.rs b/src/filters.rs index 5e18fa47..54b9bb63 100644 --- a/src/filters.rs +++ b/src/filters.rs @@ -62,30 +62,31 @@ pub fn filter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8], buf: & } } -pub fn unfilter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8]) -> Vec { - let mut unfiltered = Vec::with_capacity(data.len()); +pub fn unfilter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8], buf: &mut Vec) { + assert_eq!(buf.len(), 0); + buf.reserve(data.len()); match filter { 0 => { - unfiltered.extend_from_slice(data); + buf.extend_from_slice(data); } 1 => { for (i, byte) in data.iter().enumerate() { match i.checked_sub(bpp) { Some(x) => { - let b = unfiltered[x]; - unfiltered.push(byte.wrapping_add(b)); + let b = buf[x]; + buf.push(byte.wrapping_add(b)); } None => { - unfiltered.push(*byte); + buf.push(*byte); } }; } } 2 => { if last_line.is_empty() { - unfiltered.extend_from_slice(data); + buf.extend_from_slice(data); } else { - unfiltered.extend( + buf.extend( data.iter() .zip(last_line.iter()) .map(|(cur, last)| cur.wrapping_add(*last)), @@ -97,23 +98,23 @@ pub fn unfilter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8]) -> V if last_line.is_empty() { match i.checked_sub(bpp) { Some(x) => { - let b = unfiltered[x]; - unfiltered.push(byte.wrapping_add(b >> 1)); + let b = buf[x]; + buf.push(byte.wrapping_add(b >> 1)); } None => { - unfiltered.push(*byte); + buf.push(*byte); } }; } else { match i.checked_sub(bpp) { Some(x) => { - let b = unfiltered[x]; - unfiltered.push(byte.wrapping_add( + let b = buf[x]; + buf.push(byte.wrapping_add( ((u16::from(b) + u16::from(last_line[i])) >> 1) as u8, )); } None => { - unfiltered.push(byte.wrapping_add(last_line[i] >> 1)); + buf.push(byte.wrapping_add(last_line[i] >> 1)); } }; }; @@ -124,25 +125,25 @@ pub fn unfilter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8]) -> V if last_line.is_empty() { match i.checked_sub(bpp) { Some(x) => { - let b = unfiltered[x]; - unfiltered.push(byte.wrapping_add(b)); + let b = buf[x]; + buf.push(byte.wrapping_add(b)); } None => { - unfiltered.push(*byte); + buf.push(*byte); } }; } else { match i.checked_sub(bpp) { Some(x) => { - let b = unfiltered[x]; - unfiltered.push(byte.wrapping_add(paeth_predictor( + let b = buf[x]; + buf.push(byte.wrapping_add(paeth_predictor( b, last_line[i], last_line[x], ))); } None => { - unfiltered.push(byte.wrapping_add(last_line[i])); + buf.push(byte.wrapping_add(last_line[i])); } }; }; @@ -150,7 +151,6 @@ pub fn unfilter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8]) -> V } _ => unreachable!(), } - unfiltered } fn paeth_predictor(a: u8, b: u8, c: u8) -> u8 { diff --git a/src/png/mod.rs b/src/png/mod.rs index 33799f92..0077551b 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -282,17 +282,19 @@ impl PngImage { let bpp = ((self.ihdr.bit_depth.as_u8() * self.channels_per_pixel() + 7) / 8) as usize; let mut last_line: Vec = Vec::new(); let mut last_pass = 1; + let mut unfiltered_buf = Vec::new(); for line in self.scan_lines() { if let Some(pass) = line.pass { if pass != last_pass { - last_line = Vec::new(); + last_line.clear(); last_pass = pass; } } - let unfiltered_line = unfilter_line(line.filter, bpp, &line.data, &last_line); + unfilter_line(line.filter, bpp, &line.data, &last_line, &mut unfiltered_buf); unfiltered.push(0); - unfiltered.extend_from_slice(&unfiltered_line); - last_line = unfiltered_line; + unfiltered.extend_from_slice(&unfiltered_buf); + std::mem::swap(&mut last_line, &mut unfiltered_buf); + unfiltered_buf.clear(); } unfiltered }