qoi-bench: rework png decoding, convert grayscale

This commit is contained in:
Ivan Smirnov 2022-01-05 17:10:42 +03:00
parent 81bd8abdbe
commit 497d188f9a
2 changed files with 51 additions and 12 deletions

View file

@ -12,6 +12,7 @@ qoi-fast = { path = ".." }
# external # external
anyhow = "1.0" anyhow = "1.0"
libc = "0.2" libc = "0.2"
bytemuck = "1.7"
png = "0.17" png = "0.17"
structopt = "0.3" structopt = "0.3"
walkdir = "2.3" walkdir = "2.3"

View file

@ -6,6 +6,7 @@ use std::time::{Duration, Instant};
use anyhow::{bail, ensure, Context, Result}; use anyhow::{bail, ensure, Context, Result};
use libc::{c_int, c_void}; use libc::{c_int, c_void};
use bytemuck::cast_slice;
use structopt::StructOpt; use structopt::StructOpt;
use walkdir::{DirEntry, WalkDir}; use walkdir::{DirEntry, WalkDir};
@ -72,6 +73,27 @@ fn find_pngs(paths: &[PathBuf]) -> Result<Vec<PathBuf>> {
Ok(out) Ok(out)
} }
fn grayscale_to_rgb(buf: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(buf.len() * 3);
for &px in buf {
for _ in 0..3 {
out.push(px);
}
}
out
}
fn grayscale_alpha_to_rgba(buf: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(buf.len() * 4);
for &px in cast_slice::<_, [u8; 2]>(buf) {
for _ in 0..3 {
out.push(px[0]);
}
out.push(px[1])
}
out
}
#[derive(Clone)] #[derive(Clone)]
struct Image { struct Image {
pub width: u32, pub width: u32,
@ -81,22 +103,38 @@ struct Image {
} }
impl Image { impl Image {
fn read_png(filename: &Path) -> Result<Self> {
let mut decoder = png::Decoder::new(File::open(filename)?);
let transformations = png::Transformations::normalize_to_color8();
decoder.set_transformations(transformations);
let mut reader = decoder.read_info()?;
let mut whole_buf = vec![0; reader.output_buffer_size()];
let info = reader.next_frame(&mut whole_buf)?;
let buf = &whole_buf[..info.buffer_size()];
ensure!(info.bit_depth == png::BitDepth::Eight, "invalid bit depth: {:?}", info.bit_depth);
let (channels, data) = match info.color_type {
png::ColorType::Grayscale => {
// png crate doesn't support GRAY_TO_RGB transformation yet
(3, grayscale_to_rgb(buf))
}
png::ColorType::GrayscaleAlpha => {
// same as above, but with alpha channel
(4, grayscale_alpha_to_rgba(buf))
}
color_type => {
let channels = color_type.samples();
ensure!(channels == 3 || channels == 4, "invalid channels: {}", channels);
(channels as u8, buf[..info.buffer_size()].to_vec())
}
};
Ok(Self { width: info.width, height: info.height, channels, data })
}
pub const fn n_pixels(&self) -> usize { pub const fn n_pixels(&self) -> usize {
(self.width as usize) * (self.height as usize) (self.width as usize) * (self.height as usize)
} }
} }
fn read_png(filename: &Path) -> Result<Image> {
let decoder = png::Decoder::new(File::open(filename)?);
let mut reader = decoder.read_info()?;
let mut buf = vec![0; reader.output_buffer_size()];
let info = reader.next_frame(&mut buf)?;
let bytes = &buf[..info.buffer_size()];
let channels = info.color_type.samples() as u8;
ensure!(channels == 3 || channels == 4, "invalid channels: {}", channels);
Ok(Image { width: info.width, height: info.height, channels, data: bytes.to_vec() })
}
trait Codec { trait Codec {
fn name() -> &'static str; fn name() -> &'static str;
@ -360,7 +398,7 @@ impl BenchTotals {
fn bench_png(filename: &Path, seconds: f64, use_median: bool) -> Result<ImageBench> { fn bench_png(filename: &Path, seconds: f64, use_median: bool) -> Result<ImageBench> {
let f = filename.to_string_lossy(); let f = filename.to_string_lossy();
let img = read_png(filename).context(format!("error reading PNG file: {}", f))?; let img = Image::read_png(filename).context(format!("error reading PNG file: {}", f))?;
let size_kb = fs::metadata(filename)?.len() / 1024; let size_kb = fs::metadata(filename)?.len() / 1024;
let mpixels = img.n_pixels() as f64 / 1e6; let mpixels = img.n_pixels() as f64 / 1e6;
println!( println!(