More flexible verbosity, improve verbose test (#501)

* More flexible verbosity, improve verbose test

* Tweak reporting format

* Add evaluation reporting at trace level
This commit is contained in:
andrews05 2023-05-07 02:45:31 +12:00 committed by GitHub
parent 798a120926
commit a26d225d81
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 86 additions and 88 deletions

View file

@ -1,5 +1,5 @@
use rgb::{RGB16, RGBA8}; use rgb::{RGB16, RGBA8};
use std::fmt; use std::{fmt, fmt::Display};
use crate::PngError; use crate::PngError;
@ -27,20 +27,18 @@ pub enum ColorType {
RGBA, RGBA,
} }
impl fmt::Display for ColorType { impl Display for ColorType {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( match self {
f, ColorType::Grayscale { .. } => Display::fmt("Grayscale", f),
"{}", ColorType::RGB { .. } => Display::fmt("RGB", f),
match *self { ColorType::Indexed { palette } => {
ColorType::Grayscale { .. } => "Grayscale", Display::fmt(&format!("Indexed ({} colors)", palette.len()), f)
ColorType::RGB { .. } => "RGB",
ColorType::Indexed { .. } => "Indexed",
ColorType::GrayscaleAlpha => "Grayscale + Alpha",
ColorType::RGBA => "RGB + Alpha",
} }
) ColorType::GrayscaleAlpha => Display::fmt("Grayscale + Alpha", f),
ColorType::RGBA => Display::fmt("RGB + Alpha", f),
}
} }
} }
@ -109,9 +107,9 @@ impl TryFrom<u8> for BitDepth {
} }
} }
impl fmt::Display for BitDepth { impl Display for BitDepth {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", *self as u8) Display::fmt(&(*self as u8).to_string(), f)
} }
} }

View file

@ -9,9 +9,11 @@ use crate::png::PngImage;
#[cfg(not(feature = "parallel"))] #[cfg(not(feature = "parallel"))]
use crate::rayon; use crate::rayon;
use crate::Deadline; use crate::Deadline;
use crate::PngError;
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
use crossbeam_channel::{unbounded, Receiver, Sender}; use crossbeam_channel::{unbounded, Receiver, Sender};
use indexmap::IndexSet; use indexmap::IndexSet;
use log::trace;
use rayon::prelude::*; use rayon::prelude::*;
#[cfg(not(feature = "parallel"))] #[cfg(not(feature = "parallel"))]
use std::cell::RefCell; use std::cell::RefCell;
@ -131,9 +133,8 @@ impl Evaluator {
return; return;
} }
let filtered = image.filter_image(filter, optimize_alpha); let filtered = image.filter_image(filter, optimize_alpha);
if let Ok(idat_data) = let idat_data = deflate::deflate(&filtered, compression, &best_candidate_size);
deflate::deflate(&filtered, compression, &best_candidate_size) if let Ok(idat_data) = idat_data {
{
let new = Candidate { let new = Candidate {
image: PngData { image: PngData {
idat_data, idat_data,
@ -144,7 +145,15 @@ impl Evaluator {
is_reduction, is_reduction,
nth, nth,
}; };
best_candidate_size.set_min(new.image.estimated_output_size()); let size = new.image.estimated_output_size();
best_candidate_size.set_min(size);
trace!(
"Eval: {}-bit {:20} {:8} {} bytes",
image.ihdr.bit_depth,
image.ihdr.color_type,
filter,
size
);
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
{ {
@ -158,6 +167,14 @@ impl Evaluator {
best => *best = Some(new), best => *best = Some(new),
} }
} }
} else if let Err(PngError::DeflatedDataTooLong(size)) = idat_data {
trace!(
"Eval: {}-bit {:20} {:8} >{} bytes",
image.ihdr.bit_depth,
image.ihdr.color_type,
filter,
size
);
} }
}); });
}); });

View file

@ -1,4 +1,5 @@
use std::{fmt::Display, mem::transmute}; use std::mem::transmute;
use std::{fmt, fmt::Display};
use crate::error::PngError; use crate::error::PngError;
@ -31,11 +32,9 @@ impl TryFrom<u8> for RowFilter {
} }
impl Display for RowFilter { impl Display for RowFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( Display::fmt(
f, match self {
"{:8}",
match *self {
Self::None => "None", Self::None => "None",
Self::Sub => "Sub", Self::Sub => "Sub",
Self::Up => "Up", Self::Up => "Up",
@ -46,7 +45,8 @@ impl Display for RowFilter {
Self::Bigrams => "Bigrams", Self::Bigrams => "Bigrams",
Self::BigEnt => "BigEnt", Self::BigEnt => "BigEnt",
Self::Brute => "Brute", Self::Brute => "Brute",
} },
f,
) )
} }
} }

