Fast deinterlace for 8-bit+

This commit is contained in:
Andrew 2022-11-20 16:27:33 +13:00 committed by Josh Holmer
parent a41d7de348
commit 15a9b4a3e7

View file

@ -95,6 +95,23 @@ pub fn interlace_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_line = 8 + bits_per_pixel as usize * png.ihdr.width as usize;
// 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
current_y += pass_constants.y_step as usize;
if current_y >= png.ihdr.height as usize {
if current_pass == 7 {
if !increment_pass(&mut current_pass, png.ihdr) {
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);
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());
}
PngImage {
data: output,
ihdr: IhdrData {
interlaced: 0,
..png.ihdr
},
aux_headers: png.aux_headers.clone(),
palette: png.palette.clone(),
transparency_pixel: png.transparency_pixel.clone(),
output
}
/// Deinterlace by bytes, for images with at least 8bpp
fn deinterlace_bytes(png: &PngImage) -> Vec<u8> {
let bytes_per_pixel = png.ihdr.bpp() / 8;
let bytes_per_line = 1 + bytes_per_pixel as usize * png.ihdr.width as usize;
// Initialize each output line with a starting filter byte of 0
// 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)]