Skip to content

Commit

Permalink
Refactor reduction evaluation sequence
Browse files Browse the repository at this point in the history
  • Loading branch information
andrews05 committed May 5, 2023
1 parent 8d7f289 commit a2a5993
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 134 deletions.
26 changes: 16 additions & 10 deletions benches/reductions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,15 @@ fn reductions_rgba_to_rgb_16(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgba_16_should_be_rgb_16.png"));
let png = PngData::new(&input, false).unwrap();

b.iter(|| reduce_color_type(&png.raw, true, false));
b.iter(|| alpha::reduced_alpha_channel(&png.raw, false));
}

#[bench]
fn reductions_rgba_to_rgb_8(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgba_8_should_be_rgb_8.png"));
let png = PngData::new(&input, false).unwrap();

b.iter(|| reduce_color_type(&png.raw, true, false));
b.iter(|| alpha::reduced_alpha_channel(&png.raw, false));
}

#[bench]
Expand All @@ -158,7 +158,7 @@ fn reductions_rgba_to_grayscale_alpha_16(b: &mut Bencher) {
));
let png = PngData::new(&input, false).unwrap();

b.iter(|| reduce_color_type(&png.raw, true, false));
b.iter(|| color::reduce_rgb_to_grayscale(&png.raw));
}

#[bench]
Expand All @@ -168,7 +168,7 @@ fn reductions_rgba_to_grayscale_alpha_8(b: &mut Bencher) {
));
let png = PngData::new(&input, false).unwrap();

b.iter(|| reduce_color_type(&png.raw, true, false));
b.iter(|| color::reduce_rgb_to_grayscale(&png.raw));
}

#[bench]
Expand All @@ -178,7 +178,10 @@ fn reductions_rgba_to_grayscale_16(b: &mut Bencher) {
));
let png = PngData::new(&input, false).unwrap();

b.iter(|| reduce_color_type(&png.raw, true, false));
b.iter(|| {
color::reduce_rgb_to_grayscale(&png.raw)
.and_then(|r| alpha::reduced_alpha_channel(&r, false))
});
}

#[bench]
Expand All @@ -188,7 +191,10 @@ fn reductions_rgba_to_grayscale_8(b: &mut Bencher) {
));
let png = PngData::new(&input, false).unwrap();

b.iter(|| reduce_color_type(&png.raw, true, false));
b.iter(|| {
color::reduce_rgb_to_grayscale(&png.raw)
.and_then(|r| alpha::reduced_alpha_channel(&r, false))
});
}

#[bench]
Expand All @@ -198,31 +204,31 @@ fn reductions_rgb_to_grayscale_16(b: &mut Bencher) {
));
let png = PngData::new(&input, false).unwrap();

b.iter(|| reduce_color_type(&png.raw, true, false));
b.iter(|| color::reduce_rgb_to_grayscale(&png.raw));
}

#[bench]
fn reductions_rgb_to_grayscale_8(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_grayscale_8.png"));
let png = PngData::new(&input, false).unwrap();

b.iter(|| reduce_color_type(&png.raw, true, false));
b.iter(|| color::reduce_rgb_to_grayscale(&png.raw));
}

#[bench]
fn reductions_rgba_to_palette_8(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgba_8_should_be_palette_8.png"));
let png = PngData::new(&input, false).unwrap();

b.iter(|| reduce_color_type(&png.raw, true, false));
b.iter(|| color::reduce_to_palette(&png.raw));
}

#[bench]
fn reductions_rgb_to_palette_8(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_palette_8.png"));
let png = PngData::new(&input, false).unwrap();

b.iter(|| reduce_color_type(&png.raw, true, false));
b.iter(|| color::reduce_to_palette(&png.raw));
}

#[bench]
Expand Down
9 changes: 9 additions & 0 deletions src/colors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ impl ColorType {
pub fn has_alpha(&self) -> bool {
matches!(self, ColorType::GrayscaleAlpha | ColorType::RGBA)
}

#[inline]
pub fn has_trns(&self) -> bool {
match self {
ColorType::Grayscale { transparent_shade } => transparent_shade.is_some(),
ColorType::RGB { transparent_color } => transparent_color.is_some(),
_ => false,
}
}
}

#[repr(u8)]
Expand Down
91 changes: 4 additions & 87 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ extern crate rayon;
mod rayon;

use crate::atomicmin::AtomicMin;
use crate::colors::{BitDepth, ColorType};
use crate::colors::ColorType;
use crate::deflate::{crc32, inflate};
use crate::evaluate::Evaluator;
use crate::png::PngData;
Expand Down Expand Up @@ -477,23 +477,6 @@ fn optimize_png(
perform_strip(png, opts);
let stripped_png = png.clone();

// Interlacing is not part of the evaluator trials but must be done first to evaluate the rest correctly
let mut reduction_occurred = false;
if let Some(interlacing) = opts.interlace {
if let Some(reduced) = png.raw.change_interlacing(interlacing) {
png.raw = Arc::new(reduced);
reduction_occurred = true;
}
}

// If alpha optimization is enabled, perform a black alpha reduction before evaluating reductions
// This can allow reductions from alpha to indexed which may not have been possible otherwise
if opts.optimize_alpha {
if let Some(reduced) = cleaned_alpha_channel(&png.raw) {
png.raw = Arc::new(reduced);
}
}

// Must use normal (lazy) compression, as faster ones (greedy) are not representative
let eval_compression = 5;
// None and Bigrams work well together, especially for alpha reductions
Expand All @@ -505,7 +488,9 @@ fn optimize_png(
eval_compression,
false,
);
perform_reductions(png.raw.clone(), opts, &deadline, &eval);
let (baseline, mut reduction_occurred) =
perform_reductions(png.raw.clone(), opts, &deadline, &eval);
png.raw = baseline;
let mut eval_filter = if let Some(result) = eval.get_best_candidate() {
*png = result.image;
if result.is_reduction {
Expand Down Expand Up @@ -664,74 +649,6 @@ fn optimize_png(
Ok(output)
}

fn perform_reductions(
mut png: Arc<PngImage>,
opts: &Options,
deadline: &Deadline,
eval: &Evaluator,
) {
// The eval baseline will be set from the original png only if we attempt any reductions
let baseline = png.clone();
let mut reduction_occurred = false;

if opts.palette_reduction {
if let Some(reduced) = reduced_palette(&png, opts.optimize_alpha) {
png = Arc::new(reduced);
eval.try_image(png.clone());
reduction_occurred = true;
}
if deadline.passed() {
return;
}
}

if opts.bit_depth_reduction {
if let Some(reduced) = reduce_bit_depth_16_to_8(&png) {
png = Arc::new(reduced);
eval.try_image(png.clone());
reduction_occurred = true;
}
if deadline.passed() {
return;
}
}

if opts.color_type_reduction {
if let Some(reduced) =
reduce_color_type(&png, opts.grayscale_reduction, opts.optimize_alpha)
{
png = Arc::new(reduced);
eval.try_image(png.clone());
reduction_occurred = true;
}
if deadline.passed() {
return;
}
}

if opts.bit_depth_reduction {
if let Some(reduced) = reduce_bit_depth_8_or_less(&png, 1) {
let previous = png;
png = Arc::new(reduced);
eval.try_image(png.clone());
if png.ihdr.bit_depth < BitDepth::Four && previous.ihdr.bit_depth == BitDepth::Eight {
// Also try 16-color mode for all lower bits images, since that may compress better
if let Some(reduced) = reduce_bit_depth_8_or_less(&previous, 4) {
eval.try_image(Arc::new(reduced));
}
}
reduction_occurred = true;
}
if deadline.passed() {
return;
}
}

if reduction_occurred {
eval.set_baseline(baseline);
}
}

/// Execute a compression trial
fn perform_trial(
filtered: &[u8],
Expand Down
6 changes: 5 additions & 1 deletion src/reduction/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ where

#[must_use]
pub fn reduce_to_palette(png: &PngImage) -> Option<PngImage> {
if png.ihdr.bit_depth != BitDepth::Eight {
if png.ihdr.bit_depth != BitDepth::Eight || png.channels_per_pixel() == 1 {
return None;
}
let mut raw_data = Vec::with_capacity(png.data.len());
Expand Down Expand Up @@ -154,6 +154,10 @@ pub fn reduce_to_palette(png: &PngImage) -> Option<PngImage> {

#[must_use]
pub fn reduce_rgb_to_grayscale(png: &PngImage) -> Option<PngImage> {
if !png.ihdr.color_type.is_rgb() {
return None;
}

let mut reduced = Vec::with_capacity(png.data.len());
let byte_depth = png.bytes_per_channel();
let bpp = png.channels_per_pixel() * byte_depth;
Expand Down
Loading

0 comments on commit a2a5993

Please sign in to comment.