Fix some warnings
This commit is contained in:
parent
8053211bf4
commit
e896960b42
18 changed files with 193 additions and 110 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
|
@ -60,9 +60,9 @@ checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7"
|
|||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.7.0"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9966d2ab714d0f785dbac0a0396251a35280aeb42413281617d0209ab4898435"
|
||||
checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
|
|
@ -72,9 +72,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.68"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"
|
||||
checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#![feature(test)]
|
||||
#![allow(unused_must_use)]
|
||||
|
||||
extern crate oxipng;
|
||||
extern crate test;
|
||||
|
|
|
|||
2
build.rs
2
build.rs
|
|
@ -2,7 +2,7 @@ use rustc_version::{version, Version};
|
|||
use std::process::exit;
|
||||
|
||||
fn main() {
|
||||
// This should match the version in Github Actions scripts and the Readme
|
||||
// This should match the version in GitHub Actions scripts and the Readme
|
||||
const REQUIRED_VERSION: &str = "1.46.0";
|
||||
if version().unwrap() < Version::parse(REQUIRED_VERSION).unwrap() {
|
||||
eprintln!("oxipng requires rustc >= {}.", REQUIRED_VERSION);
|
||||
|
|
|
|||
4
clippy.toml
Normal file
4
clippy.toml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
msrv = "1.46"
|
||||
too-many-arguments-threshold = 9
|
||||
max-struct-bools = 11
|
||||
too-many-lines-threshold = 249
|
||||
|
|
@ -7,6 +7,7 @@ pub struct AtomicMin {
|
|||
}
|
||||
|
||||
impl AtomicMin {
|
||||
#[must_use]
|
||||
pub fn new(init: Option<usize>) -> Self {
|
||||
Self {
|
||||
val: AtomicUsize::new(init.unwrap_or(usize::MAX)),
|
||||
|
|
@ -22,7 +23,7 @@ impl AtomicMin {
|
|||
}
|
||||
}
|
||||
|
||||
/// Unset value is usize_max
|
||||
/// Unset value is `usize_max`
|
||||
pub fn as_atomic_usize(&self) -> &AtomicUsize {
|
||||
&self.val
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ impl fmt::Display for ColorType {
|
|||
impl ColorType {
|
||||
/// Get the code used by the PNG specification to denote this color type
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn png_header_code(self) -> u8 {
|
||||
match self {
|
||||
ColorType::Grayscale => 0,
|
||||
|
|
@ -46,6 +47,7 @@ impl ColorType {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn channels_per_pixel(self) -> u8 {
|
||||
match self {
|
||||
ColorType::Grayscale | ColorType::Indexed => 1,
|
||||
|
|
@ -91,6 +93,7 @@ impl fmt::Display for BitDepth {
|
|||
impl BitDepth {
|
||||
/// Retrieve the number of bits per channel per pixel as a `u8`
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn as_u8(self) -> u8 {
|
||||
match self {
|
||||
BitDepth::One => 1,
|
||||
|
|
@ -106,6 +109,7 @@ impl BitDepth {
|
|||
///
|
||||
/// If depth is unsupported
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn from_u8(depth: u8) -> BitDepth {
|
||||
match depth {
|
||||
1 => BitDepth::One,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::Deadline;
|
|||
use crate::PngError;
|
||||
use crate::PngResult;
|
||||
pub use cloudflare_zlib::is_supported;
|
||||
use cloudflare_zlib::*;
|
||||
use cloudflare_zlib::{Deflate, ZError};
|
||||
|
||||
impl From<ZError> for PngError {
|
||||
fn from(err: ZError) -> Self {
|
||||
|
|
@ -40,6 +40,7 @@ pub(crate) fn cfzlib_deflate(
|
|||
|
||||
#[test]
|
||||
fn compress_test() {
|
||||
use cloudflare_zlib::{Z_BEST_COMPRESSION, Z_DEFAULT_STRATEGY};
|
||||
let vec = cfzlib_deflate(
|
||||
b"azxcvbnm",
|
||||
Z_BEST_COMPRESSION as u8,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use crate::atomicmin::AtomicMin;
|
||||
use crate::error::PngError;
|
||||
use crate::PngResult;
|
||||
use miniz_oxide::deflate::core::*;
|
||||
use miniz_oxide::deflate::core::{
|
||||
compress, create_comp_flags_from_zip_params, CompressorOxide, TDEFLFlush, TDEFLStatus,
|
||||
};
|
||||
|
||||
pub(crate) fn compress_to_vec_oxipng(
|
||||
input: &[u8],
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ impl fmt::Display for PngError {
|
|||
|
||||
impl PngError {
|
||||
#[cold]
|
||||
#[must_use]
|
||||
pub fn new(description: &str) -> PngError {
|
||||
PngError::Other(description.into())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,12 +88,12 @@ impl Evaluator {
|
|||
|
||||
/// Set baseline image. It will be used only to measure minimum compression level required
|
||||
pub fn set_baseline(&self, image: Arc<PngImage>) {
|
||||
self.try_image_inner(image, false)
|
||||
self.try_image_inner(image, false);
|
||||
}
|
||||
|
||||
/// Check if the image is smaller than others
|
||||
pub fn try_image(&self, image: Arc<PngImage>) {
|
||||
self.try_image_inner(image, true)
|
||||
self.try_image_inner(image, true);
|
||||
}
|
||||
|
||||
fn try_image_inner(&self, image: Arc<PngImage>, is_reduction: bool) {
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ pub enum Headers {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn file_header_is_valid(bytes: &[u8]) -> bool {
|
||||
let expected_header: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
|
||||
|
||||
|
|
|
|||
94
src/lib.rs
94
src/lib.rs
|
|
@ -1,18 +1,41 @@
|
|||
#![warn(trivial_casts, trivial_numeric_casts, unused_import_braces)]
|
||||
#![deny(missing_debug_implementations, missing_copy_implementations)]
|
||||
#![warn(clippy::expl_impl_clone_on_copy)]
|
||||
#![warn(clippy::float_cmp_const)]
|
||||
#![warn(clippy::linkedlist)]
|
||||
#![warn(clippy::map_flatten)]
|
||||
#![warn(clippy::match_same_arms)]
|
||||
#![warn(clippy::mem_forget)]
|
||||
#![warn(clippy::mut_mut)]
|
||||
#![warn(clippy::mutex_integer)]
|
||||
#![warn(clippy::needless_continue)]
|
||||
#![warn(clippy::path_buf_push_overwrite)]
|
||||
#![warn(clippy::range_plus_one)]
|
||||
#![allow(clippy::cognitive_complexity)]
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
#![deny(
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unused_import_braces,
|
||||
missing_debug_implementations,
|
||||
missing_copy_implementations,
|
||||
rust_2018_idioms,
|
||||
rust_2018_compatibility,
|
||||
future_incompatible,
|
||||
unused,
|
||||
nonstandard_style
|
||||
)]
|
||||
#![warn(
|
||||
clippy::all,
|
||||
clippy::doc_markdown,
|
||||
clippy::wildcard_imports,
|
||||
clippy::unreadable_literal,
|
||||
clippy::unnested_or_patterns,
|
||||
clippy::must_use_candidate,
|
||||
clippy::map_unwrap_or,
|
||||
clippy::large_types_passed_by_value,
|
||||
clippy::float_cmp_const,
|
||||
clippy::lossy_float_literal,
|
||||
clippy::float_equality_without_abs,
|
||||
clippy::suboptimal_flops,
|
||||
clippy::imprecise_flops,
|
||||
clippy::mem_forget,
|
||||
clippy::mutex_integer,
|
||||
clippy::path_buf_push_overwrite,
|
||||
clippy::expl_impl_clone_on_copy,
|
||||
clippy::linkedlist,
|
||||
clippy::map_flatten,
|
||||
clippy::match_same_arms,
|
||||
clippy::mut_mut,
|
||||
clippy::needless_continue,
|
||||
clippy::range_plus_one,
|
||||
clippy::range_minus_one
|
||||
)]
|
||||
#![cfg_attr(
|
||||
not(any(feature = "libdeflater", feature = "zopfli")),
|
||||
allow(irrefutable_let_patterns),
|
||||
|
|
@ -30,7 +53,10 @@ use crate::deflate::inflate;
|
|||
use crate::evaluate::Evaluator;
|
||||
use crate::png::PngData;
|
||||
use crate::png::PngImage;
|
||||
use crate::reduction::*;
|
||||
use crate::reduction::{
|
||||
alpha, bit_depth, color, reduce_bit_depth, reduce_color_type, reduced_palette,
|
||||
try_alpha_reductions,
|
||||
};
|
||||
use crc::{Crc, CRC_32_ISO_HDLC};
|
||||
use image::{DynamicImage, GenericImageView, ImageFormat, Pixel};
|
||||
use log::{debug, error, info, warn};
|
||||
|
|
@ -79,6 +105,7 @@ pub enum OutFile {
|
|||
}
|
||||
|
||||
impl OutFile {
|
||||
#[must_use]
|
||||
pub fn path(&self) -> Option<&Path> {
|
||||
match *self {
|
||||
OutFile::Path(Some(ref p)) => Some(p.as_path()),
|
||||
|
|
@ -95,6 +122,7 @@ pub enum InFile {
|
|||
}
|
||||
|
||||
impl InFile {
|
||||
#[must_use]
|
||||
pub fn path(&self) -> Option<&Path> {
|
||||
match *self {
|
||||
InFile::Path(ref p) => Some(p.as_path()),
|
||||
|
|
@ -201,6 +229,7 @@ pub struct Options {
|
|||
}
|
||||
|
||||
impl Options {
|
||||
#[must_use]
|
||||
pub fn from_preset(level: u8) -> Options {
|
||||
let opts = Options::default();
|
||||
match level {
|
||||
|
|
@ -221,6 +250,7 @@ impl Options {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn max_compression() -> Options {
|
||||
Options::from_preset(6)
|
||||
}
|
||||
|
|
@ -364,7 +394,7 @@ pub fn optimize(input: &InFile, output: &OutFile, opts: &Options) -> PngResult<(
|
|||
let mut png = PngData::from_slice(&in_data, opts.fix_errors)?;
|
||||
|
||||
// Run the optimizer on the decoded PNG.
|
||||
let mut optimized_output = optimize_png(&mut png, &in_data, opts, deadline)?;
|
||||
let mut optimized_output = optimize_png(&mut png, &in_data, opts, &deadline)?;
|
||||
|
||||
if is_fully_optimized(in_data.len(), optimized_output.len(), opts) {
|
||||
info!("File already optimized");
|
||||
|
|
@ -396,8 +426,7 @@ pub fn optimize(input: &InFile, output: &OutFile, opts: &Options) -> PngResult<(
|
|||
(&OutFile::Path(ref output_path), _) => {
|
||||
let output_path = output_path
|
||||
.as_ref()
|
||||
.map(|p| p.as_path())
|
||||
.unwrap_or_else(|| input.path().unwrap());
|
||||
.map_or_else(|| input.path().unwrap(), PathBuf::as_path);
|
||||
if opts.backup {
|
||||
perform_backup(output_path)?;
|
||||
}
|
||||
|
|
@ -447,7 +476,7 @@ pub fn optimize_from_memory(data: &[u8], opts: &Options) -> PngResult<Vec<u8>> {
|
|||
let mut png = PngData::from_slice(data, opts.fix_errors)?;
|
||||
|
||||
// Run the optimizer on the decoded PNG.
|
||||
let optimized_output = optimize_png(&mut png, data, opts, deadline)?;
|
||||
let optimized_output = optimize_png(&mut png, data, opts, &deadline)?;
|
||||
|
||||
if is_fully_optimized(original_size, optimized_output.len(), opts) {
|
||||
info!("Image already optimized");
|
||||
|
|
@ -470,7 +499,7 @@ fn optimize_png(
|
|||
png: &mut PngData,
|
||||
original_data: &[u8],
|
||||
opts: &Options,
|
||||
deadline: Arc<Deadline>,
|
||||
deadline: &Arc<Deadline>,
|
||||
) -> PngResult<Vec<u8>> {
|
||||
type TrialWithData = (TrialOptions, Vec<u8>);
|
||||
|
||||
|
|
@ -532,13 +561,11 @@ fn optimize_png(
|
|||
if opts.interlace.is_none() {
|
||||
eval.set_baseline(png.raw.clone());
|
||||
}
|
||||
perform_reductions(png.raw.clone(), opts, &deadline, &eval);
|
||||
let reduction_occurred = if let Some(result) = eval.get_result() {
|
||||
perform_reductions(png.raw.clone(), opts, deadline, &eval);
|
||||
let reduction_occurred = eval.get_result().map_or(false, |result| {
|
||||
*png = result;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
});
|
||||
|
||||
if opts.idat_recoding || reduction_occurred {
|
||||
// Go through selected permutations and determine the best
|
||||
|
|
@ -605,7 +632,7 @@ fn optimize_png(
|
|||
trial.strategy,
|
||||
window,
|
||||
&best_size,
|
||||
&deadline,
|
||||
deadline,
|
||||
),
|
||||
#[cfg(feature = "zopfli")]
|
||||
Deflaters::Zopfli => deflate::zopfli_deflate(filtered),
|
||||
|
|
@ -783,7 +810,7 @@ fn perform_reductions(
|
|||
}
|
||||
}
|
||||
|
||||
try_alpha_reductions(png, &opts.alphas, eval);
|
||||
try_alpha_reductions(&png, &opts.alphas, eval);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -801,6 +828,7 @@ pub struct Deadline {
|
|||
}
|
||||
|
||||
impl Deadline {
|
||||
#[must_use]
|
||||
pub fn new(timeout: Option<Duration>) -> Self {
|
||||
Self {
|
||||
imp: timeout.map(|timeout| DeadlineImp {
|
||||
|
|
@ -872,7 +900,7 @@ fn perform_strip(png: &mut PngData, opts: &Options) {
|
|||
*b"cHRM", *b"gAMA", *b"iCCP", *b"sBIT", *b"sRGB", *b"bKGD", *b"hIST", *b"pHYs",
|
||||
*b"sPLT",
|
||||
];
|
||||
let keys: Vec<[u8; 4]> = raw.aux_headers.keys().cloned().collect();
|
||||
let keys: Vec<[u8; 4]> = raw.aux_headers.keys().copied().collect();
|
||||
for hdr in &keys {
|
||||
if !PRESERVED_HEADERS.contains(hdr) {
|
||||
raw.aux_headers.remove(hdr);
|
||||
|
|
@ -908,7 +936,7 @@ fn perform_strip(png: &mut PngData, opts: &Options) {
|
|||
}
|
||||
}
|
||||
|
||||
/// If the profile is sRGB, extracts the rendering intent value from it
|
||||
/// If the profile is `sRGB`, extracts the rendering intent value from it
|
||||
fn srgb_rendering_intent(mut iccp: &[u8]) -> Option<u8> {
|
||||
// Skip (useless) profile name
|
||||
loop {
|
||||
|
|
@ -1032,14 +1060,14 @@ fn copy_permissions(metadata_input: &Metadata, out_file: &File) -> PngResult<()>
|
|||
))
|
||||
})
|
||||
.and_then(|out_meta_reread| {
|
||||
if out_meta_reread.permissions().mode() != permissions {
|
||||
if out_meta_reread.permissions().mode() == permissions {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(PngError::new(&format!(
|
||||
"failed to set permissions, expected: {:04o}, found: {:04o}",
|
||||
permissions,
|
||||
out_meta_reread.permissions().mode()
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
|||
77
src/main.rs
77
src/main.rs
|
|
@ -1,17 +1,41 @@
|
|||
#![warn(trivial_casts, trivial_numeric_casts, unused_import_braces)]
|
||||
#![deny(missing_debug_implementations, missing_copy_implementations)]
|
||||
#![warn(clippy::expl_impl_clone_on_copy)]
|
||||
#![warn(clippy::float_cmp_const)]
|
||||
#![warn(clippy::linkedlist)]
|
||||
#![warn(clippy::map_flatten)]
|
||||
#![warn(clippy::match_same_arms)]
|
||||
#![warn(clippy::mem_forget)]
|
||||
#![warn(clippy::mut_mut)]
|
||||
#![warn(clippy::mutex_integer)]
|
||||
#![warn(clippy::needless_continue)]
|
||||
#![warn(clippy::path_buf_push_overwrite)]
|
||||
#![warn(clippy::range_plus_one)]
|
||||
#![allow(clippy::cognitive_complexity)]
|
||||
#![deny(
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unused_import_braces,
|
||||
missing_debug_implementations,
|
||||
missing_copy_implementations,
|
||||
rust_2018_idioms,
|
||||
rust_2018_compatibility,
|
||||
future_incompatible,
|
||||
unused,
|
||||
nonstandard_style
|
||||
)]
|
||||
#![warn(
|
||||
clippy::all,
|
||||
clippy::doc_markdown,
|
||||
clippy::wildcard_imports,
|
||||
clippy::unreadable_literal,
|
||||
clippy::unnested_or_patterns,
|
||||
clippy::must_use_candidate,
|
||||
clippy::map_unwrap_or,
|
||||
clippy::large_types_passed_by_value,
|
||||
clippy::float_cmp_const,
|
||||
clippy::lossy_float_literal,
|
||||
clippy::float_equality_without_abs,
|
||||
clippy::suboptimal_flops,
|
||||
clippy::imprecise_flops,
|
||||
clippy::mem_forget,
|
||||
clippy::mutex_integer,
|
||||
clippy::path_buf_push_overwrite,
|
||||
clippy::expl_impl_clone_on_copy,
|
||||
clippy::linkedlist,
|
||||
clippy::map_flatten,
|
||||
clippy::match_same_arms,
|
||||
clippy::mut_mut,
|
||||
clippy::needless_continue,
|
||||
clippy::range_plus_one,
|
||||
clippy::range_minus_one
|
||||
)]
|
||||
|
||||
use clap::{App, AppSettings, Arg, ArgMatches};
|
||||
use indexmap::IndexSet;
|
||||
|
|
@ -318,12 +342,13 @@ fn collect_files(
|
|||
}
|
||||
continue;
|
||||
};
|
||||
let out_file = if let Some(ref out_dir) = *out_dir {
|
||||
let out_path = Some(out_dir.join(input.file_name().unwrap()));
|
||||
OutFile::Path(out_path)
|
||||
} else {
|
||||
(*out_file).clone()
|
||||
};
|
||||
let out_file = out_dir.as_ref().map_or_else(
|
||||
|| (*out_file).clone(),
|
||||
|out_dir| {
|
||||
let out_path = Some(out_dir.join(input.file_name().unwrap()));
|
||||
OutFile::Path(out_path)
|
||||
},
|
||||
);
|
||||
let in_file = if using_stdin {
|
||||
InFile::StdIn
|
||||
} else {
|
||||
|
|
@ -335,7 +360,7 @@ fn collect_files(
|
|||
}
|
||||
|
||||
fn parse_opts_into_struct(
|
||||
matches: &ArgMatches,
|
||||
matches: &ArgMatches<'_>,
|
||||
) -> Result<(OutFile, Option<PathBuf>, Options), String> {
|
||||
stderrlog::new()
|
||||
.module(module_path!())
|
||||
|
|
@ -389,10 +414,12 @@ fn parse_opts_into_struct(
|
|||
|
||||
let out_file = if matches.is_present("stdout") {
|
||||
OutFile::StdOut
|
||||
} else if let Some(x) = matches.value_of("output_file") {
|
||||
OutFile::Path(Some(PathBuf::from(x)))
|
||||
} else {
|
||||
OutFile::Path(None)
|
||||
matches
|
||||
.value_of("output_file")
|
||||
.map_or(OutFile::Path(None), |x| {
|
||||
OutFile::Path(Some(PathBuf::from(x)))
|
||||
})
|
||||
};
|
||||
|
||||
if matches.is_present("alpha") {
|
||||
|
|
@ -452,7 +479,7 @@ fn parse_opts_into_struct(
|
|||
}
|
||||
|
||||
if let Some(hdrs) = matches.value_of("keep") {
|
||||
opts.strip = Headers::Keep(hdrs.split(',').map(|x| x.trim().to_owned()).collect())
|
||||
opts.strip = Headers::Keep(hdrs.split(',').map(|x| x.trim().to_owned()).collect());
|
||||
}
|
||||
|
||||
if let Some(hdrs) = matches.value_of("strip") {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::colors::ColorType;
|
||||
use crate::deflate;
|
||||
use crate::error::PngError;
|
||||
use crate::filters::*;
|
||||
use crate::headers::*;
|
||||
use crate::filters::{filter_line, unfilter_line};
|
||||
use crate::headers::{file_header_is_valid, parse_ihdr_header, parse_next_header, IhdrData};
|
||||
use crate::interlace::{deinterlace_image, interlace_image};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use crc::{Crc, CRC_32_ISO_HDLC};
|
||||
|
|
@ -170,6 +170,7 @@ impl PngData {
|
|||
}
|
||||
|
||||
/// 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];
|
||||
|
|
@ -264,12 +265,14 @@ impl PngImage {
|
|||
|
||||
/// Return the number of channels in the image, based on color type
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn channels_per_pixel(&self) -> u8 {
|
||||
self.ihdr.color_type.channels_per_pixel()
|
||||
}
|
||||
|
||||
/// Return an iterator over the scanlines of the image
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn scan_lines(&self) -> ScanLines<'_> {
|
||||
ScanLines::new(self)
|
||||
}
|
||||
|
|
@ -309,6 +312,7 @@ impl PngImage {
|
|||
/// 3: Average
|
||||
/// 4: Paeth
|
||||
/// 5: All (heuristically pick the best filter for each line)
|
||||
#[must_use]
|
||||
pub fn filter_image(&self, filter: u8) -> Vec<u8> {
|
||||
let mut filtered = Vec::with_capacity(self.data.len());
|
||||
let bpp = ((self.ihdr.bit_depth.as_u8() * self.channels_per_pixel() + 7) / 8) as usize;
|
||||
|
|
@ -351,7 +355,7 @@ impl PngImage {
|
|||
best_filter = filter;
|
||||
std::mem::swap(&mut best_line, &mut f_buf);
|
||||
}
|
||||
f_buf.clear() //discard buffer, and start again
|
||||
f_buf.clear(); //discard buffer, and start again
|
||||
}
|
||||
filtered.push(best_filter);
|
||||
filtered.extend_from_slice(&best_line);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use rayon::prelude::*;
|
|||
use std::sync::Arc;
|
||||
|
||||
pub(crate) fn try_alpha_reductions(
|
||||
png: Arc<PngImage>,
|
||||
png: &Arc<PngImage>,
|
||||
alphas: &IndexSet<AlphaOptim>,
|
||||
eval: &Evaluator,
|
||||
) {
|
||||
|
|
@ -23,10 +23,11 @@ pub(crate) fn try_alpha_reductions(
|
|||
alphas
|
||||
.par_iter()
|
||||
.with_max_len(1)
|
||||
.filter_map(|&alpha| filtered_alpha_channel(&png, alpha))
|
||||
.filter_map(|&alpha| filtered_alpha_channel(png, alpha))
|
||||
.for_each(|image| eval.try_image(Arc::new(image)));
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn filtered_alpha_channel(png: &PngImage, optim: AlphaOptim) -> Option<PngImage> {
|
||||
let (bpc, bpp) = match png.ihdr.color_type {
|
||||
ColorType::RGBA | ColorType::GrayscaleAlpha => {
|
||||
|
|
@ -214,7 +215,7 @@ pub fn reduced_alpha_channel(png: &PngImage) -> Option<PngImage> {
|
|||
// and alpha has just been removed
|
||||
if let Some(sbit_header) = png.aux_headers.get(b"sBIT") {
|
||||
// Some programs save the sBIT header as RGB even if the image is RGBA.
|
||||
aux_headers.insert(*b"sBIT", sbit_header.iter().cloned().take(3).collect());
|
||||
aux_headers.insert(*b"sBIT", sbit_header.iter().copied().take(3).collect());
|
||||
}
|
||||
|
||||
Some(PngImage {
|
||||
|
|
|
|||
|
|
@ -117,11 +117,11 @@ pub fn reduced_color_to_palette(png: &PngImage) -> Option<PngImage> {
|
|||
raw_data.push(line.filter);
|
||||
let ok = if png.ihdr.color_type == ColorType::RGB {
|
||||
reduce_scanline_to_palette(
|
||||
line.data.as_rgb().iter().cloned().map(|px| {
|
||||
px.alpha(if Some(px) != transparency_pixel {
|
||||
255
|
||||
} else {
|
||||
line.data.as_rgb().iter().copied().map(|px| {
|
||||
px.alpha(if Some(px) == transparency_pixel {
|
||||
0
|
||||
} else {
|
||||
255
|
||||
})
|
||||
}),
|
||||
&mut palette,
|
||||
|
|
@ -130,7 +130,7 @@ pub fn reduced_color_to_palette(png: &PngImage) -> Option<PngImage> {
|
|||
} else {
|
||||
debug_assert_eq!(png.ihdr.color_type, ColorType::RGBA);
|
||||
reduce_scanline_to_palette(
|
||||
line.data.as_rgba().iter().cloned(),
|
||||
line.data.as_rgba().iter().copied(),
|
||||
&mut palette,
|
||||
&mut raw_data,
|
||||
)
|
||||
|
|
@ -143,10 +143,10 @@ pub fn reduced_color_to_palette(png: &PngImage) -> Option<PngImage> {
|
|||
let num_transparent = palette
|
||||
.iter()
|
||||
.filter_map(|(px, &idx)| {
|
||||
if px.a != 255 {
|
||||
Some(idx as usize + 1)
|
||||
} else {
|
||||
if px.a == 255 {
|
||||
None
|
||||
} else {
|
||||
Some(idx as usize + 1)
|
||||
}
|
||||
})
|
||||
.max();
|
||||
|
|
@ -180,7 +180,7 @@ pub fn reduced_color_to_palette(png: &PngImage) -> Option<PngImage> {
|
|||
|
||||
if let Some(sbit_header) = png.aux_headers.get(b"sBIT") {
|
||||
// Some programs save the sBIT header as RGB even if the image is RGBA.
|
||||
aux_headers.insert(*b"sBIT", sbit_header.iter().cloned().take(3).collect());
|
||||
aux_headers.insert(*b"sBIT", sbit_header.iter().copied().take(3).collect());
|
||||
}
|
||||
|
||||
let mut palette_vec = vec![RGBA8::new(0, 0, 0, 0); palette.len()];
|
||||
|
|
@ -220,8 +220,8 @@ pub fn reduce_rgb_to_grayscale(png: &PngImage) -> Option<PngImage> {
|
|||
let pixel_bytes = cur_pixel
|
||||
.iter()
|
||||
.step_by(2)
|
||||
.cloned()
|
||||
.zip(cur_pixel.iter().skip(1).step_by(2).cloned())
|
||||
.copied()
|
||||
.zip(cur_pixel.iter().skip(1).step_by(2).copied())
|
||||
.unique()
|
||||
.collect::<Vec<(u8, u8)>>();
|
||||
if pixel_bytes.len() > 1 {
|
||||
|
|
@ -235,15 +235,16 @@ pub fn reduce_rgb_to_grayscale(png: &PngImage) -> Option<PngImage> {
|
|||
}
|
||||
}
|
||||
|
||||
let transparency_pixel = if let Some(ref trns) = png.transparency_pixel {
|
||||
if trns.len() != 6 || trns[0..2] != trns[2..4] || trns[2..4] != trns[4..6] {
|
||||
None
|
||||
} else {
|
||||
Some(trns[0..2].to_owned())
|
||||
}
|
||||
} else {
|
||||
png.transparency_pixel.clone()
|
||||
};
|
||||
let transparency_pixel = png.transparency_pixel.as_ref().map_or_else(
|
||||
|| png.transparency_pixel.clone(),
|
||||
|trns| {
|
||||
if trns.len() != 6 || trns[0..2] != trns[2..4] || trns[2..4] != trns[4..6] {
|
||||
None
|
||||
} else {
|
||||
Some(trns[0..2].to_owned())
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let mut aux_headers = png.aux_headers.clone();
|
||||
if let Some(sbit_header) = png.aux_headers.get(b"sBIT") {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
use crate::colors::{BitDepth, ColorType};
|
||||
use crate::headers::IhdrData;
|
||||
use crate::png::PngImage;
|
||||
use indexmap::map::{Entry::*, IndexMap};
|
||||
use indexmap::map::{
|
||||
Entry::{Occupied, Vacant},
|
||||
IndexMap,
|
||||
};
|
||||
use rgb::RGBA8;
|
||||
use std::borrow::Cow;
|
||||
|
||||
|
|
@ -10,13 +13,16 @@ use crate::alpha::reduced_alpha_channel;
|
|||
pub mod bit_depth;
|
||||
use crate::bit_depth::reduce_bit_depth_8_or_less;
|
||||
pub mod color;
|
||||
use crate::color::*;
|
||||
use crate::color::{
|
||||
reduce_rgb_to_grayscale, reduce_rgba_to_grayscale_alpha, reduced_color_to_palette,
|
||||
};
|
||||
|
||||
pub(crate) use crate::alpha::try_alpha_reductions;
|
||||
pub(crate) use crate::bit_depth::reduce_bit_depth;
|
||||
|
||||
/// Attempt to reduce the number of colors in the palette
|
||||
/// Returns `None` if palette hasn't changed
|
||||
#[must_use]
|
||||
pub fn reduced_palette(png: &PngImage) -> Option<PngImage> {
|
||||
if png.ihdr.color_type != ColorType::Indexed {
|
||||
// Can't reduce if there is no palette
|
||||
|
|
@ -67,7 +73,7 @@ pub fn reduced_palette(png: &PngImage) -> Option<PngImage> {
|
|||
.get(i)
|
||||
.copied()
|
||||
.unwrap_or_else(|| RGBA8::new(0, 0, 0, 255));
|
||||
((color.a as i32) << 18)
|
||||
((i32::from(color.a)) << 18)
|
||||
// These are coefficients for standard sRGB to luma conversion
|
||||
- i32::from(color.r) * 299
|
||||
- i32::from(color.g) * 587
|
||||
|
|
@ -78,14 +84,14 @@ pub fn reduced_palette(png: &PngImage) -> Option<PngImage> {
|
|||
|
||||
let mut next_index = 0_u16;
|
||||
let mut seen = IndexMap::with_capacity(palette.len());
|
||||
for (i, used) in used_enumerated.iter().cloned() {
|
||||
for (i, used) in used_enumerated.iter().copied() {
|
||||
if !used {
|
||||
continue;
|
||||
}
|
||||
// There are invalid files that use pixel indices beyond palette size
|
||||
let color = palette
|
||||
.get(i)
|
||||
.cloned()
|
||||
.copied()
|
||||
.unwrap_or_else(|| RGBA8::new(0, 0, 0, 255));
|
||||
match seen.entry(color) {
|
||||
Vacant(new) => {
|
||||
|
|
@ -137,7 +143,7 @@ fn do_palette_reduction(png: &PngImage, palette_map: &[Option<u8>; 256]) -> Opti
|
|||
}
|
||||
|
||||
fn palette_map_to_byte_map(png: &PngImage, palette_map: &[Option<u8>; 256]) -> Option<[u8; 256]> {
|
||||
let len = png.palette.as_ref().map_or(0, |p| p.len());
|
||||
let len = png.palette.as_ref().map_or(0, Vec::len);
|
||||
if (0..len).all(|i| palette_map[i].map_or(true, |to| to == i as u8)) {
|
||||
// No reduction necessary
|
||||
return None;
|
||||
|
|
@ -148,18 +154,18 @@ fn palette_map_to_byte_map(png: &PngImage, palette_map: &[Option<u8>; 256]) -> O
|
|||
// low bit-depths can be pre-computed for every byte value
|
||||
match png.ihdr.bit_depth {
|
||||
BitDepth::Eight => {
|
||||
for byte in 0..=255usize {
|
||||
byte_map[byte] = palette_map[byte].unwrap_or(0)
|
||||
for byte in 0..=255_usize {
|
||||
byte_map[byte] = palette_map[byte].unwrap_or(0);
|
||||
}
|
||||
}
|
||||
BitDepth::Four => {
|
||||
for byte in 0..=255usize {
|
||||
for byte in 0..=255_usize {
|
||||
byte_map[byte] = palette_map[(byte & 0x0F)].unwrap_or(0)
|
||||
| (palette_map[(byte >> 4)].unwrap_or(0) << 4);
|
||||
}
|
||||
}
|
||||
BitDepth::Two => {
|
||||
for byte in 0..=255usize {
|
||||
for byte in 0..=255_usize {
|
||||
byte_map[byte] = palette_map[(byte & 0x03)].unwrap_or(0)
|
||||
| (palette_map[((byte >> 2) & 0x03)].unwrap_or(0) << 2)
|
||||
| (palette_map[((byte >> 4) & 0x03)].unwrap_or(0) << 4)
|
||||
|
|
@ -173,7 +179,7 @@ fn palette_map_to_byte_map(png: &PngImage, palette_map: &[Option<u8>; 256]) -> O
|
|||
}
|
||||
|
||||
fn reordered_palette(palette: &[RGBA8], palette_map: &[Option<u8>; 256]) -> Vec<RGBA8> {
|
||||
let max_index = palette_map.iter().cloned().flatten().max().unwrap_or(0) as usize;
|
||||
let max_index = palette_map.iter().copied().flatten().max().unwrap_or(0) as usize;
|
||||
let mut new_palette = vec![RGBA8::new(0, 0, 0, 255); max_index + 1];
|
||||
for (&color, &map_to) in palette.iter().zip(palette_map.iter()) {
|
||||
if let Some(map_to) = map_to {
|
||||
|
|
@ -185,6 +191,7 @@ fn reordered_palette(palette: &[RGBA8], palette_map: &[Option<u8>; 256]) -> Vec<
|
|||
|
||||
/// Attempt to reduce the color type of the image
|
||||
/// Returns true if the color type was reduced, false otherwise
|
||||
#[must_use]
|
||||
pub fn reduce_color_type(png: &PngImage, grayscale_reduction: bool) -> Option<PngImage> {
|
||||
let mut should_reduce_bit_depth = false;
|
||||
let mut reduced = Cow::Borrowed(png);
|
||||
|
|
@ -244,6 +251,6 @@ pub fn reduce_color_type(png: &PngImage, grayscale_reduction: bool) -> Option<Pn
|
|||
|
||||
match reduced {
|
||||
Cow::Owned(r) => Some(r),
|
||||
_ => None,
|
||||
Cow::Borrowed(_) => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ fn test_it_converts_callbacks<CBPRE, CBPOST>(
|
|||
mut callback_pre: CBPRE,
|
||||
mut callback_post: CBPOST,
|
||||
) where
|
||||
CBPOST: FnMut(&Path) -> (),
|
||||
CBPRE: FnMut(&Path) -> (),
|
||||
CBPOST: FnMut(&Path),
|
||||
CBPRE: FnMut(&Path),
|
||||
{
|
||||
let png = PngData::new(&input, opts.fix_errors).unwrap();
|
||||
|
||||
|
|
@ -46,14 +46,14 @@ fn test_it_converts_callbacks<CBPRE, CBPOST>(
|
|||
|
||||
callback_pre(&input);
|
||||
|
||||
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
|
||||
match oxipng::optimize(&InFile::Path(input), output, opts) {
|
||||
Ok(_) => (),
|
||||
Err(x) => panic!("{}", x),
|
||||
};
|
||||
let output = output.path().unwrap();
|
||||
assert!(output.exists());
|
||||
|
||||
callback_post(&output);
|
||||
callback_post(output);
|
||||
|
||||
let png = match PngData::new(output, opts.fix_errors) {
|
||||
Ok(x) => x,
|
||||
|
|
|
|||
Loading…
Reference in a new issue