Reduce allocations (#122)
* Iterate with fewer temp allocations * Avoid allocation when not reducing * Avoid slow modulo
This commit is contained in:
parent
c530bba261
commit
76a2e51c15
4 changed files with 68 additions and 53 deletions
|
|
@ -295,7 +295,7 @@ impl PngData {
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
last_line = line.data;
|
last_line = line.data.to_vec();
|
||||||
last_pass = line.pass;
|
last_pass = line.pass;
|
||||||
}
|
}
|
||||||
filtered
|
filtered
|
||||||
|
|
@ -322,17 +322,17 @@ impl PngData {
|
||||||
|
|
||||||
for line in self.scan_lines() {
|
for line in self.scan_lines() {
|
||||||
reduced.push(line.filter);
|
reduced.push(line.filter);
|
||||||
for (i, byte) in line.data.iter().enumerate() {
|
for (i, &byte) in line.data.iter().enumerate() {
|
||||||
if i % 2 == 0 {
|
if i % 2 == 0 {
|
||||||
// High byte
|
// High byte
|
||||||
high_byte = *byte;
|
high_byte = byte;
|
||||||
} else {
|
} else {
|
||||||
// Low byte
|
// Low byte
|
||||||
if high_byte != *byte {
|
if high_byte != byte {
|
||||||
// Can't reduce, exit early
|
// Can't reduce, exit early
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
reduced.push(*byte);
|
reduced.push(byte);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -406,8 +406,8 @@ impl PngData {
|
||||||
let mut seen = HashSet::with_capacity(indexed_palette.len());
|
let mut seen = HashSet::with_capacity(indexed_palette.len());
|
||||||
for line in self.scan_lines() {
|
for line in self.scan_lines() {
|
||||||
match self.ihdr_data.bit_depth {
|
match self.ihdr_data.bit_depth {
|
||||||
BitDepth::Eight => for byte in &line.data {
|
BitDepth::Eight => for &byte in line.data {
|
||||||
seen.insert(*byte);
|
seen.insert(byte);
|
||||||
},
|
},
|
||||||
BitDepth::Four => {
|
BitDepth::Four => {
|
||||||
let bitvec = BitVec::from_bytes(&line.data);
|
let bitvec = BitVec::from_bytes(&line.data);
|
||||||
|
|
@ -489,52 +489,52 @@ impl PngData {
|
||||||
for line in self.scan_lines() {
|
for line in self.scan_lines() {
|
||||||
new_data.push(line.filter);
|
new_data.push(line.filter);
|
||||||
match self.ihdr_data.bit_depth {
|
match self.ihdr_data.bit_depth {
|
||||||
BitDepth::Eight => for byte in &line.data {
|
BitDepth::Eight => for &byte in line.data {
|
||||||
if let Some(new_idx) = index_map.get(byte) {
|
if let Some(&new_idx) = index_map.get(&byte) {
|
||||||
new_data.push(*new_idx);
|
new_data.push(new_idx);
|
||||||
} else {
|
} else {
|
||||||
new_data.push(*byte);
|
new_data.push(byte);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BitDepth::Four => for byte in &line.data {
|
BitDepth::Four => for &byte in line.data {
|
||||||
let upper = *byte & 0b1111_0000;
|
let upper = byte & 0b1111_0000;
|
||||||
let lower = *byte & 0b0000_1111;
|
let lower = byte & 0b0000_1111;
|
||||||
let mut new_byte = 0u8;
|
let mut new_byte = 0u8;
|
||||||
new_byte |= if let Some(new_idx) = index_map.get(&(upper >> 4)) {
|
new_byte |= if let Some(&new_idx) = index_map.get(&(upper >> 4)) {
|
||||||
*new_idx << 4
|
new_idx << 4
|
||||||
} else {
|
} else {
|
||||||
upper
|
upper
|
||||||
};
|
};
|
||||||
new_byte |= if let Some(new_idx) = index_map.get(&lower) {
|
new_byte |= if let Some(&new_idx) = index_map.get(&lower) {
|
||||||
*new_idx
|
new_idx
|
||||||
} else {
|
} else {
|
||||||
lower
|
lower
|
||||||
};
|
};
|
||||||
new_data.push(new_byte);
|
new_data.push(new_byte);
|
||||||
},
|
},
|
||||||
BitDepth::Two => for byte in &line.data {
|
BitDepth::Two => for &byte in line.data {
|
||||||
let one = *byte & 0b1100_0000;
|
let one = byte & 0b1100_0000;
|
||||||
let two = *byte & 0b0011_0000;
|
let two = byte & 0b0011_0000;
|
||||||
let three = *byte & 0b0000_1100;
|
let three = byte & 0b0000_1100;
|
||||||
let four = *byte & 0b0000_0011;
|
let four = byte & 0b0000_0011;
|
||||||
let mut new_byte = 0u8;
|
let mut new_byte = 0u8;
|
||||||
new_byte |= if let Some(new_idx) = index_map.get(&(one >> 6)) {
|
new_byte |= if let Some(&new_idx) = index_map.get(&(one >> 6)) {
|
||||||
*new_idx << 6
|
new_idx << 6
|
||||||
} else {
|
} else {
|
||||||
one
|
one
|
||||||
};
|
};
|
||||||
new_byte |= if let Some(new_idx) = index_map.get(&(two >> 4)) {
|
new_byte |= if let Some(&new_idx) = index_map.get(&(two >> 4)) {
|
||||||
*new_idx << 4
|
new_idx << 4
|
||||||
} else {
|
} else {
|
||||||
two
|
two
|
||||||
};
|
};
|
||||||
new_byte |= if let Some(new_idx) = index_map.get(&(three >> 2)) {
|
new_byte |= if let Some(&new_idx) = index_map.get(&(three >> 2)) {
|
||||||
*new_idx << 2
|
new_idx << 2
|
||||||
} else {
|
} else {
|
||||||
three
|
three
|
||||||
};
|
};
|
||||||
new_byte |= if let Some(new_idx) = index_map.get(&four) {
|
new_byte |= if let Some(&new_idx) = index_map.get(&four) {
|
||||||
*new_idx
|
new_idx
|
||||||
} else {
|
} else {
|
||||||
four
|
four
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ pub struct ScanLines<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for ScanLines<'a> {
|
impl<'a> Iterator for ScanLines<'a> {
|
||||||
type Item = ScanLine;
|
type Item = ScanLine<'a>;
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.end == self.png.raw_data.len() {
|
if self.end == self.png.raw_data.len() {
|
||||||
None
|
None
|
||||||
|
|
@ -109,7 +109,7 @@ impl<'a> Iterator for ScanLines<'a> {
|
||||||
}
|
}
|
||||||
Some(ScanLine {
|
Some(ScanLine {
|
||||||
filter: self.png.raw_data[self.start],
|
filter: self.png.raw_data[self.start],
|
||||||
data: self.png.raw_data[(self.start + 1)..self.end].to_owned(),
|
data: &self.png.raw_data[(self.start + 1)..self.end],
|
||||||
pass: current_pass,
|
pass: current_pass,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -122,7 +122,7 @@ impl<'a> Iterator for ScanLines<'a> {
|
||||||
self.end = self.start + bytes_per_line + 1;
|
self.end = self.start + bytes_per_line + 1;
|
||||||
Some(ScanLine {
|
Some(ScanLine {
|
||||||
filter: self.png.raw_data[self.start],
|
filter: self.png.raw_data[self.start],
|
||||||
data: self.png.raw_data[(self.start + 1)..self.end].to_owned(),
|
data: &self.png.raw_data[(self.start + 1)..self.end],
|
||||||
pass: None,
|
pass: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -131,11 +131,11 @@ impl<'a> Iterator for ScanLines<'a> {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
/// A scan line in a PNG image
|
/// A scan line in a PNG image
|
||||||
pub struct ScanLine {
|
pub struct ScanLine<'a> {
|
||||||
/// The filter type used to encode the current scan line (0-4)
|
/// The filter type used to encode the current scan line (0-4)
|
||||||
pub filter: u8,
|
pub filter: u8,
|
||||||
/// The byte data for the current scan line, encoded with the filter specified in the `filter` field
|
/// The byte data for the current scan line, encoded with the filter specified in the `filter` field
|
||||||
pub data: Vec<u8>,
|
pub data: &'a[u8],
|
||||||
/// The current pass if the image is interlaced
|
/// The current pass if the image is interlaced
|
||||||
pub pass: Option<u8>,
|
pub pass: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,37 @@
|
||||||
use png::PngData;
|
use png::PngData;
|
||||||
|
|
||||||
pub fn reduce_alpha_channel(png: &mut PngData, bpp_factor: usize) -> Option<Vec<u8>> {
|
pub fn reduce_alpha_channel(png: &mut PngData, channels: u8) -> Option<Vec<u8>> {
|
||||||
let mut reduced = Vec::with_capacity(png.raw_data.len());
|
let byte_depth = png.ihdr_data.bit_depth.as_u8() >> 3;
|
||||||
let byte_depth: u8 = png.ihdr_data.bit_depth.as_u8() >> 3;
|
let bpp = channels * byte_depth;
|
||||||
let bpp: usize = bpp_factor * byte_depth as usize;
|
let bpp_mask = bpp - 1;
|
||||||
let colored_bytes = bpp - byte_depth as usize;
|
assert_eq!(0, bpp & bpp_mask);
|
||||||
|
let colored_bytes = bpp - byte_depth;
|
||||||
for line in png.scan_lines() {
|
for line in png.scan_lines() {
|
||||||
reduced.push(line.filter);
|
for (i, &byte) in line.data.iter().enumerate() {
|
||||||
for (i, byte) in line.data.iter().enumerate() {
|
if i as u8 & bpp_mask >= colored_bytes {
|
||||||
if i % bpp >= colored_bytes {
|
if byte != 255 {
|
||||||
if *byte != 255 {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
reduced.push(*byte);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut reduced = Vec::with_capacity(png.raw_data.len());
|
||||||
|
for line in png.scan_lines() {
|
||||||
|
reduced.push(line.filter);
|
||||||
|
for (i, &byte) in line.data.iter().enumerate() {
|
||||||
|
if i as u8 & bpp_mask >= colored_bytes {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
reduced.push(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sBIT contains information about alpha channel's original depth,
|
||||||
|
// and alpha has just been removed
|
||||||
if let Some(sbit_header) = png.aux_headers.get_mut(&"sBIT".to_string()) {
|
if let Some(sbit_header) = png.aux_headers.get_mut(&"sBIT".to_string()) {
|
||||||
assert_eq!(sbit_header.len(), bpp_factor);
|
assert_eq!(sbit_header.len(), channels as usize);
|
||||||
sbit_header.pop();
|
sbit_header.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,16 +16,18 @@ pub fn reduce_rgba_to_rgb(png: &mut PngData) -> bool {
|
||||||
|
|
||||||
pub fn reduce_rgba_to_grayscale_alpha(png: &mut PngData) -> bool {
|
pub fn reduce_rgba_to_grayscale_alpha(png: &mut PngData) -> bool {
|
||||||
let mut reduced = Vec::with_capacity(png.raw_data.len());
|
let mut reduced = Vec::with_capacity(png.raw_data.len());
|
||||||
let byte_depth: u8 = png.ihdr_data.bit_depth.as_u8() >> 3;
|
let byte_depth = png.ihdr_data.bit_depth.as_u8() >> 3;
|
||||||
let bpp: usize = 4 * byte_depth as usize;
|
let bpp = 4 * byte_depth;
|
||||||
let colored_bytes = bpp - byte_depth as usize;
|
let bpp_mask = bpp - 1;
|
||||||
|
assert_eq!(0, bpp & bpp_mask);
|
||||||
|
let colored_bytes = bpp - byte_depth;
|
||||||
for line in png.scan_lines() {
|
for line in png.scan_lines() {
|
||||||
reduced.push(line.filter);
|
reduced.push(line.filter);
|
||||||
let mut low_bytes = Vec::with_capacity(4);
|
let mut low_bytes = Vec::with_capacity(4);
|
||||||
let mut high_bytes = Vec::with_capacity(4);
|
let mut high_bytes = Vec::with_capacity(4);
|
||||||
let mut trans_bytes = Vec::with_capacity(byte_depth as usize);
|
let mut trans_bytes = Vec::with_capacity(byte_depth as usize);
|
||||||
for (i, byte) in line.data.iter().enumerate() {
|
for (i, byte) in line.data.iter().enumerate() {
|
||||||
if i % bpp < colored_bytes {
|
if i as u8 & bpp_mask < colored_bytes {
|
||||||
if byte_depth == 1 || i % 2 == 1 {
|
if byte_depth == 1 || i % 2 == 1 {
|
||||||
low_bytes.push(*byte);
|
low_bytes.push(*byte);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -35,7 +37,7 @@ pub fn reduce_rgba_to_grayscale_alpha(png: &mut PngData) -> bool {
|
||||||
trans_bytes.push(*byte);
|
trans_bytes.push(*byte);
|
||||||
}
|
}
|
||||||
|
|
||||||
if i % bpp == bpp - 1 {
|
if (i as u8 & bpp_mask) == bpp - 1 {
|
||||||
if low_bytes.iter().unique().count() > 1 {
|
if low_bytes.iter().unique().count() > 1 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue