This commit is contained in:
Kornel 2024-12-31 15:47:18 +00:00 committed by andrews05
parent 8270c6a034
commit a72bc945fc
18 changed files with 148 additions and 208 deletions

View file

@ -5,7 +5,8 @@ extern crate test;
use std::path::PathBuf;
use oxipng::{internal_tests::*, *};
use oxipng::internal_tests::*;
use oxipng::*;
use test::Bencher;
#[bench]
@ -13,9 +14,7 @@ fn filters_16_bits_filter_0(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::None, false);
});
b.iter(|| png.raw.filter_image(RowFilter::None, false));
}
#[bench]
@ -23,9 +22,7 @@ fn filters_8_bits_filter_0(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::None, false);
});
b.iter(|| png.raw.filter_image(RowFilter::None, false));
}
#[bench]
@ -35,9 +32,7 @@ fn filters_4_bits_filter_0(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::None, false);
});
b.iter(|| png.raw.filter_image(RowFilter::None, false));
}
#[bench]
@ -47,9 +42,7 @@ fn filters_2_bits_filter_0(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::None, false);
});
b.iter(|| png.raw.filter_image(RowFilter::None, false));
}
#[bench]
@ -59,9 +52,7 @@ fn filters_1_bits_filter_0(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::None, false);
});
b.iter(|| png.raw.filter_image(RowFilter::None, false));
}
#[bench]
@ -69,9 +60,7 @@ fn filters_16_bits_filter_1(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Sub, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Sub, false));
}
#[bench]
@ -79,9 +68,7 @@ fn filters_8_bits_filter_1(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Sub, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Sub, false));
}
#[bench]
@ -91,9 +78,7 @@ fn filters_4_bits_filter_1(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Sub, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Sub, false));
}
#[bench]
@ -103,9 +88,7 @@ fn filters_2_bits_filter_1(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Sub, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Sub, false));
}
#[bench]
@ -115,9 +98,7 @@ fn filters_1_bits_filter_1(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Sub, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Sub, false));
}
#[bench]
@ -125,9 +106,7 @@ fn filters_16_bits_filter_2(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Up, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Up, false));
}
#[bench]
@ -135,9 +114,7 @@ fn filters_8_bits_filter_2(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Up, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Up, false));
}
#[bench]
@ -147,9 +124,7 @@ fn filters_4_bits_filter_2(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Up, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Up, false));
}
#[bench]
@ -159,9 +134,7 @@ fn filters_2_bits_filter_2(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Up, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Up, false));
}
#[bench]
@ -171,9 +144,7 @@ fn filters_1_bits_filter_2(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Up, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Up, false));
}
#[bench]
@ -181,9 +152,7 @@ fn filters_16_bits_filter_3(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Average, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Average, false));
}
#[bench]
@ -191,9 +160,7 @@ fn filters_8_bits_filter_3(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Average, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Average, false));
}
#[bench]
@ -203,9 +170,7 @@ fn filters_4_bits_filter_3(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Average, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Average, false));
}
#[bench]
@ -215,9 +180,7 @@ fn filters_2_bits_filter_3(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Average, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Average, false));
}
#[bench]
@ -227,9 +190,7 @@ fn filters_1_bits_filter_3(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Average, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Average, false));
}
#[bench]
@ -237,9 +198,7 @@ fn filters_16_bits_filter_4(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Paeth, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Paeth, false));
}
#[bench]
@ -247,9 +206,7 @@ fn filters_8_bits_filter_4(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Paeth, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Paeth, false));
}
#[bench]
@ -259,9 +216,7 @@ fn filters_4_bits_filter_4(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Paeth, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Paeth, false));
}
#[bench]
@ -271,9 +226,7 @@ fn filters_2_bits_filter_4(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Paeth, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Paeth, false));
}
#[bench]
@ -283,9 +236,7 @@ fn filters_1_bits_filter_4(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Paeth, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Paeth, false));
}
#[bench]
@ -293,9 +244,7 @@ fn filters_16_bits_filter_5(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::MinSum, false);
});
b.iter(|| png.raw.filter_image(RowFilter::MinSum, false));
}
#[bench]
@ -303,9 +252,7 @@ fn filters_8_bits_filter_5(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::MinSum, false);
});
b.iter(|| png.raw.filter_image(RowFilter::MinSum, false));
}
#[bench]
@ -315,9 +262,7 @@ fn filters_4_bits_filter_5(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::MinSum, false);
});
b.iter(|| png.raw.filter_image(RowFilter::MinSum, false));
}
#[bench]
@ -327,9 +272,7 @@ fn filters_2_bits_filter_5(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::MinSum, false);
});
b.iter(|| png.raw.filter_image(RowFilter::MinSum, false));
}
#[bench]
@ -339,7 +282,5 @@ fn filters_1_bits_filter_5(b: &mut Bencher) {
));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::MinSum, false);
});
b.iter(|| png.raw.filter_image(RowFilter::MinSum, false));
}

