From b6cc8e24148f25ed6d06a5ef2f9d263ddf96aa76 Mon Sep 17 00:00:00 2001 From: Kornel Date: Wed, 19 May 2021 20:16:14 +0100 Subject: [PATCH] Optimize unfiltering (#400) * Clippy lints * Fewer pushes * Remove special case for empty last line * Zip with last line * Combine bound checks --- src/filters.rs | 97 +++++++++++++++----------------------------------- src/png/mod.rs | 1 + 2 files changed, 30 insertions(+), 68 deletions(-) diff --git a/src/filters.rs b/src/filters.rs index f370778b..0c4d50c0 100644 --- a/src/filters.rs +++ b/src/filters.rs @@ -65,88 +65,49 @@ 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], buf: &mut Vec) { buf.clear(); buf.reserve(data.len()); + assert_eq!(data.len(), last_line.len()); match filter { 0 => { buf.extend_from_slice(data); } 1 => { - for (i, byte) in data.iter().enumerate() { - match i.checked_sub(bpp) { - Some(x) => { - let b = buf[x]; - buf.push(byte.wrapping_add(b)); - } - None => { - buf.push(*byte); - } - }; + for (i, &cur) in data.iter().enumerate() { + let prev_byte = i.checked_sub(bpp).and_then(|x| buf.get(x).copied()); + buf.push(match prev_byte { + Some(b) => cur.wrapping_add(b), + None => cur, + }); } } 2 => { - if last_line.is_empty() { - buf.extend_from_slice(data); - } else { - buf.extend( - data.iter() - .zip(last_line.iter()) - .map(|(cur, last)| cur.wrapping_add(*last)), - ); - }; + buf.extend( + data.iter() + .zip(last_line) + .map(|(&cur, &last)| cur.wrapping_add(last)), + ); } 3 => { - for (i, byte) in data.iter().enumerate() { - if last_line.is_empty() { - match i.checked_sub(bpp) { - Some(x) => { - let b = buf[x]; - buf.push(byte.wrapping_add(b >> 1)); - } - None => { - buf.push(*byte); - } - }; - } else { - match i.checked_sub(bpp) { - Some(x) => { - let b = buf[x]; - buf.push(byte.wrapping_add( - ((u16::from(b) + u16::from(last_line[i])) >> 1) as u8, - )); - } - None => { - buf.push(byte.wrapping_add(last_line[i] >> 1)); - } - }; - }; + for (i, (&cur, &last)) in data.iter().zip(last_line).enumerate() { + let prev_byte = i.checked_sub(bpp).and_then(|x| buf.get(x).copied()); + buf.push(match prev_byte { + Some(b) => cur.wrapping_add(((u16::from(b) + u16::from(last)) >> 1) as u8), + None => cur.wrapping_add(last >> 1), + }); } } 4 => { - for (i, byte) in data.iter().enumerate() { - if last_line.is_empty() { - match i.checked_sub(bpp) { - Some(x) => { - let b = buf[x]; - buf.push(byte.wrapping_add(b)); + for (i, (&cur, &up)) in data.iter().zip(last_line).enumerate() { + buf.push( + match i + .checked_sub(bpp) + .map(|x| (buf.get(x).copied(), last_line.get(x).copied())) + { + Some((Some(left), Some(left_up))) => { + cur.wrapping_add(paeth_predictor(left, up, left_up)) } - None => { - buf.push(*byte); - } - }; - } else { - match i.checked_sub(bpp) { - Some(x) => { - let b = buf[x]; - buf.push(byte.wrapping_add(paeth_predictor( - b, - last_line[i], - last_line[x], - ))); - } - None => { - buf.push(byte.wrapping_add(last_line[i])); - } - }; - }; + _ => cur.wrapping_add(up), + }, + ); } } _ => unreachable!(), diff --git a/src/png/mod.rs b/src/png/mod.rs index e8b6dddc..a3601ffa 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -294,6 +294,7 @@ impl PngImage { last_pass = pass; } } + last_line.resize(line.data.len(), 0); unfilter_line( line.filter, bpp,