From bacb7b8b743bbb9dae7e2c643755e81ff531ea8e Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 15 Dec 2022 10:51:34 +1300 Subject: [PATCH] Simplify reductions by not using scan lines --- src/reduction/alpha.rs | 50 +++++------- src/reduction/bit_depth.rs | 60 +++++--------- src/reduction/color.rs | 160 ++++++++++++++++++------------------- src/reduction/mod.rs | 47 +++++------ 4 files changed, 141 insertions(+), 176 deletions(-) diff --git a/src/reduction/alpha.rs b/src/reduction/alpha.rs index e35d737a..cf8c22ad 100644 --- a/src/reduction/alpha.rs +++ b/src/reduction/alpha.rs @@ -16,13 +16,11 @@ pub fn cleaned_alpha_channel(png: &PngImage) -> Option { }; let mut reduced = Vec::with_capacity(png.data.len()); - for line in png.scan_lines(false) { - for pixel in line.data.chunks(bpp) { - if pixel.iter().skip(bpp - bpc).all(|b| *b == 0) { - reduced.resize(reduced.len() + bpp, 0); - } else { - reduced.extend_from_slice(pixel); - } + for pixel in png.data.chunks(bpp) { + if pixel.iter().skip(bpp - bpc).all(|b| *b == 0) { + reduced.resize(reduced.len() + bpp, 0); + } else { + reduced.extend_from_slice(pixel); } } @@ -53,18 +51,16 @@ pub fn reduced_alpha_channel(png: &PngImage, optimize_alpha: bool) -> Option Option { - raw_data.resize(raw_data.len() + colored_bytes, trns[1]); - } - _ => raw_data.extend_from_slice(&pixel[0..colored_bytes]), - }; - } + for pixel in png.data.chunks(bpp) { + match transparency_pixel { + Some(ref trns) if pixel.iter().skip(colored_bytes).all(|b| *b == 0) => { + raw_data.resize(raw_data.len() + colored_bytes, trns[1]); + } + _ => raw_data.extend_from_slice(&pixel[0..colored_bytes]), + }; } let mut aux_headers = png.aux_headers.clone(); diff --git a/src/reduction/bit_depth.rs b/src/reduction/bit_depth.rs index e6069333..003dd433 100644 --- a/src/reduction/bit_depth.rs +++ b/src/reduction/bit_depth.rs @@ -37,29 +37,13 @@ pub fn reduce_bit_depth(png: &PngImage, minimum_bits: usize) -> Option } // Reduce from 16 to 8 bits per channel per pixel - let mut reduced = Vec::with_capacity( - (png.ihdr.width * png.ihdr.height * u32::from(png.channels_per_pixel())) as usize, - ); - let mut high_byte = 0; - - for line in png.scan_lines(false) { - for (i, &byte) in line.data.iter().enumerate() { - if i % 2 == 0 { - // High byte - high_byte = byte; - } else { - // Low byte - if high_byte != byte { - // Can't reduce, exit early - return None; - } - reduced.push(byte); - } - } + if png.data.chunks(2).any(|pair| pair[0] != pair[1]) { + // Can't reduce + return None; } Some(PngImage { - data: reduced, + data: png.data.iter().step_by(2).cloned().collect(), ihdr: IhdrData { bit_depth: BitDepth::Eight, ..png.ihdr @@ -77,8 +61,8 @@ pub fn reduce_bit_depth_8_or_less(png: &PngImage, mut minimum_bits: usize) -> Op if minimum_bits >= bit_depth { return None; } - for line in png.scan_lines(false) { - if png.ihdr.color_type == ColorType::Indexed { + if png.ihdr.color_type == ColorType::Indexed { + for line in png.scan_lines(false) { let line_max = line .data .iter() @@ -105,23 +89,23 @@ pub fn reduce_bit_depth_8_or_less(png: &PngImage, mut minimum_bits: usize) -> Op return None; } } - } else { - for &byte in line.data { - while minimum_bits < bit_depth { - let permutations: &[u8] = if minimum_bits == 1 { - &ONE_BIT_PERMUTATIONS - } else if minimum_bits == 2 { - &TWO_BIT_PERMUTATIONS - } else if minimum_bits == 4 { - &FOUR_BIT_PERMUTATIONS - } else { - return None; - }; - if permutations.iter().any(|perm| *perm == byte) { - break; - } - minimum_bits <<= 1; + } + } else { + for &byte in &png.data { + while minimum_bits < bit_depth { + let permutations: &[u8] = if minimum_bits == 1 { + &ONE_BIT_PERMUTATIONS + } else if minimum_bits == 2 { + &TWO_BIT_PERMUTATIONS + } else if minimum_bits == 4 { + &FOUR_BIT_PERMUTATIONS + } else { + return None; + }; + if permutations.iter().any(|perm| *perm == byte) { + break; } + minimum_bits <<= 1; } } } diff --git a/src/reduction/color.rs b/src/reduction/color.rs index 260d7e9a..acb397b0 100644 --- a/src/reduction/color.rs +++ b/src/reduction/color.rs @@ -19,37 +19,35 @@ pub fn reduce_rgba_to_grayscale_alpha(png: &PngImage) -> Option { return None; } let colored_bytes = bpp - byte_depth; - for line in png.scan_lines(false) { - 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 as u8 & bpp_mask < colored_bytes { - if byte_depth == 1 || i % 2 == 1 { - low_bytes.push(*byte); - } else { - high_bytes.push(*byte); - } + 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 png.data.iter().enumerate() { + if i as u8 & bpp_mask < colored_bytes { + if byte_depth == 1 || i % 2 == 1 { + low_bytes.push(*byte); } else { - trans_bytes.push(*byte); + high_bytes.push(*byte); } + } else { + trans_bytes.push(*byte); + } - if (i as u8 & bpp_mask) == bpp - 1 { - if low_bytes.iter().unique().count() > 1 { + if (i as u8 & bpp_mask) == bpp - 1 { + if low_bytes.iter().unique().count() > 1 { + return None; + } + if byte_depth == 2 { + if high_bytes.iter().unique().count() > 1 { return None; } - if byte_depth == 2 { - if high_bytes.iter().unique().count() > 1 { - return None; - } - reduced.push(high_bytes[0]); - high_bytes.clear(); - } - reduced.push(low_bytes[0]); - low_bytes.clear(); - reduced.extend_from_slice(&trans_bytes); - trans_bytes.clear(); + reduced.push(high_bytes[0]); + high_bytes.clear(); } + reduced.push(low_bytes[0]); + low_bytes.clear(); + reduced.extend_from_slice(&trans_bytes); + trans_bytes.clear(); } } @@ -116,41 +114,39 @@ pub fn reduce_to_palette(png: &PngImage) -> Option { .as_ref() .filter(|t| png.ihdr.color_type == ColorType::RGB && t.len() >= 6) .map(|t| RGB8::new(t[1], t[3], t[5])); - for line in png.scan_lines(false) { - let ok = if png.ihdr.color_type == ColorType::RGB { - reduce_scanline_to_palette( - line.data.as_rgb().iter().cloned().map(|px| { - px.alpha(if Some(px) != transparency_pixel { - 255 - } else { - 0 - }) - }), - &mut palette, - &mut raw_data, - ) - } else if png.ihdr.color_type == ColorType::GrayscaleAlpha { - reduce_scanline_to_palette( - line.data.as_gray_alpha().iter().cloned().map(|px| RGBA { - r: px.0, - g: px.0, - b: px.0, - a: px.1, - }), - &mut palette, - &mut raw_data, - ) - } else { - debug_assert_eq!(png.ihdr.color_type, ColorType::RGBA); - reduce_scanline_to_palette( - line.data.as_rgba().iter().cloned(), - &mut palette, - &mut raw_data, - ) - }; - if !ok { - return None; - } + let ok = if png.ihdr.color_type == ColorType::RGB { + reduce_scanline_to_palette( + png.data.as_rgb().iter().cloned().map(|px| { + px.alpha(if Some(px) != transparency_pixel { + 255 + } else { + 0 + }) + }), + &mut palette, + &mut raw_data, + ) + } else if png.ihdr.color_type == ColorType::GrayscaleAlpha { + reduce_scanline_to_palette( + png.data.as_gray_alpha().iter().cloned().map(|px| RGBA { + r: px.0, + g: px.0, + b: px.0, + a: px.1, + }), + &mut palette, + &mut raw_data, + ) + } else { + debug_assert_eq!(png.ihdr.color_type, ColorType::RGBA); + reduce_scanline_to_palette( + png.data.as_rgba().iter().cloned(), + &mut palette, + &mut raw_data, + ) + }; + if !ok { + return None; } let num_transparent = palette @@ -219,31 +215,29 @@ pub fn reduce_rgb_to_grayscale(png: &PngImage) -> Option { let byte_depth: u8 = png.ihdr.bit_depth.as_u8() >> 3; let bpp: usize = 3 * byte_depth as usize; let mut cur_pixel = Vec::with_capacity(bpp); - for line in png.scan_lines(false) { - for (i, byte) in line.data.iter().enumerate() { - cur_pixel.push(*byte); - if i % bpp == bpp - 1 { - if bpp == 3 { - if cur_pixel.iter().unique().count() > 1 { - return None; - } - reduced.push(cur_pixel[0]); - } else { - let pixel_bytes = cur_pixel - .iter() - .step_by(2) - .cloned() - .zip(cur_pixel.iter().skip(1).step_by(2).cloned()) - .unique() - .collect::>(); - if pixel_bytes.len() > 1 { - return None; - } - reduced.push(pixel_bytes[0].0); - reduced.push(pixel_bytes[0].1); + for (i, byte) in png.data.iter().enumerate() { + cur_pixel.push(*byte); + if i % bpp == bpp - 1 { + if bpp == 3 { + if cur_pixel.iter().unique().count() > 1 { + return None; } - cur_pixel.clear(); + reduced.push(cur_pixel[0]); + } else { + let pixel_bytes = cur_pixel + .iter() + .step_by(2) + .cloned() + .zip(cur_pixel.iter().skip(1).step_by(2).cloned()) + .unique() + .collect::>(); + if pixel_bytes.len() > 1 { + return None; + } + reduced.push(pixel_bytes[0].0); + reduced.push(pixel_bytes[0].1); } + cur_pixel.clear(); } } diff --git a/src/reduction/mod.rs b/src/reduction/mod.rs index 1601b2fc..0d575c6f 100644 --- a/src/reduction/mod.rs +++ b/src/reduction/mod.rs @@ -34,29 +34,27 @@ pub fn reduced_palette(png: &PngImage, optimize_alpha: bool) -> Option let palette = png.palette.as_ref()?; // Find palette entries that are never used - for line in png.scan_lines(false) { - match png.ihdr.bit_depth { - BitDepth::Eight => { - for &byte in line.data { - used[byte as usize] = true; - } + match png.ihdr.bit_depth { + BitDepth::Eight => { + for &byte in &png.data { + used[byte as usize] = true; } - BitDepth::Four => { - for &byte in line.data { - used[(byte & 0x0F) as usize] = true; - used[(byte >> 4) as usize] = true; - } - } - BitDepth::Two => { - for &byte in line.data { - used[(byte & 0x03) as usize] = true; - used[((byte >> 2) & 0x03) as usize] = true; - used[((byte >> 4) & 0x03) as usize] = true; - used[(byte >> 6) as usize] = true; - } - } - _ => unreachable!(), } + BitDepth::Four => { + for &byte in &png.data { + used[(byte & 0x0F) as usize] = true; + used[(byte >> 4) as usize] = true; + } + } + BitDepth::Two => { + for &byte in &png.data { + used[(byte & 0x03) as usize] = true; + used[((byte >> 2) & 0x03) as usize] = true; + used[((byte >> 4) & 0x03) as usize] = true; + used[(byte >> 6) as usize] = true; + } + } + _ => unreachable!(), } let mut used_enumerated: Vec<(usize, &bool)> = used.iter().enumerate().collect(); @@ -117,14 +115,9 @@ pub fn reduced_palette(png: &PngImage, optimize_alpha: bool) -> Option #[must_use] fn do_palette_reduction(png: &PngImage, palette_map: &[Option; 256]) -> Option { let byte_map = palette_map_to_byte_map(png, palette_map)?; - let mut raw_data = Vec::with_capacity(png.data.len()); // Reassign data bytes to new indices - for line in png.scan_lines(false) { - for byte in line.data { - raw_data.push(byte_map[*byte as usize]); - } - } + let raw_data = png.data.iter().map(|b| byte_map[*b as usize]).collect(); let mut aux_headers = png.aux_headers.clone(); if let Some(bkgd_header) = png.aux_headers.get(b"bKGD") {