Improve up and left alpha reductions

This commit is contained in:
Andrew 2022-10-31 13:15:00 +13:00 committed by Josh Holmer
parent 446c788eb3
commit 28ffd174d0

View file

@ -2,7 +2,6 @@ use crate::colors::AlphaOptim;
use crate::colors::ColorType; use crate::colors::ColorType;
use crate::evaluate::Evaluator; use crate::evaluate::Evaluator;
use crate::headers::IhdrData; use crate::headers::IhdrData;
use crate::png::scan_lines::ScanLine;
use crate::png::PngImage; use crate::png::PngImage;
#[cfg(not(feature = "parallel"))] #[cfg(not(feature = "parallel"))]
use crate::rayon::prelude::*; use crate::rayon::prelude::*;
@ -90,50 +89,69 @@ fn reduced_alpha_to_white(png: &PngImage, bpc: usize, bpp: usize) -> Vec<u8> {
} }
fn reduced_alpha_to_up(png: &PngImage, bpc: usize, bpp: usize) -> Vec<u8> { fn reduced_alpha_to_up(png: &PngImage, bpc: usize, bpp: usize) -> Vec<u8> {
let mut scan_lines = png.scan_lines().collect::<Vec<ScanLine<'_>>>(); let mut reduced = Vec::with_capacity(png.data.len());
scan_lines.reverse(); let mut prev_line = Vec::new();
let mut lines = Vec::with_capacity(scan_lines.len()); let mut transparent = Vec::new();
let mut last_line = Vec::new(); for line in png.scan_lines() {
let mut current_line = Vec::with_capacity(scan_lines[0].data.len() + 1); // filter size + pixels if line.data.len() != prev_line.len() {
for line in scan_lines { prev_line = vec![0; line.data.len()];
if line.data.len() != last_line.len() { transparent = vec![0; line.data.len()];
last_line = vec![0; line.data.len()];
} }
current_line.push(line.filter); reduced.push(line.filter);
for (pixel, last_pixel) in line.data.chunks(bpp).zip(last_line.chunks(bpp)) { let line_start = reduced.len();
let mut line_transparent = true;
for (col, (pixel, prev_pixel)) in
line.data.chunks(bpp).zip(prev_line.chunks(bpp)).enumerate()
{
if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 {
current_line.extend_from_slice(&last_pixel[0..(bpp - bpc)]); // Copy the color values from the previous line
current_line.resize(current_line.len() + bpc, 0); reduced.extend_from_slice(&prev_pixel[0..(bpp - bpc)]);
reduced.resize(reduced.len() + bpc, 0);
transparent[col] += 1;
} else { } else {
current_line.extend_from_slice(pixel); if transparent[col] > 0 {
// Copy the current color values upwards in this column
let mut offset = line_start + col * bpp;
for _ in 0..transparent[col] {
offset -= prev_line.len() + 1;
reduced[offset..(offset + bpp - bpc)]
.copy_from_slice(&pixel[..(bpp - bpc)]);
}
transparent[col] = 0;
}
reduced.extend_from_slice(pixel);
line_transparent = false;
} }
} }
last_line = current_line[1..current_line.len()].to_vec(); if line_transparent {
lines.push(current_line.clone()); // Zero out the line if it's fully transparent
current_line.clear(); reduced.truncate(line_start);
reduced.resize(line_start + prev_line.len(), 0);
transparent = vec![0; prev_line.len()];
} }
lines.into_iter().rev().flatten().collect() prev_line = reduced[line_start..].to_vec();
}
reduced
} }
fn reduced_alpha_to_down(png: &PngImage, bpc: usize, bpp: usize) -> Vec<u8> { fn reduced_alpha_to_down(png: &PngImage, bpc: usize, bpp: usize) -> Vec<u8> {
let mut reduced = Vec::with_capacity(png.data.len()); let mut reduced = Vec::with_capacity(png.data.len());
let mut last_line = Vec::new(); let mut prev_line = Vec::new();
let mut pos = 0;
for line in png.scan_lines() { for line in png.scan_lines() {
if line.data.len() != last_line.len() { if line.data.len() != prev_line.len() {
last_line = vec![0; line.data.len()]; prev_line = vec![0; line.data.len()];
} }
reduced.push(line.filter); reduced.push(line.filter);
for (pixel, last_pixel) in line.data.chunks(bpp).zip(last_line.chunks(bpp)) { let line_start = reduced.len();
for (pixel, prev_pixel) in line.data.chunks(bpp).zip(prev_line.chunks(bpp)) {
if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 {
reduced.extend_from_slice(&last_pixel[0..(bpp - bpc)]); reduced.extend_from_slice(&prev_pixel[0..(bpp - bpc)]);
reduced.resize(reduced.len() + bpc, 0); reduced.resize(reduced.len() + bpc, 0);
} else { } else {
reduced.extend_from_slice(pixel); reduced.extend_from_slice(pixel);
} }
} }
last_line = reduced[(pos + 1)..reduced.len()].to_vec(); prev_line = reduced[line_start..].to_vec();
pos = reduced.len();
} }
reduced reduced
} }
@ -141,19 +159,26 @@ fn reduced_alpha_to_down(png: &PngImage, bpc: usize, bpp: usize) -> Vec<u8> {
fn reduced_alpha_to_left(png: &PngImage, bpc: usize, bpp: usize) -> Vec<u8> { fn reduced_alpha_to_left(png: &PngImage, bpc: usize, bpp: usize) -> Vec<u8> {
let mut reduced = Vec::with_capacity(png.data.len()); let mut reduced = Vec::with_capacity(png.data.len());
for line in png.scan_lines() { for line in png.scan_lines() {
let mut line_bytes = Vec::with_capacity(line.data.len());
let mut last_pixel = vec![0; bpp];
for pixel in line.data.chunks(bpp).rev() {
if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 {
line_bytes.extend_from_slice(&last_pixel[0..(bpp - bpc)]);
line_bytes.resize(line_bytes.len() + bpc, 0);
} else {
line_bytes.extend_from_slice(pixel);
last_pixel = pixel.to_owned();
}
}
reduced.push(line.filter); reduced.push(line.filter);
reduced.extend(line_bytes.chunks(bpp).rev().flatten()); let mut prev_pixel = vec![0; bpp];
let mut transparent = 0;
for pixel in line.data.chunks(bpp) {
if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 {
// Count number of consecutive transparent pixel bytes
transparent += bpp;
} else {
prev_pixel[..(bpp - bpc)].copy_from_slice(&pixel[..(bpp - bpc)]);
if transparent > 0 {
// Copy the current color values to preceding transparent pixels
reduced.extend(prev_pixel.iter().cycle().take(transparent));
transparent = 0;
}
reduced.extend_from_slice(pixel);
}
}
if transparent > 0 {
reduced.extend(prev_pixel.iter().cycle().take(transparent));
}
} }
reduced reduced
} }
@ -162,14 +187,14 @@ fn reduced_alpha_to_right(png: &PngImage, bpc: usize, bpp: usize) -> Vec<u8> {
let mut reduced = Vec::with_capacity(png.data.len()); let mut reduced = Vec::with_capacity(png.data.len());
for line in png.scan_lines() { for line in png.scan_lines() {
reduced.push(line.filter); reduced.push(line.filter);
let mut last_pixel = vec![0; bpp]; let mut prev_pixel = vec![0; bpp];
for pixel in line.data.chunks(bpp) { for pixel in line.data.chunks(bpp) {
if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 { if pixel.iter().skip(bpp - bpc).fold(0, |sum, i| sum | i) == 0 {
reduced.extend_from_slice(&last_pixel[0..(bpp - bpc)]); reduced.extend_from_slice(&prev_pixel[0..(bpp - bpc)]);
reduced.resize(reduced.len() + bpc, 0); reduced.resize(reduced.len() + bpc, 0);
} else { } else {
prev_pixel[..(bpp - bpc)].copy_from_slice(&pixel[..(bpp - bpc)]);
reduced.extend_from_slice(pixel); reduced.extend_from_slice(pixel);
last_pixel = pixel.to_owned();
} }
} }
} }