Reduce allocations while unfiltering by reusing buffers
This commit is contained in:
parent
1a6284b177
commit
e04153051c
2 changed files with 27 additions and 25 deletions
|
|
@ -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<u8> {
|
pub fn unfilter_line(filter: u8, bpp: usize, data: &[u8], last_line: &[u8], buf: &mut Vec<u8>) {
|
||||||
let mut unfiltered = Vec::with_capacity(data.len());
|
assert_eq!(buf.len(), 0);
|
||||||
|
buf.reserve(data.len());
|
||||||
match filter {
|
match filter {
|
||||||
0 => {
|
0 => {
|
||||||
unfiltered.extend_from_slice(data);
|
buf.extend_from_slice(data);
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
for (i, byte) in data.iter().enumerate() {
|
for (i, byte) in data.iter().enumerate() {
|
||||||
match i.checked_sub(bpp) {
|
match i.checked_sub(bpp) {
|
||||||
Some(x) => {
|
Some(x) => {
|
||||||
let b = unfiltered[x];
|
let b = buf[x];
|
||||||
unfiltered.push(byte.wrapping_add(b));
|
buf.push(byte.wrapping_add(b));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
unfiltered.push(*byte);
|
buf.push(*byte);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2 => {
|
2 => {
|
||||||
if last_line.is_empty() {
|
if last_line.is_empty() {
|
||||||
unfiltered.extend_from_slice(data);
|
buf.extend_from_slice(data);
|
||||||
} else {
|
} else {
|
||||||
unfiltered.extend(
|
buf.extend(
|
||||||
data.iter()
|
data.iter()
|
||||||
.zip(last_line.iter())
|
.zip(last_line.iter())
|
||||||
.map(|(cur, last)| cur.wrapping_add(*last)),
|
.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() {
|
if last_line.is_empty() {
|
||||||
match i.checked_sub(bpp) {
|
match i.checked_sub(bpp) {
|
||||||
Some(x) => {
|
Some(x) => {
|
||||||
let b = unfiltered[x];
|
let b = buf[x];
|
||||||
unfiltered.push(byte.wrapping_add(b >> 1));
|
buf.push(byte.wrapping_add(b >> 1));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
unfiltered.push(*byte);
|
buf.push(*byte);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
match i.checked_sub(bpp) {
|
match i.checked_sub(bpp) {
|
||||||
Some(x) => {
|
Some(x) => {
|
||||||
let b = unfiltered[x];
|
let b = buf[x];
|
||||||
unfiltered.push(byte.wrapping_add(
|
buf.push(byte.wrapping_add(
|
||||||
((u16::from(b) + u16::from(last_line[i])) >> 1) as u8,
|
((u16::from(b) + u16::from(last_line[i])) >> 1) as u8,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
None => {
|
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() {
|
if last_line.is_empty() {
|
||||||
match i.checked_sub(bpp) {
|
match i.checked_sub(bpp) {
|
||||||
Some(x) => {
|
Some(x) => {
|
||||||
let b = unfiltered[x];
|
let b = buf[x];
|
||||||
unfiltered.push(byte.wrapping_add(b));
|
buf.push(byte.wrapping_add(b));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
unfiltered.push(*byte);
|
buf.push(*byte);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
match i.checked_sub(bpp) {
|
match i.checked_sub(bpp) {
|
||||||
Some(x) => {
|
Some(x) => {
|
||||||
let b = unfiltered[x];
|
let b = buf[x];
|
||||||
unfiltered.push(byte.wrapping_add(paeth_predictor(
|
buf.push(byte.wrapping_add(paeth_predictor(
|
||||||
b,
|
b,
|
||||||
last_line[i],
|
last_line[i],
|
||||||
last_line[x],
|
last_line[x],
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
None => {
|
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!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
unfiltered
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paeth_predictor(a: u8, b: u8, c: u8) -> u8 {
|
fn paeth_predictor(a: u8, b: u8, c: u8) -> u8 {
|
||||||
|
|
|
||||||
|
|
@ -282,17 +282,19 @@ impl PngImage {
|
||||||
let bpp = ((self.ihdr.bit_depth.as_u8() * self.channels_per_pixel() + 7) / 8) as usize;
|
let bpp = ((self.ihdr.bit_depth.as_u8() * self.channels_per_pixel() + 7) / 8) as usize;
|
||||||
let mut last_line: Vec<u8> = Vec::new();
|
let mut last_line: Vec<u8> = Vec::new();
|
||||||
let mut last_pass = 1;
|
let mut last_pass = 1;
|
||||||
|
let mut unfiltered_buf = Vec::new();
|
||||||
for line in self.scan_lines() {
|
for line in self.scan_lines() {
|
||||||
if let Some(pass) = line.pass {
|
if let Some(pass) = line.pass {
|
||||||
if pass != last_pass {
|
if pass != last_pass {
|
||||||
last_line = Vec::new();
|
last_line.clear();
|
||||||
last_pass = pass;
|
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.push(0);
|
||||||
unfiltered.extend_from_slice(&unfiltered_line);
|
unfiltered.extend_from_slice(&unfiltered_buf);
|
||||||
last_line = unfiltered_line;
|
std::mem::swap(&mut last_line, &mut unfiltered_buf);
|
||||||
|
unfiltered_buf.clear();
|
||||||
}
|
}
|
||||||
unfiltered
|
unfiltered
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue