Fast deinterlace for 8-bit+

This commit is contained in:
Andrew 2022-11-20 16:27:33 +13:00
parent a41d7de348
commit 937a653dd5

View file

@ -95,6 +95,23 @@ pub fn interlace_image(png: &PngImage) -> PngImage {
} }
pub fn deinterlace_image(png: &PngImage) -> PngImage { pub fn deinterlace_image(png: &PngImage) -> PngImage {
PngImage {
data: match png.ihdr.bpp() {
8.. => deinterlace_bytes(png),
_ => deinterlace_bits(png),
},
ihdr: IhdrData {
interlaced: 0,
..png.ihdr
},
aux_headers: png.aux_headers.clone(),
palette: png.palette.clone(),
transparency_pixel: png.transparency_pixel.clone(),
}
}
/// Deinterlace by bits, for images with less than 8bpp
fn deinterlace_bits(png: &PngImage) -> Vec<u8> {
let bits_per_pixel = png.ihdr.bpp(); let bits_per_pixel = png.ihdr.bpp();
let bits_per_line = 8 + bits_per_pixel as usize * png.ihdr.width as usize; let bits_per_line = 8 + bits_per_pixel as usize * png.ihdr.width as usize;
// Initialize each output line with a starting filter byte of 0 // Initialize each output line with a starting filter byte of 0
@ -125,29 +142,9 @@ pub fn deinterlace_image(png: &PngImage) -> PngImage {
// Calculate the next line and move to next pass if necessary // Calculate the next line and move to next pass if necessary
current_y += pass_constants.y_step as usize; current_y += pass_constants.y_step as usize;
if current_y >= png.ihdr.height as usize { if current_y >= png.ihdr.height as usize {
if current_pass == 7 { if !increment_pass(&mut current_pass, png.ihdr) {
break; break;
} }
current_pass += 1;
if current_pass == 2 && png.ihdr.width <= 4 {
current_pass += 1;
}
if current_pass == 3 && png.ihdr.height <= 4 {
current_pass += 1;
}
if current_pass == 4 && png.ihdr.width <= 2 {
current_pass += 1;
}
if current_pass == 5 && png.ihdr.height <= 2 {
current_pass += 1;
}
if current_pass == 6 && png.ihdr.width == 1 {
current_pass += 1;
}
if current_pass == 7 && png.ihdr.height == 1 {
break;
}
pass_constants = interlaced_constants(current_pass); pass_constants = interlaced_constants(current_pass);
current_y = pass_constants.y_shift as usize; current_y = pass_constants.y_shift as usize;
} }
@ -159,16 +156,64 @@ pub fn deinterlace_image(png: &PngImage) -> PngImage {
} }
output.extend_from_slice(line.as_raw_slice()); output.extend_from_slice(line.as_raw_slice());
} }
PngImage { output
data: output, }
ihdr: IhdrData {
interlaced: 0, /// Deinterlace by bytes, for images with at least 8bpp
..png.ihdr fn deinterlace_bytes(png: &PngImage) -> Vec<u8> {
}, let bytes_per_pixel = png.ihdr.bpp() / 8;
aux_headers: png.aux_headers.clone(), let bytes_per_line = 1 + bytes_per_pixel as usize * png.ihdr.width as usize;
palette: png.palette.clone(), // Initialize each output line with a starting filter byte of 0
transparency_pixel: png.transparency_pixel.clone(), // as well as some blank data
let mut lines: Vec<Vec<u8>> = vec![vec![0; bytes_per_line]; png.ihdr.height as usize];
let mut current_pass = 1;
let mut pass_constants = interlaced_constants(current_pass);
let mut current_y: usize = pass_constants.y_shift as usize;
for line in png.scan_lines() {
for (i, byte) in line.data.iter().enumerate() {
let current_x: usize = pass_constants.x_shift as usize
+ (i / bytes_per_pixel as usize) * pass_constants.x_step as usize;
// Copy this byte into the output line, offset by 1 because of filter byte
let index = 1 + (i % bytes_per_pixel as usize) + current_x * bytes_per_pixel as usize;
lines[current_y][index] = *byte;
}
// Calculate the next line and move to next pass if necessary
current_y += pass_constants.y_step as usize;
if current_y >= png.ihdr.height as usize {
if !increment_pass(&mut current_pass, png.ihdr) {
break;
}
pass_constants = interlaced_constants(current_pass);
current_y = pass_constants.y_shift as usize;
}
} }
lines.concat()
}
fn increment_pass(current_pass: &mut u8, ihdr: IhdrData) -> bool {
if *current_pass == 7 {
return false;
}
*current_pass += 1;
if *current_pass == 2 && ihdr.width <= 4 {
*current_pass += 1;
}
if *current_pass == 3 && ihdr.height <= 4 {
*current_pass += 1;
}
if *current_pass == 4 && ihdr.width <= 2 {
*current_pass += 1;
}
if *current_pass == 5 && ihdr.height <= 2 {
*current_pass += 1;
}
if *current_pass == 6 && ihdr.width == 1 {
*current_pass += 1;
}
if *current_pass == 7 && ihdr.height == 1 {
return false;
}
true
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]