diff --git a/.github/workflows/oxipng.yml b/.github/workflows/oxipng.yml index e653b658..408e1255 100644 --- a/.github/workflows/oxipng.yml +++ b/.github/workflows/oxipng.yml @@ -49,9 +49,9 @@ jobs: - target: i686-pc-windows-msvc os: windows-latest - target: x86_64-apple-darwin - os: macos-13 # x86_64 runner + os: macos-15 - target: aarch64-apple-darwin - os: macos-15 # ARM64 runner + os: macos-15 env: CARGO_BUILD_TARGET: ${{ matrix.target }} diff --git a/src/cli.rs b/src/cli.rs index f10d8af3..21132fe6 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -229,9 +229,9 @@ PNG delta filters (apply the same filter to every line) Heuristic strategies (try to find the best delta filter for each line) 5 => MinSum Minimum sum of absolute differences - 6 => Entropy Highest Shannon entropy + 6 => Entropy Smallest Shannon entropy 7 => Bigrams Lowest count of distinct bigrams - 8 => BigEnt Highest Shannon entropy of bigrams + 8 => BigEnt Smallest Shannon entropy of bigrams 9 => Brute Smallest compressed size (slow) The default value depends on the optimization level preset.") @@ -309,7 +309,7 @@ compressed chunks (such as iCCP) will also be disabled. Note that the combinatio .help("Disable checksum validation") .long_help("\ Do not perform checksum validation of PNG chunks. This may allow some files with errors to \ -be processed successfully.") +be processed successfully. The output will always have correct checksums.") .long("fix") .action(ArgAction::SetTrue), ) diff --git a/tests/alpha.rs b/tests/alpha.rs new file mode 100644 index 00000000..f86174f9 --- /dev/null +++ b/tests/alpha.rs @@ -0,0 +1,346 @@ +use std::{ + fs::remove_file, + path::{Path, PathBuf}, +}; + +use oxipng::{internal_tests::*, *}; + +const GRAYSCALE_ALPHA: u8 = 4; +const RGBA: u8 = 6; + +fn get_opts(input: &Path) -> (OutFile, oxipng::Options) { + let options = oxipng::Options { + force: true, + optimize_alpha: true, + ..Default::default() + }; + (OutFile::from_path(input.with_extension("out.png")), options) +} + +fn test_it_converts( + input: &str, + filter: FilterStrategy, + color_type_in: u8, + bit_depth_in: BitDepth, + color_type_out: u8, + bit_depth_out: BitDepth, +) { + let input = PathBuf::from(input); + + let (output, mut opts) = get_opts(&input); + let png = PngData::new(&input, &opts).unwrap(); + opts.filters = indexset! {filter}; + assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_in); + assert_eq!(png.raw.ihdr.bit_depth, bit_depth_in); + + match oxipng::optimize(&InFile::Path(input), &output, &opts) { + Ok(_) => (), + Err(x) => panic!("{}", x), + }; + let output = output.path().unwrap(); + assert!(output.exists()); + + let png = match PngData::new(output, &opts) { + Ok(x) => x, + Err(x) => { + remove_file(output).ok(); + panic!("{}", x) + } + }; + + assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_out); + assert_eq!(png.raw.ihdr.bit_depth, bit_depth_out); + if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type { + assert!(palette.len() <= 1 << (png.raw.ihdr.bit_depth as u8)); + } + + remove_file(output).ok(); +} + +#[test] +fn alpha_filter_0_for_rgba_16() { + test_it_converts( + "tests/files/filter_0_for_rgba_16.png", + FilterStrategy::NONE, + RGBA, + BitDepth::Sixteen, + RGBA, + BitDepth::Sixteen, + ); +} + +#[test] +fn alpha_filter_1_for_rgba_16() { + test_it_converts( + "tests/files/filter_1_for_rgba_16.png", + FilterStrategy::SUB, + RGBA, + BitDepth::Sixteen, + RGBA, + BitDepth::Sixteen, + ); +} + +#[test] +fn alpha_filter_2_for_rgba_16() { + test_it_converts( + "tests/files/filter_2_for_rgba_16.png", + FilterStrategy::UP, + RGBA, + BitDepth::Sixteen, + RGBA, + BitDepth::Sixteen, + ); +} + +#[test] +fn alpha_filter_3_for_rgba_16() { + test_it_converts( + "tests/files/filter_3_for_rgba_16.png", + FilterStrategy::AVERAGE, + RGBA, + BitDepth::Sixteen, + RGBA, + BitDepth::Sixteen, + ); +} + +#[test] +fn alpha_filter_4_for_rgba_16() { + test_it_converts( + "tests/files/filter_4_for_rgba_16.png", + FilterStrategy::PAETH, + RGBA, + BitDepth::Sixteen, + RGBA, + BitDepth::Sixteen, + ); +} + +#[test] +fn alpha_filter_5_for_rgba_16() { + test_it_converts( + "tests/files/filter_5_for_rgba_16.png", + FilterStrategy::MinSum, + RGBA, + BitDepth::Sixteen, + RGBA, + BitDepth::Sixteen, + ); +} + +#[test] +fn alpha_filter_0_for_rgba_8() { + test_it_converts( + "tests/files/filter_0_for_rgba_8.png", + FilterStrategy::NONE, + RGBA, + BitDepth::Eight, + RGBA, + BitDepth::Eight, + ); +} + +#[test] +fn alpha_filter_1_for_rgba_8() { + test_it_converts( + "tests/files/filter_1_for_rgba_8.png", + FilterStrategy::SUB, + RGBA, + BitDepth::Eight, + RGBA, + BitDepth::Eight, + ); +} + +#[test] +fn alpha_filter_2_for_rgba_8() { + test_it_converts( + "tests/files/filter_2_for_rgba_8.png", + FilterStrategy::UP, + RGBA, + BitDepth::Eight, + RGBA, + BitDepth::Eight, + ); +} + +#[test] +fn alpha_filter_3_for_rgba_8() { + test_it_converts( + "tests/files/filter_3_for_rgba_8.png", + FilterStrategy::AVERAGE, + RGBA, + BitDepth::Eight, + RGBA, + BitDepth::Eight, + ); +} + +#[test] +fn alpha_filter_4_for_rgba_8() { + test_it_converts( + "tests/files/filter_4_for_rgba_8.png", + FilterStrategy::PAETH, + RGBA, + BitDepth::Eight, + RGBA, + BitDepth::Eight, + ); +} + +#[test] +fn alpha_filter_5_for_rgba_8() { + test_it_converts( + "tests/files/filter_5_for_rgba_8.png", + FilterStrategy::MinSum, + RGBA, + BitDepth::Eight, + RGBA, + BitDepth::Eight, + ); +} + +#[test] +fn alpha_filter_0_for_grayscale_alpha_16() { + test_it_converts( + "tests/files/filter_0_for_grayscale_alpha_16.png", + FilterStrategy::NONE, + GRAYSCALE_ALPHA, + BitDepth::Sixteen, + GRAYSCALE_ALPHA, + BitDepth::Sixteen, + ); +} + +#[test] +fn alpha_filter_1_for_grayscale_alpha_16() { + test_it_converts( + "tests/files/filter_1_for_grayscale_alpha_16.png", + FilterStrategy::SUB, + GRAYSCALE_ALPHA, + BitDepth::Sixteen, + GRAYSCALE_ALPHA, + BitDepth::Sixteen, + ); +} + +#[test] +fn alpha_filter_2_for_grayscale_alpha_16() { + test_it_converts( + "tests/files/filter_2_for_grayscale_alpha_16.png", + FilterStrategy::UP, + GRAYSCALE_ALPHA, + BitDepth::Sixteen, + GRAYSCALE_ALPHA, + BitDepth::Sixteen, + ); +} + +#[test] +fn alpha_filter_3_for_grayscale_alpha_16() { + test_it_converts( + "tests/files/filter_3_for_grayscale_alpha_16.png", + FilterStrategy::AVERAGE, + GRAYSCALE_ALPHA, + BitDepth::Sixteen, + GRAYSCALE_ALPHA, + BitDepth::Sixteen, + ); +} + +#[test] +fn alpha_filter_4_for_grayscale_alpha_16() { + test_it_converts( + "tests/files/filter_4_for_grayscale_alpha_16.png", + FilterStrategy::PAETH, + GRAYSCALE_ALPHA, + BitDepth::Sixteen, + GRAYSCALE_ALPHA, + BitDepth::Sixteen, + ); +} + +#[test] +fn alpha_filter_5_for_grayscale_alpha_16() { + test_it_converts( + "tests/files/filter_5_for_grayscale_alpha_16.png", + FilterStrategy::MinSum, + GRAYSCALE_ALPHA, + BitDepth::Sixteen, + GRAYSCALE_ALPHA, + BitDepth::Sixteen, + ); +} + +#[test] +fn alpha_filter_0_for_grayscale_alpha_8() { + test_it_converts( + "tests/files/filter_0_for_grayscale_alpha_8.png", + FilterStrategy::NONE, + GRAYSCALE_ALPHA, + BitDepth::Eight, + GRAYSCALE_ALPHA, + BitDepth::Eight, + ); +} + +#[test] +fn alpha_filter_1_for_grayscale_alpha_8() { + test_it_converts( + "tests/files/filter_1_for_grayscale_alpha_8.png", + FilterStrategy::SUB, + GRAYSCALE_ALPHA, + BitDepth::Eight, + GRAYSCALE_ALPHA, + BitDepth::Eight, + ); +} + +#[test] +fn alpha_filter_2_for_grayscale_alpha_8() { + test_it_converts( + "tests/files/filter_2_for_grayscale_alpha_8.png", + FilterStrategy::UP, + GRAYSCALE_ALPHA, + BitDepth::Eight, + GRAYSCALE_ALPHA, + BitDepth::Eight, + ); +} + +#[test] +fn alpha_filter_3_for_grayscale_alpha_8() { + test_it_converts( + "tests/files/filter_3_for_grayscale_alpha_8.png", + FilterStrategy::AVERAGE, + GRAYSCALE_ALPHA, + BitDepth::Eight, + GRAYSCALE_ALPHA, + BitDepth::Eight, + ); +} + +#[test] +fn alpha_filter_4_for_grayscale_alpha_8() { + test_it_converts( + "tests/files/filter_4_for_grayscale_alpha_8.png", + FilterStrategy::PAETH, + GRAYSCALE_ALPHA, + BitDepth::Eight, + GRAYSCALE_ALPHA, + BitDepth::Eight, + ); +} + +#[test] +fn alpha_filter_5_for_grayscale_alpha_8() { + test_it_converts( + "tests/files/filter_5_for_grayscale_alpha_8.png", + FilterStrategy::MinSum, + GRAYSCALE_ALPHA, + BitDepth::Eight, + GRAYSCALE_ALPHA, + BitDepth::Eight, + ); +} diff --git a/tests/files/filter_0_for_grayscale_alpha_16.png b/tests/files/filter_0_for_grayscale_alpha_16.png index e5dabbf5..92fc0852 100644 Binary files a/tests/files/filter_0_for_grayscale_alpha_16.png and b/tests/files/filter_0_for_grayscale_alpha_16.png differ diff --git a/tests/files/filter_0_for_grayscale_alpha_8.png b/tests/files/filter_0_for_grayscale_alpha_8.png index 2c094a33..ad4cdf80 100644 Binary files a/tests/files/filter_0_for_grayscale_alpha_8.png and b/tests/files/filter_0_for_grayscale_alpha_8.png differ diff --git a/tests/files/filter_0_for_rgba_16.png b/tests/files/filter_0_for_rgba_16.png index 656ee55c..5bd9e12b 100644 Binary files a/tests/files/filter_0_for_rgba_16.png and b/tests/files/filter_0_for_rgba_16.png differ diff --git a/tests/files/filter_0_for_rgba_8.png b/tests/files/filter_0_for_rgba_8.png index a3e6e8c4..e6a303c7 100644 Binary files a/tests/files/filter_0_for_rgba_8.png and b/tests/files/filter_0_for_rgba_8.png differ diff --git a/tests/files/filter_1_for_grayscale_alpha_16.png b/tests/files/filter_1_for_grayscale_alpha_16.png index e5dabbf5..92fc0852 100644 Binary files a/tests/files/filter_1_for_grayscale_alpha_16.png and b/tests/files/filter_1_for_grayscale_alpha_16.png differ diff --git a/tests/files/filter_1_for_grayscale_alpha_8.png b/tests/files/filter_1_for_grayscale_alpha_8.png index 2c094a33..ad4cdf80 100644 Binary files a/tests/files/filter_1_for_grayscale_alpha_8.png and b/tests/files/filter_1_for_grayscale_alpha_8.png differ diff --git a/tests/files/filter_1_for_rgba_16.png b/tests/files/filter_1_for_rgba_16.png index 656ee55c..5bd9e12b 100644 Binary files a/tests/files/filter_1_for_rgba_16.png and b/tests/files/filter_1_for_rgba_16.png differ diff --git a/tests/files/filter_1_for_rgba_8.png b/tests/files/filter_1_for_rgba_8.png index a3e6e8c4..e6a303c7 100644 Binary files a/tests/files/filter_1_for_rgba_8.png and b/tests/files/filter_1_for_rgba_8.png differ diff --git a/tests/files/filter_2_for_grayscale_alpha_16.png b/tests/files/filter_2_for_grayscale_alpha_16.png index e5dabbf5..92fc0852 100644 Binary files a/tests/files/filter_2_for_grayscale_alpha_16.png and b/tests/files/filter_2_for_grayscale_alpha_16.png differ diff --git a/tests/files/filter_2_for_grayscale_alpha_8.png b/tests/files/filter_2_for_grayscale_alpha_8.png index 2c094a33..ad4cdf80 100644 Binary files a/tests/files/filter_2_for_grayscale_alpha_8.png and b/tests/files/filter_2_for_grayscale_alpha_8.png differ diff --git a/tests/files/filter_2_for_rgba_16.png b/tests/files/filter_2_for_rgba_16.png index 656ee55c..5bd9e12b 100644 Binary files a/tests/files/filter_2_for_rgba_16.png and b/tests/files/filter_2_for_rgba_16.png differ diff --git a/tests/files/filter_2_for_rgba_8.png b/tests/files/filter_2_for_rgba_8.png index a3e6e8c4..e6a303c7 100644 Binary files a/tests/files/filter_2_for_rgba_8.png and b/tests/files/filter_2_for_rgba_8.png differ diff --git a/tests/files/filter_3_for_grayscale_alpha_16.png b/tests/files/filter_3_for_grayscale_alpha_16.png index e5dabbf5..92fc0852 100644 Binary files a/tests/files/filter_3_for_grayscale_alpha_16.png and b/tests/files/filter_3_for_grayscale_alpha_16.png differ diff --git a/tests/files/filter_3_for_grayscale_alpha_8.png b/tests/files/filter_3_for_grayscale_alpha_8.png index 2c094a33..ad4cdf80 100644 Binary files a/tests/files/filter_3_for_grayscale_alpha_8.png and b/tests/files/filter_3_for_grayscale_alpha_8.png differ diff --git a/tests/files/filter_3_for_rgba_16.png b/tests/files/filter_3_for_rgba_16.png index 656ee55c..5bd9e12b 100644 Binary files a/tests/files/filter_3_for_rgba_16.png and b/tests/files/filter_3_for_rgba_16.png differ diff --git a/tests/files/filter_3_for_rgba_8.png b/tests/files/filter_3_for_rgba_8.png index a3e6e8c4..e6a303c7 100644 Binary files a/tests/files/filter_3_for_rgba_8.png and b/tests/files/filter_3_for_rgba_8.png differ diff --git a/tests/files/filter_4_for_grayscale_alpha_16.png b/tests/files/filter_4_for_grayscale_alpha_16.png index e5dabbf5..92fc0852 100644 Binary files a/tests/files/filter_4_for_grayscale_alpha_16.png and b/tests/files/filter_4_for_grayscale_alpha_16.png differ diff --git a/tests/files/filter_4_for_grayscale_alpha_8.png b/tests/files/filter_4_for_grayscale_alpha_8.png index 2c094a33..ad4cdf80 100644 Binary files a/tests/files/filter_4_for_grayscale_alpha_8.png and b/tests/files/filter_4_for_grayscale_alpha_8.png differ diff --git a/tests/files/filter_4_for_rgba_16.png b/tests/files/filter_4_for_rgba_16.png index 656ee55c..5bd9e12b 100644 Binary files a/tests/files/filter_4_for_rgba_16.png and b/tests/files/filter_4_for_rgba_16.png differ diff --git a/tests/files/filter_4_for_rgba_8.png b/tests/files/filter_4_for_rgba_8.png index a3e6e8c4..e6a303c7 100644 Binary files a/tests/files/filter_4_for_rgba_8.png and b/tests/files/filter_4_for_rgba_8.png differ diff --git a/tests/files/filter_5_for_grayscale_alpha_16.png b/tests/files/filter_5_for_grayscale_alpha_16.png index e5dabbf5..92fc0852 100644 Binary files a/tests/files/filter_5_for_grayscale_alpha_16.png and b/tests/files/filter_5_for_grayscale_alpha_16.png differ diff --git a/tests/files/filter_5_for_grayscale_alpha_8.png b/tests/files/filter_5_for_grayscale_alpha_8.png index 2c094a33..ad4cdf80 100644 Binary files a/tests/files/filter_5_for_grayscale_alpha_8.png and b/tests/files/filter_5_for_grayscale_alpha_8.png differ diff --git a/tests/files/filter_5_for_rgba_16.png b/tests/files/filter_5_for_rgba_16.png index 656ee55c..5bd9e12b 100644 Binary files a/tests/files/filter_5_for_rgba_16.png and b/tests/files/filter_5_for_rgba_16.png differ diff --git a/tests/files/filter_5_for_rgba_8.png b/tests/files/filter_5_for_rgba_8.png index a3e6e8c4..e6a303c7 100644 Binary files a/tests/files/filter_5_for_rgba_8.png and b/tests/files/filter_5_for_rgba_8.png differ