View file

@ -1,4 +1,4 @@
use std::fmt::Display; use std::{fmt, fmt::Display};
use crate::headers::IhdrData; use crate::headers::IhdrData;
use crate::png::PngImage; use crate::png::PngImage;
@ -25,14 +25,13 @@ impl TryFrom<u8> for Interlacing {
} }
impl Display for Interlacing { impl Display for Interlacing {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( Display::fmt(
f, match self {
"{}",
match *self {
Self::None => "non-interlaced", Self::None => "non-interlaced",
Self::Adam7 => "interlaced", Self::Adam7 => "interlaced",
} },
f,
) )
} }
} }

View file

@ -25,13 +25,13 @@ extern crate rayon;
mod rayon; mod rayon;
use crate::atomicmin::AtomicMin; use crate::atomicmin::AtomicMin;
use crate::colors::{BitDepth, ColorType}; use crate::colors::BitDepth;
use crate::deflate::{crc32, inflate}; use crate::deflate::{crc32, inflate};
use crate::evaluate::Evaluator; use crate::evaluate::Evaluator;
use crate::png::PngData; use crate::png::PngData;
use crate::png::PngImage; use crate::png::PngImage;
use crate::reduction::*; use crate::reduction::*;
use log::{debug, info, warn}; use log::{debug, info, trace, warn};
use rayon::prelude::*; use rayon::prelude::*;
use std::fmt; use std::fmt;
use std::fs::{copy, File, Metadata}; use std::fs::{copy, File, Metadata};
@ -330,7 +330,7 @@ pub fn optimize(input: &InFile, output: &OutFile, opts: &Options) -> PngResult<(
)) ))
}) })
.map(Some)?; .map(Some)?;
debug!("preserving metadata: {:?}", opt_metadata_preserved); trace!("preserving metadata: {:?}", opt_metadata_preserved);
} else { } else {
opt_metadata_preserved = None; opt_metadata_preserved = None;
} }
@ -532,7 +532,7 @@ fn optimize_png(
} }
if !filters.is_empty() { if !filters.is_empty() {
debug!("Evaluating: {} filters", filters.len()); trace!("Evaluating: {} filters", filters.len());
let eval = Evaluator::new(deadline, filters, eval_compression, opts.optimize_alpha); let eval = Evaluator::new(deadline, filters, eval_compression, opts.optimize_alpha);
if eval_filter.is_some() { if eval_filter.is_some() {
eval.set_best_size(png.idat_data.len()); eval.set_best_size(png.idat_data.len());
@ -615,7 +615,7 @@ fn optimize_png(
png.idat_data = idat_data; png.idat_data = idat_data;
debug!("Found better combination:"); debug!("Found better combination:");
debug!( debug!(
" zc = {} f = {} {} bytes", " zc = {} f = {:8} {} bytes",
opts.compression, opts.compression,
opts.filter, opts.filter,
png.idat_data.len() png.idat_data.len()
@ -746,16 +746,20 @@ fn perform_trial(
match new_idat { match new_idat {
Ok(n) => { Ok(n) => {
let bytes = n.len(); let bytes = n.len();
debug!( trace!(
" zc = {} f = {} {} bytes", " zc = {} f = {:8} {} bytes",
trial.compression, trial.filter, bytes trial.compression,
trial.filter,
bytes
); );
Some((trial, n)) Some((trial, n))
} }
Err(PngError::DeflatedDataTooLong(bytes)) => { Err(PngError::DeflatedDataTooLong(bytes)) => {
debug!( trace!(
" zc = {} f = {} >{} bytes", " zc = {} f = {:8} >{} bytes",
trial.compression, trial.filter, bytes, trial.compression,
trial.filter,
bytes,
); );
None None
} }
@ -814,24 +818,10 @@ impl Deadline {
/// Display the format of the image data /// Display the format of the image data
fn report_format(prefix: &str, png: &PngImage) { fn report_format(prefix: &str, png: &PngImage) {
if let ColorType::Indexed { palette } = &png.ihdr.color_type { debug!(
debug!( "{}{}-bit {}, {}",
"{}{} bits/pixel, {} colors in palette ({})", prefix, png.ihdr.bit_depth, png.ihdr.color_type, png.ihdr.interlaced
prefix, );
png.ihdr.bit_depth,
palette.len(),
png.ihdr.interlaced
);
} else {
debug!(
"{}{}x{} bits/pixel, {} ({})",
prefix,
png.channels_per_pixel(),
png.ihdr.bit_depth,
png.ihdr.color_type,
png.ihdr.interlaced
);
}
} }
/// Strip headers from the `PngData` object, as requested by the passed `Options` /// Strip headers from the `PngData` object, as requested by the passed `Options`
@ -1032,9 +1022,10 @@ fn copy_times(_: &Metadata, _: &Path) -> PngResult<()> {
fn copy_times(input_path_meta: &Metadata, out_path: &Path) -> PngResult<()> { fn copy_times(input_path_meta: &Metadata, out_path: &Path) -> PngResult<()> {
let atime = filetime::FileTime::from_last_access_time(input_path_meta); let atime = filetime::FileTime::from_last_access_time(input_path_meta);
let mtime = filetime::FileTime::from_last_modification_time(input_path_meta); let mtime = filetime::FileTime::from_last_modification_time(input_path_meta);
debug!( trace!(
"attempting to set file times: atime: {:?}, mtime: {:?}", "attempting to set file times: atime: {:?}, mtime: {:?}",
atime, mtime atime,
mtime
); );
filetime::set_file_times(out_path, atime, mtime).map_err(|err_io| { filetime::set_file_times(out_path, atime, mtime).map_err(|err_io| {
PngError::new(&format!( PngError::new(&format!(

View file

@ -13,7 +13,7 @@
#![warn(clippy::range_plus_one)] #![warn(clippy::range_plus_one)]
#![allow(clippy::cognitive_complexity)] #![allow(clippy::cognitive_complexity)]
use clap::{AppSettings, Arg, ArgMatches, Command}; use clap::{AppSettings, Arg, ArgAction, ArgMatches, Command};
use indexmap::IndexSet; use indexmap::IndexSet;
use log::{error, warn}; use log::{error, warn};
use oxipng::Deflaters; use oxipng::Deflaters;
@ -154,9 +154,10 @@ fn main() {
) )
.arg( .arg(
Arg::new("verbose") Arg::new("verbose")
.help("Run in verbose mode") .help("Run in verbose mode (use multiple times to increase verbosity)")
.short('v') .short('v')
.long("verbose") .long("verbose")
.action(ArgAction::Count)
.conflicts_with("quiet"), .conflicts_with("quiet"),
) )
.arg( .arg(
@ -381,7 +382,7 @@ fn parse_opts_into_struct(
stderrlog::new() stderrlog::new()
.module(module_path!()) .module(module_path!())
.quiet(matches.is_present("quiet")) .quiet(matches.is_present("quiet"))
.verbosity(if matches.is_present("verbose") { 3 } else { 2 }) .verbosity(matches.get_count("verbose") as usize + 2)
.show_level(false) .show_level(false)
.init() .init()
.unwrap(); .unwrap();

View file

@ -180,34 +180,26 @@ fn verbose_mode() {
}); });
let logs: Vec<_> = receiver.into_iter().collect(); let logs: Vec<_> = receiver.into_iter().collect();
println!("logs={:?}", logs); let expected_prefixes = [
assert_eq!(logs.len(), 9);
let expected_logs = [
" 500x400 pixels, PNG format", " 500x400 pixels, PNG format",
" 3x8 bits/pixel, RGB (non-interlaced)", " 8-bit RGB, non-interlaced",
" IDAT size = 113794 bytes", " IDAT size = 113794 bytes",
" File size = 114708 bytes", " File size = 114708 bytes",
"Trying: 1 filters", "Trying: 1 filters",
" zc = 11 f = None 149409 bytes",
"Found better combination:", "Found better combination:",
" zc = 11 f = None 149409 bytes", " zc = 11 f = None ",
" IDAT size = 149409 bytes", " IDAT size = ",
]; ];
for (idx, expected_log) in expected_logs.into_iter().enumerate() { assert_eq!(logs.len(), expected_prefixes.len());
if let Some(log) = logs.get(idx) { for (i, log) in logs.into_iter().enumerate() {
if !log.starts_with(expected_log) { let expected_prefix = expected_prefixes[i];
panic!( assert!(
"logs[{}] = {:?} doesn't start with {:?}", log.starts_with(&expected_prefix),
idx, log, expected_log "logs[{}] = {:?} doesn't start with {:?}",
); i,
} log,
} else { expected_prefix
panic!( );
"Expected to find {} log entries, but got {}",
expected_logs.len(),
logs.len()
);
}
} }
} }