View file

@ -13,9 +13,7 @@ fn filters_minsum(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::MinSum, false);
});
b.iter(|| png.raw.filter_image(RowFilter::MinSum, false));
}
#[bench]
@ -23,9 +21,7 @@ fn filters_entropy(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Entropy, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Entropy, false));
}
#[bench]
@ -33,9 +29,7 @@ fn filters_bigrams(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Bigrams, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Bigrams, false));
}
#[bench]
@ -43,9 +37,7 @@ fn filters_bigent(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::BigEnt, false);
});
b.iter(|| png.raw.filter_image(RowFilter::BigEnt, false));
}
#[bench]
@ -53,7 +45,5 @@ fn filters_brute(b: &mut Bencher) {
let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
let png = PngData::new(&input, &Options::default()).unwrap();
b.iter(|| {
png.raw.filter_image(RowFilter::Brute, false);
});
b.iter(|| png.raw.filter_image(RowFilter::Brute, false));
}

View file

@ -6,6 +6,7 @@ pub struct AtomicMin {
}
impl AtomicMin {
#[must_use]
pub fn new(init: Option<usize>) -> Self {
Self {
val: AtomicUsize::new(init.unwrap_or(usize::MAX)),
@ -21,8 +22,8 @@ impl AtomicMin {
}
}
/// Unset value is usize_max
pub fn as_atomic_usize(&self) -> &AtomicUsize {
/// Unset value is `usize_max`
pub const fn as_atomic_usize(&self) -> &AtomicUsize {
&self.val
}

View file

@ -32,13 +32,13 @@ impl Display for ColorType {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ColorType::Grayscale { .. } => Display::fmt("Grayscale", f),
ColorType::RGB { .. } => Display::fmt("RGB", f),
ColorType::Indexed { palette } => {
Self::Grayscale { .. } => Display::fmt("Grayscale", f),
Self::RGB { .. } => Display::fmt("RGB", f),
Self::Indexed { palette } => {
Display::fmt(&format!("Indexed ({} colors)", palette.len()), f)
}
ColorType::GrayscaleAlpha => Display::fmt("Grayscale + Alpha", f),
ColorType::RGBA => Display::fmt("RGB + Alpha", f),
Self::GrayscaleAlpha => Display::fmt("Grayscale + Alpha", f),
Self::RGBA => Display::fmt("RGB + Alpha", f),
}
}
}
@ -46,49 +46,47 @@ impl Display for ColorType {
impl ColorType {
/// Get the code used by the PNG specification to denote this color type
#[inline]
pub fn png_header_code(&self) -> u8 {
#[must_use]
pub const fn png_header_code(&self) -> u8 {
match self {
ColorType::Grayscale { .. } => 0,
ColorType::RGB { .. } => 2,
ColorType::Indexed { .. } => 3,
ColorType::GrayscaleAlpha => 4,
ColorType::RGBA => 6,
Self::Grayscale { .. } => 0,
Self::RGB { .. } => 2,
Self::Indexed { .. } => 3,
Self::GrayscaleAlpha => 4,
Self::RGBA => 6,
}
}
#[inline]
pub(crate) fn channels_per_pixel(&self) -> u8 {
pub(crate) const fn channels_per_pixel(&self) -> u8 {
match self {
ColorType::Grayscale { .. } | ColorType::Indexed { .. } => 1,
ColorType::GrayscaleAlpha => 2,
ColorType::RGB { .. } => 3,
ColorType::RGBA => 4,
Self::Grayscale { .. } | Self::Indexed { .. } => 1,
Self::GrayscaleAlpha => 2,
Self::RGB { .. } => 3,
Self::RGBA => 4,
}
}
#[inline]
pub(crate) fn is_rgb(&self) -> bool {
matches!(self, ColorType::RGB { .. } | ColorType::RGBA)
pub(crate) const fn is_rgb(&self) -> bool {
matches!(self, Self::RGB { .. } | Self::RGBA)
}
#[inline]
pub(crate) fn is_gray(&self) -> bool {
matches!(
self,
ColorType::Grayscale { .. } | ColorType::GrayscaleAlpha
)
pub(crate) const fn is_gray(&self) -> bool {
matches!(self, Self::Grayscale { .. } | Self::GrayscaleAlpha)
}
#[inline]
pub(crate) fn has_alpha(&self) -> bool {
matches!(self, ColorType::GrayscaleAlpha | ColorType::RGBA)
pub(crate) const fn has_alpha(&self) -> bool {
matches!(self, Self::GrayscaleAlpha | Self::RGBA)
}
#[inline]
pub(crate) fn has_trns(&self) -> bool {
pub(crate) const fn has_trns(&self) -> bool {
match self {
ColorType::Grayscale { transparent_shade } => transparent_shade.is_some(),
ColorType::RGB { transparent_color } => transparent_color.is_some(),
Self::Grayscale { transparent_shade } => transparent_shade.is_some(),
Self::RGB { transparent_color } => transparent_color.is_some(),
_ => false,
}
}

View file

@ -30,6 +30,7 @@ pub fn inflate(data: &[u8], out_size: usize) -> PngResult<Vec<u8>> {
Ok(dest)
}
#[must_use]
pub fn crc32(data: &[u8]) -> u32 {
let mut crc = Crc::new();
crc.update(data);

View file

@ -33,14 +33,13 @@ impl fmt::Display for PngError {
f.write_str("Missing data in the file; the file is truncated")
}
PngError::APNGNotSupported => f.write_str("APNG files are not (yet) supported"),
PngError::ChunkMissing(s) => write!(f, "Chunk {} missing or empty", s),
PngError::ChunkMissing(s) => write!(f, "Chunk {s} missing or empty"),
PngError::InvalidDepthForType(d, ref c) => {
write!(f, "Invalid bit depth {} for color type {}", d, c)
write!(f, "Invalid bit depth {d} for color type {c}")
}
PngError::IncorrectDataLength(l1, l2) => write!(
f,
"Data length {} does not match the expected length {}",
l1, l2
"Data length {l1} does not match the expected length {l2}"
),
PngError::C2PAMetadataPreventsChanges => f.write_str(
"The image contains C2PA manifest that would be invalidated by any file changes",
@ -52,6 +51,7 @@ impl fmt::Display for PngError {
impl PngError {
#[cold]
#[must_use]
pub fn new(description: &str) -> PngError {
PngError::Other(description.into())
}

View file

@ -18,7 +18,7 @@ use rayon::prelude::*;
use crate::rayon;
use crate::{atomicmin::AtomicMin, deflate, filters::RowFilter, png::PngImage, Deadline, PngError};
pub struct Candidate {
pub(crate) struct Candidate {
pub image: Arc<PngImage>,
pub idat_data: Vec<u8>,
pub filtered: Vec<u8>,

View file

@ -30,7 +30,7 @@ impl IhdrData {
/// Bits per pixel
#[must_use]
#[inline]
pub fn bpp(&self) -> usize {
pub const fn bpp(&self) -> usize {
self.bit_depth as usize * self.color_type.channels_per_pixel() as usize
}
@ -91,11 +91,11 @@ pub enum StripChunks {
impl StripChunks {
pub(crate) fn keep(&self, name: &[u8; 4]) -> bool {
match &self {
StripChunks::None => true,
StripChunks::Keep(names) => names.contains(name),
StripChunks::Strip(names) => !names.contains(name),
StripChunks::Safe => DISPLAY_CHUNKS.contains(name),
StripChunks::All => false,
Self::None => true,
Self::Keep(names) => names.contains(name),
Self::Strip(names) => !names.contains(name),
Self::Safe => DISPLAY_CHUNKS.contains(name),
Self::All => false,
}
}
}

View file

@ -195,8 +195,7 @@ pub fn optimize(input: &InFile, output: &OutFile, opts: &Options) -> PngResult<(
.map_err(|err| {
// Fail if metadata cannot be preserved
PngError::new(&format!(
"Unable to read metadata from input file {:?}: {}",
input_path, err
"Unable to read metadata from input file {input_path:?}: {err}"
))
})
.map(Some)?;
@ -211,7 +210,7 @@ pub fn optimize(input: &InFile, output: &OutFile, opts: &Options) -> PngResult<(
let mut data = Vec::new();
stdin()
.read_to_end(&mut data)
.map_err(|e| PngError::new(&format!("Error reading stdin: {}", e)))?;
.map_err(|e| PngError::new(&format!("Error reading stdin: {e}")))?;
data
}
};
@ -260,7 +259,7 @@ pub fn optimize(input: &InFile, output: &OutFile, opts: &Options) -> PngResult<(
let mut buffer = BufWriter::new(stdout());
buffer
.write_all(&optimized_output)
.map_err(|e| PngError::new(&format!("Unable to write to stdout: {}", e)))?;
.map_err(|e| PngError::new(&format!("Unable to write to stdout: {e}")))?;
}
(OutFile::Path { path, .. }, _) => {
let output_path = path
@ -460,7 +459,7 @@ fn optimize_raw(
if eval_result.is_some() {
// Some filters have already been evaluated, we don't need to try them again
filters = filters.difference(&eval_filters).cloned().collect();
filters = filters.difference(&eval_filters).copied().collect();
}
if !filters.is_empty() {
@ -609,6 +608,7 @@ pub struct Deadline {
}
impl Deadline {
#[must_use]
pub fn new(timeout: Option<Duration>) -> Self {
Self {
imp: timeout.map(|timeout| DeadlineImp {
@ -742,7 +742,7 @@ fn postprocess_chunks(
c.data.truncate(4);
c.data.append(&mut data);
}
})
});
}
}
@ -756,8 +756,7 @@ fn copy_permissions(metadata_input: &Metadata, out_file: &File) -> PngResult<()>
.set_permissions(metadata_input.permissions())
.map_err(|err_io| {
PngError::new(&format!(
"unable to set permissions for output file: {}",
err_io
"unable to set permissions for output file: {err_io}"
))
})
}
@ -778,8 +777,7 @@ fn copy_times(input_path_meta: &Metadata, out_path: &Path) -> PngResult<()> {
);
filetime::set_file_times(out_path, atime, mtime).map_err(|err_io| {
PngError::new(&format!(
"unable to set file times on {:?}: {}",
out_path, err_io
"unable to set file times on {out_path:?}: {err_io}"
))
})
}

View file

@ -228,8 +228,8 @@ fn parse_opts_into_struct(
let out_dir = if let Some(path) = matches.get_one::<PathBuf>("output_dir") {
if !path.exists() {
match DirBuilder::new().recursive(true).create(path) {
Ok(_) => (),
Err(x) => return Err(format!("Could not create output directory {}", x)),
Ok(()) => (),
Err(x) => return Err(format!("Could not create output directory {x}")),
};
} else if !path.is_dir() {
return Err(format!(
@ -305,9 +305,9 @@ fn parse_opts_into_struct(
})
.collect::<Result<IndexSet<_>, _>>()?;
if keep_display {
names.extend(DISPLAY_CHUNKS.iter().cloned());
names.extend(DISPLAY_CHUNKS.iter().copied());
}
opts.strip = StripChunks::Keep(names)
opts.strip = StripChunks::Keep(names);
}
if let Some(strip) = matches.get_one::<String>("strip") {
@ -329,7 +329,7 @@ fn parse_opts_into_struct(
}
let name = parse_chunk_name(x)?;
if FORBIDDEN_CHUNKS.contains(&name) {
return Err(format!("{} chunk is not allowed to be stripped", x));
return Err(format!("{x} chunk is not allowed to be stripped"));
}
Ok(name)
})
@ -370,7 +370,7 @@ fn parse_chunk_name(name: &str) -> Result<[u8; 4], String> {
name.trim()
.as_bytes()
.try_into()
.map_err(|_| format!("Invalid chunk name {}", name))
.map_err(|_| format!("Invalid chunk name {name}"))
}
fn parse_numeric_range_opts(

View file

@ -31,6 +31,7 @@ impl OutFile {
/// Construct a new `OutFile` with the given path.
///
/// This is a convenience method for `OutFile::Path { path: Some(path), preserve_attrs: false }`.
#[must_use]
pub fn from_path(path: PathBuf) -> Self {
OutFile::Path {
path: Some(path),
@ -38,9 +39,10 @@ impl OutFile {
}
}
#[must_use]
pub fn path(&self) -> Option<&Path> {
match *self {
OutFile::Path {
Self::Path {
path: Some(ref p), ..
} => Some(p.as_path()),
_ => None,
@ -57,10 +59,11 @@ pub enum InFile {
}
impl InFile {
#[must_use]
pub fn path(&self) -> Option<&Path> {
match *self {
InFile::Path(ref p) => Some(p.as_path()),
InFile::StdIn => None,
Self::Path(ref p) => Some(p.as_path()),
Self::StdIn => None,
}
}
}
@ -68,15 +71,15 @@ impl InFile {
impl fmt::Display for InFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
InFile::Path(ref p) => write!(f, "{}", p.display()),
InFile::StdIn => f.write_str("stdin"),
Self::Path(ref p) => write!(f, "{}", p.display()),
Self::StdIn => f.write_str("stdin"),
}
}
}
impl<T: Into<PathBuf>> From<T> for InFile {
fn from(s: T) -> Self {
InFile::Path(s.into())
Self::Path(s.into())
}
}
@ -91,7 +94,7 @@ pub struct Options {
///
/// Default: `false`
pub force: bool,
/// Which RowFilters to try on the file
/// Which `RowFilters` to try on the file
///
/// Default: `None,Sub,Entropy,Bigrams`
pub filter: IndexSet<RowFilter>,
@ -156,8 +159,9 @@ pub struct Options {
}
impl Options {
pub fn from_preset(level: u8) -> Options {
let opts = Options::default();
#[must_use]
pub fn from_preset(level: u8) -> Self {
let opts = Self::default();
match level {
0 => opts.apply_preset_0(),
1 => opts.apply_preset_1(),
@ -173,8 +177,9 @@ impl Options {
}
}
pub fn max_compression() -> Options {
Options::from_preset(6)
#[must_use]
pub fn max_compression() -> Self {
Self::from_preset(6)
}
// The following methods make assumptions that they are operating
@ -237,9 +242,9 @@ impl Options {
}
impl Default for Options {
fn default() -> Options {
fn default() -> Self {
// Default settings based on -o 2 from the CLI interface
Options {
Self {
fix_errors: false,
force: false,
filter: indexset! {RowFilter::None, RowFilter::Sub, RowFilter::Entropy, RowFilter::Bigrams},

View file

@ -105,7 +105,7 @@ impl PngData {
aux_chunks.push(Chunk {
name: chunk.name,
data: Vec::new(),
})
});
}
idat_data.extend_from_slice(chunk.data);
}
@ -126,7 +126,7 @@ impl PngData {
aux_chunks.push(Chunk {
name: chunk.name,
data: chunk.data.to_owned(),
})
});
} else if chunk.name == *b"acTL" {
warn!(
"Stripping animation data from APNG - image will become standard PNG"
@ -170,11 +170,13 @@ impl PngData {
}
/// Return an estimate of the output size which can help with evaluation of very small data
#[must_use]
pub fn estimated_output_size(&self) -> usize {
self.idat_data.len() + self.raw.key_chunks_size()
}
/// Format the `PngData` struct into a valid PNG bytestream
#[must_use]
pub fn output(&self) -> Vec<u8> {
// PNG header
let mut output = vec![0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
@ -227,7 +229,7 @@ impl PngData {
transparent_color: Some(trns),
} => {
// Transparency pixel - 6 byte RGB16
let trns_data: Vec<_> = trns.iter().flat_map(|c| c.to_be_bytes()).collect();
let trns_data: Vec<_> = trns.iter().flat_map(u16::to_be_bytes).collect();
write_png_block(b"tRNS", &trns_data, &mut output);
}
_ => {}
@ -261,7 +263,7 @@ impl PngImage {
/// Assumes that the data has already been de-filtered
#[inline]
#[must_use]
pub fn change_interlacing(&self, interlace: Interlacing) -> Option<PngImage> {
pub fn change_interlacing(&self, interlace: Interlacing) -> Option<Self> {
if interlace == self.ihdr.interlaced {
return None;
}
@ -277,13 +279,15 @@ impl PngImage {
/// Return the number of channels in the image, based on color type
#[inline]
pub fn channels_per_pixel(&self) -> usize {
#[must_use]
pub const fn channels_per_pixel(&self) -> usize {
self.ihdr.color_type.channels_per_pixel() as usize
}
/// Return the number of bytes per channel in the image
#[inline]
pub fn bytes_per_channel(&self) -> usize {
#[must_use]
pub const fn bytes_per_channel(&self) -> usize {
match self.ihdr.bit_depth {
BitDepth::Sixteen => 2,
// Depths lower than 8 will round up to 1 byte
@ -292,6 +296,7 @@ impl PngImage {
}
/// Calculate the size of the PLTE and tRNS chunks
#[must_use]
pub fn key_chunks_size(&self) -> usize {
match &self.ihdr.color_type {
ColorType::Indexed { palette } => {
@ -310,6 +315,7 @@ impl PngImage {
/// Return an iterator over the scanlines of the image
#[inline]
#[must_use]
pub fn scan_lines(&self, has_filter: bool) -> ScanLines<'_> {
ScanLines::new(self, has_filter)
}
@ -337,6 +343,7 @@ impl PngImage {
}
/// Apply the specified filter type to all rows in the image
#[must_use]
pub fn filter_image(&self, filter: RowFilter, optimize_alpha: bool) -> Vec<u8> {
let mut filtered = Vec::with_capacity(self.data.len());
let bpp = self.bytes_per_channel() * self.channels_per_pixel();
@ -411,7 +418,7 @@ impl PngImage {
for f in try_filters {
f.filter_line(bpp, &mut line_data, &prev_line, &mut f_buf, alpha_bytes);
let mut counts = vec![0; 0x100];
for &i in f_buf.iter() {
for &i in &f_buf {
counts[i as usize] += 1;
}
let size = counts.into_iter().fold(0, |acc, x| {
@ -435,7 +442,7 @@ impl PngImage {
f.filter_line(bpp, &mut line_data, &prev_line, &mut f_buf, alpha_bytes);
let mut set = bitarr![0; 0x10000];
for pair in f_buf.windows(2) {
let bigram = (pair[0] as usize) << 8 | pair[1] as usize;
let bigram = ((pair[0] as usize) << 8) | pair[1] as usize;
set.set(bigram, true);
}
let size = set.count_ones();
@ -455,7 +462,7 @@ impl PngImage {
f.filter_line(bpp, &mut line_data, &prev_line, &mut f_buf, alpha_bytes);
counts.clear();
for pair in f_buf.windows(2) {
let bigram = (pair[0] as u16) << 8 | pair[1] as u16;
let bigram = (u16::from(pair[0]) << 8) | u16::from(pair[1]);
counts.entry(bigram).and_modify(|e| *e += 1).or_insert(1);
}
let size = counts.values().fold(0, |acc, &x| acc + ilog2i(x)) as i32;
@ -516,7 +523,7 @@ fn write_png_block(key: &[u8], chunk: &[u8], output: &mut Vec<u8>) {
}
// Integer approximation for i * log2(i) - much faster than float calculations
fn ilog2i(i: u32) -> u32 {
const fn ilog2i(i: u32) -> u32 {
let log = 32 - i.leading_zeros() - 1;
i * log + ((i - (1 << log)) << 1)
}

View file

@ -11,6 +11,7 @@ pub struct ScanLines<'a> {
}
impl<'a> ScanLines<'a> {
#[must_use]
pub fn new(png: &'a PngImage, has_filter: bool) -> Self {
Self {
iter: ScanLineRanges::new(png, has_filter),
@ -22,6 +23,7 @@ impl<'a> ScanLines<'a> {
impl<'a> Iterator for ScanLines<'a> {
type Item = ScanLine<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let (len, pass, num_pixels) = self.iter.next()?;
@ -79,6 +81,7 @@ impl ScanLineRanges {
impl Iterator for ScanLineRanges {
type Item = (usize, Option<u8>, usize);
fn next(&mut self) -> Option<Self::Item> {
if self.left == 0 {
return None;

View file

@ -7,6 +7,7 @@ use crate::{
};
/// Clean the alpha channel by setting the color of all fully transparent pixels to black
#[must_use]
pub fn cleaned_alpha_channel(png: &PngImage) -> Option<PngImage> {
if !png.ihdr.color_type.has_alpha() {
return None;
@ -85,8 +86,8 @@ pub fn reduced_alpha_channel(png: &PngImage, optimize_alpha: bool) -> Option<Png
// Construct the color type with appropriate transparency data
let transparent = transparency_pixel.map(|trns| match png.ihdr.bit_depth {
BitDepth::Sixteen => (trns as u16) << 8 | trns as u16,
_ => trns as u16,
BitDepth::Sixteen => (u16::from(trns) << 8) | u16::from(trns),
_ => u16::from(trns),
});
let target_color_type = match png.ihdr.color_type {
ColorType::GrayscaleAlpha => ColorType::Grayscale {

View file

@ -22,7 +22,7 @@ pub fn reduced_bit_depth_16_to_8(png: &PngImage, force_scale: bool) -> Option<Pn
}
Some(PngImage {
data: png.data.iter().step_by(2).cloned().collect(),
data: png.data.iter().step_by(2).copied().collect(),
ihdr: IhdrData {
color_type: png.ihdr.color_type.clone(),
bit_depth: BitDepth::Eight,
@ -48,8 +48,8 @@ pub fn scaled_bit_depth_16_to_8(png: &PngImage) -> Option<PngImage> {
}
// See: http://www.libpng.org/pub/png/spec/1.2/PNG-Decoders.html#D.Sample-depth-rescaling
// This allows values such as 0x00FF to be rounded to 0x01 rather than truncated to 0x00
let val = u16::from_be_bytes([pair[0], pair[1]]) as f64;
(val * 255.0 / 65535.0).round() as u8
let val = f32::from(u16::from_be_bytes([pair[0], pair[1]]));
(val * (255.0 / 65535.0)).round() as u8
})
.collect();
@ -138,7 +138,7 @@ pub fn reduced_bit_depth_8_or_less(png: &PngImage) -> Option<PngImage> {
let mut check = reduced_trans;
let mut bits = minimum_bits;
while bits < 8 {
check = check << bits | check;
check = (check << bits) | check;
bits <<= 1;
}
// If the transparency doesn't fit the new bit depth it is therefore unused - set it to None
@ -188,7 +188,7 @@ pub fn expanded_bit_depth_to_8(png: &PngImage) -> Option<PngImage> {
// Expand gray by repeating the bits
let mut bits = bit_depth;
while bits < 8 {
val = val << bits | val;
val = (val << bits) | val;
bits <<= 1;
}
}
@ -207,7 +207,7 @@ pub fn expanded_bit_depth_to_8(png: &PngImage) -> Option<PngImage> {
{
let mut bits = bit_depth;
while bits < 8 {
trans = trans << bits | trans;
trans = (trans << bits) | trans;
bits <<= 1;
}
ColorType::Grayscale {

View file

@ -49,7 +49,7 @@ pub fn reduced_to_indexed(png: &PngImage, allow_grayscale: bool) -> Option<PngIm
let mut raw_data = Vec::with_capacity(png.data.len() / png.channels_per_pixel());
let palette: Vec<_> = match png.ihdr.color_type {
ColorType::Grayscale { transparent_shade } => {
let pmap = build_palette(png.data.as_gray().iter().cloned(), &mut raw_data)?;
let pmap = build_palette(png.data.as_gray().iter().copied(), &mut raw_data)?;
// Convert the Gray16 transparency to Gray8
let transparency_pixel = transparent_shade.map(|t| Gray::from(t as u8));
pmap.into_iter()
@ -63,7 +63,7 @@ pub fn reduced_to_indexed(png: &PngImage, allow_grayscale: bool) -> Option<PngIm
.collect()
}
ColorType::RGB { transparent_color } => {
let pmap = build_palette(png.data.as_rgb().iter().cloned(), &mut raw_data)?;
let pmap = build_palette(png.data.as_rgb().iter().copied(), &mut raw_data)?;
// Convert the RGB16 transparency to RGB8
let transparency_pixel = transparent_color.map(|t| t.map(|c| c as u8));
pmap.into_iter()
@ -77,11 +77,11 @@ pub fn reduced_to_indexed(png: &PngImage, allow_grayscale: bool) -> Option<PngIm
.collect()
}
ColorType::GrayscaleAlpha => {
let pmap = build_palette(png.data.as_gray_alpha().iter().cloned(), &mut raw_data)?;
let pmap = build_palette(png.data.as_gray_alpha().iter().copied(), &mut raw_data)?;
pmap.into_iter().map(RGBA::from).collect()
}
ColorType::RGBA => {
let pmap = build_palette(png.data.as_rgba().iter().cloned(), &mut raw_data)?;
let pmap = build_palette(png.data.as_rgba().iter().copied(), &mut raw_data)?;
pmap.into_iter().collect()
}
_ => return None,

View file

@ -278,7 +278,7 @@ fn co_occurrence_matrix(num_colors: usize, png: &PngImage) -> Vec<Vec<u32>> {
matrix[val][prev_val] += 1;
}
}
prev = Some(line)
prev = Some(line);
}
matrix
}

View file

@ -2,8 +2,6 @@
use std::cell::RefCell;
#[cfg(feature = "zopfli")]
use std::num::NonZeroU8;
#[cfg(feature = "filetime")]
use std::ops::Deref;
use std::{
fs::remove_file,
path::{Path, PathBuf},
@ -94,7 +92,7 @@ fn test_it_converts(
bit_depth_out,
|_| {},
|_| {},
)
);
}
#[test]
@ -194,10 +192,7 @@ fn verbose_mode() {
let expected_prefix = expected_prefixes[i];
assert!(
log.starts_with(expected_prefix),
"logs[{}] = {:?} doesn't start with {:?}",
i,
log,
expected_prefix
"logs[{i}] = {log:?} doesn't start with {expected_prefix:?}"
);
}
}
@ -540,8 +535,8 @@ fn preserve_attrs() {
let cellref_atime_canon = atime_canon.borrow();
let cellref_mtime_canon = mtime_canon.borrow();
let ref_atime_canon: &filetime::FileTime = cellref_atime_canon.deref();
let ref_mtime_canon: &filetime::FileTime = cellref_mtime_canon.deref();
let ref_atime_canon: &filetime::FileTime = &cellref_atime_canon;
let ref_mtime_canon: &filetime::FileTime = &cellref_mtime_canon;
assert_eq!(
ref_atime_canon,