diff --git a/src/png/mod.rs b/src/png/mod.rs index d8afa106..5d8d432f 100644 --- a/src/png/mod.rs +++ b/src/png/mod.rs @@ -295,7 +295,7 @@ impl PngData { } _ => unreachable!(), } - last_line = line.data; + last_line = line.data.to_vec(); last_pass = line.pass; } filtered @@ -322,17 +322,17 @@ impl PngData { for line in self.scan_lines() { reduced.push(line.filter); - for (i, byte) in line.data.iter().enumerate() { + for (i, &byte) in line.data.iter().enumerate() { if i % 2 == 0 { // High byte - high_byte = *byte; + high_byte = byte; } else { // Low byte - if high_byte != *byte { + if high_byte != byte { // Can't reduce, exit early return false; } - reduced.push(*byte); + reduced.push(byte); } } } @@ -406,8 +406,8 @@ impl PngData { let mut seen = HashSet::with_capacity(indexed_palette.len()); for line in self.scan_lines() { match self.ihdr_data.bit_depth { - BitDepth::Eight => for byte in &line.data { - seen.insert(*byte); + BitDepth::Eight => for &byte in line.data { + seen.insert(byte); }, BitDepth::Four => { let bitvec = BitVec::from_bytes(&line.data); @@ -489,52 +489,52 @@ impl PngData { for line in self.scan_lines() { new_data.push(line.filter); match self.ihdr_data.bit_depth { - BitDepth::Eight => for byte in &line.data { - if let Some(new_idx) = index_map.get(byte) { - new_data.push(*new_idx); + BitDepth::Eight => for &byte in line.data { + if let Some(&new_idx) = index_map.get(&byte) { + new_data.push(new_idx); } else { - new_data.push(*byte); + new_data.push(byte); } }, - BitDepth::Four => for byte in &line.data { - let upper = *byte & 0b1111_0000; - let lower = *byte & 0b0000_1111; + BitDepth::Four => for &byte in line.data { + let upper = byte & 0b1111_0000; + let lower = byte & 0b0000_1111; let mut new_byte = 0u8; - new_byte |= if let Some(new_idx) = index_map.get(&(upper >> 4)) { - *new_idx << 4 + new_byte |= if let Some(&new_idx) = index_map.get(&(upper >> 4)) { + new_idx << 4 } else { upper }; - new_byte |= if let Some(new_idx) = index_map.get(&lower) { - *new_idx + new_byte |= if let Some(&new_idx) = index_map.get(&lower) { + new_idx } else { lower }; new_data.push(new_byte); }, - BitDepth::Two => for byte in &line.data { - let one = *byte & 0b1100_0000; - let two = *byte & 0b0011_0000; - let three = *byte & 0b0000_1100; - let four = *byte & 0b0000_0011; + BitDepth::Two => for &byte in line.data { + let one = byte & 0b1100_0000; + let two = byte & 0b0011_0000; + let three = byte & 0b0000_1100; + let four = byte & 0b0000_0011; let mut new_byte = 0u8; - new_byte |= if let Some(new_idx) = index_map.get(&(one >> 6)) { - *new_idx << 6 + new_byte |= if let Some(&new_idx) = index_map.get(&(one >> 6)) { + new_idx << 6 } else { one }; - new_byte |= if let Some(new_idx) = index_map.get(&(two >> 4)) { - *new_idx << 4 + new_byte |= if let Some(&new_idx) = index_map.get(&(two >> 4)) { + new_idx << 4 } else { two }; - new_byte |= if let Some(new_idx) = index_map.get(&(three >> 2)) { - *new_idx << 2 + new_byte |= if let Some(&new_idx) = index_map.get(&(three >> 2)) { + new_idx << 2 } else { three }; - new_byte |= if let Some(new_idx) = index_map.get(&four) { - *new_idx + new_byte |= if let Some(&new_idx) = index_map.get(&four) { + new_idx } else { four }; diff --git a/src/png/scan_lines.rs b/src/png/scan_lines.rs index a451e14a..bbf36a9d 100644 --- a/src/png/scan_lines.rs +++ b/src/png/scan_lines.rs @@ -12,7 +12,7 @@ pub struct ScanLines<'a> { } impl<'a> Iterator for ScanLines<'a> { - type Item = ScanLine; + type Item = ScanLine<'a>; fn next(&mut self) -> Option { if self.end == self.png.raw_data.len() { None @@ -109,7 +109,7 @@ impl<'a> Iterator for ScanLines<'a> { } Some(ScanLine { 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, }) } else { @@ -122,7 +122,7 @@ impl<'a> Iterator for ScanLines<'a> { self.end = self.start + bytes_per_line + 1; Some(ScanLine { 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, }) } @@ -131,11 +131,11 @@ impl<'a> Iterator for ScanLines<'a> { #[derive(Debug, Clone)] /// 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) pub filter: u8, /// The byte data for the current scan line, encoded with the filter specified in the `filter` field - pub data: Vec, + pub data: &'a[u8], /// The current pass if the image is interlaced pub pass: Option, } diff --git a/src/reduction/alpha.rs b/src/reduction/alpha.rs index 8be26072..18248271 100644 --- a/src/reduction/alpha.rs +++ b/src/reduction/alpha.rs @@ -1,24 +1,37 @@ use png::PngData; -pub fn reduce_alpha_channel(png: &mut PngData, bpp_factor: usize) -> Option> { - let mut reduced = Vec::with_capacity(png.raw_data.len()); - let byte_depth: u8 = png.ihdr_data.bit_depth.as_u8() >> 3; - let bpp: usize = bpp_factor * byte_depth as usize; - let colored_bytes = bpp - byte_depth as usize; +pub fn reduce_alpha_channel(png: &mut PngData, channels: u8) -> Option> { + let byte_depth = png.ihdr_data.bit_depth.as_u8() >> 3; + let bpp = channels * byte_depth; + let bpp_mask = bpp - 1; + assert_eq!(0, bpp & bpp_mask); + let colored_bytes = bpp - byte_depth; for line in png.scan_lines() { - reduced.push(line.filter); - for (i, byte) in line.data.iter().enumerate() { - if i % bpp >= colored_bytes { - if *byte != 255 { + for (i, &byte) in line.data.iter().enumerate() { + if i as u8 & bpp_mask >= colored_bytes { + if byte != 255 { 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()) { - assert_eq!(sbit_header.len(), bpp_factor); + assert_eq!(sbit_header.len(), channels as usize); sbit_header.pop(); } diff --git a/src/reduction/color.rs b/src/reduction/color.rs index a61de657..432c100d 100644 --- a/src/reduction/color.rs +++ b/src/reduction/color.rs @@ -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 { let mut reduced = Vec::with_capacity(png.raw_data.len()); - let byte_depth: u8 = png.ihdr_data.bit_depth.as_u8() >> 3; - let bpp: usize = 4 * byte_depth as usize; - let colored_bytes = bpp - byte_depth as usize; + let byte_depth = png.ihdr_data.bit_depth.as_u8() >> 3; + let bpp = 4 * byte_depth; + let bpp_mask = bpp - 1; + assert_eq!(0, bpp & bpp_mask); + let colored_bytes = bpp - byte_depth; for line in png.scan_lines() { reduced.push(line.filter); let mut low_bytes = Vec::with_capacity(4); let mut high_bytes = Vec::with_capacity(4); let mut trans_bytes = Vec::with_capacity(byte_depth as usize); 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 { low_bytes.push(*byte); } else { @@ -35,7 +37,7 @@ pub fn reduce_rgba_to_grayscale_alpha(png: &mut PngData) -> bool { trans_bytes.push(*byte); } - if i % bpp == bpp - 1 { + if (i as u8 & bpp_mask) == bpp - 1 { if low_bytes.iter().unique().count() > 1 { return false; }