From 15a9b4a3e79f50a758f3f0a4f7e9aaf5ae649a21 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 20 Nov 2022 16:27:33 +1300 Subject: [PATCH] Fast deinterlace for 8-bit+ --- src/interlace.rs | 105 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 30 deletions(-) diff --git a/src/interlace.rs b/src/interlace.rs index 33332724..4db6b6f8 100644 --- a/src/interlace.rs +++ b/src/interlace.rs @@ -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 { 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 { + 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![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)]