Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
This commit is contained in:
198
vendor/png/examples/corpus-bench.rs
vendored
Normal file
198
vendor/png/examples/corpus-bench.rs
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
use std::{fs, path::PathBuf};
|
||||
|
||||
use clap::Parser;
|
||||
use png::Decoder;
|
||||
|
||||
#[derive(clap::ValueEnum, Clone)]
|
||||
enum Speed {
|
||||
Fast,
|
||||
Default,
|
||||
Best,
|
||||
}
|
||||
|
||||
#[derive(clap::ValueEnum, Clone)]
|
||||
enum Filter {
|
||||
None,
|
||||
Sub,
|
||||
Up,
|
||||
Average,
|
||||
Paeth,
|
||||
Adaptive,
|
||||
}
|
||||
|
||||
#[derive(clap::Parser)]
|
||||
struct Args {
|
||||
directory: Option<PathBuf>,
|
||||
#[clap(short, long, value_enum, default_value_t = Speed::Fast)]
|
||||
speed: Speed,
|
||||
#[clap(short, long, value_enum, default_value_t = Filter::Adaptive)]
|
||||
filter: Filter,
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn run_encode(
|
||||
args: &Args,
|
||||
dimensions: (u32, u32),
|
||||
color_type: png::ColorType,
|
||||
bit_depth: png::BitDepth,
|
||||
image: &[u8],
|
||||
) -> Vec<u8> {
|
||||
let mut reencoded = Vec::new();
|
||||
let mut encoder = png::Encoder::new(&mut reencoded, dimensions.0, dimensions.1);
|
||||
encoder.set_color(color_type);
|
||||
encoder.set_depth(bit_depth);
|
||||
encoder.set_compression(match args.speed {
|
||||
Speed::Fast => png::Compression::Fast,
|
||||
Speed::Default => png::Compression::Default,
|
||||
Speed::Best => png::Compression::Best,
|
||||
});
|
||||
encoder.set_filter(match args.filter {
|
||||
Filter::None => png::FilterType::NoFilter,
|
||||
Filter::Sub => png::FilterType::Sub,
|
||||
Filter::Up => png::FilterType::Up,
|
||||
Filter::Average => png::FilterType::Avg,
|
||||
Filter::Paeth => png::FilterType::Paeth,
|
||||
Filter::Adaptive => png::FilterType::Paeth,
|
||||
});
|
||||
encoder.set_adaptive_filter(match args.filter {
|
||||
Filter::Adaptive => png::AdaptiveFilterType::Adaptive,
|
||||
_ => png::AdaptiveFilterType::NonAdaptive,
|
||||
});
|
||||
let mut encoder = encoder.write_header().unwrap();
|
||||
encoder.write_image_data(&image).unwrap();
|
||||
encoder.finish().unwrap();
|
||||
reencoded
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn run_decode(image: &[u8], output: &mut [u8]) {
|
||||
let mut reader = Decoder::new(image).read_info().unwrap();
|
||||
reader.next_frame(output).unwrap();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut total_uncompressed = 0;
|
||||
let mut total_compressed = 0;
|
||||
let mut total_pixels = 0;
|
||||
let mut total_encode_time = 0;
|
||||
let mut total_decode_time = 0;
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
println!(
|
||||
"{:45} Ratio Encode Decode",
|
||||
"Directory"
|
||||
);
|
||||
println!(
|
||||
"{:45}------- -------------------- --------------------",
|
||||
"---------"
|
||||
);
|
||||
|
||||
let mut image2 = Vec::new();
|
||||
|
||||
let mut pending = vec![args.directory.clone().unwrap_or(PathBuf::from("."))];
|
||||
while let Some(directory) = pending.pop() {
|
||||
let mut dir_uncompressed = 0;
|
||||
let mut dir_compressed = 0;
|
||||
let mut dir_pixels = 0;
|
||||
let mut dir_encode_time = 0;
|
||||
let mut dir_decode_time = 0;
|
||||
|
||||
for entry in fs::read_dir(&directory).unwrap().flatten() {
|
||||
if entry.file_type().unwrap().is_dir() {
|
||||
pending.push(entry.path());
|
||||
continue;
|
||||
}
|
||||
|
||||
match entry.path().extension() {
|
||||
Some(st) if st == "png" => {}
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
// Parse
|
||||
let data = fs::read(entry.path()).unwrap();
|
||||
let mut decoder = Decoder::new(&*data);
|
||||
if decoder.read_header_info().ok().map(|h| h.color_type)
|
||||
== Some(png::ColorType::Indexed)
|
||||
{
|
||||
decoder.set_transformations(
|
||||
png::Transformations::EXPAND | png::Transformations::STRIP_16,
|
||||
);
|
||||
}
|
||||
let mut reader = match decoder.read_info() {
|
||||
Ok(reader) => reader,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let mut image = vec![0; reader.output_buffer_size()];
|
||||
let info = match reader.next_frame(&mut image) {
|
||||
Ok(info) => info,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let (width, height) = (info.width, info.height);
|
||||
let bit_depth = info.bit_depth;
|
||||
let mut color_type = info.color_type;
|
||||
|
||||
// qoibench expands grayscale to RGB, so we do the same.
|
||||
if bit_depth == png::BitDepth::Eight {
|
||||
if color_type == png::ColorType::Grayscale {
|
||||
image = image.into_iter().flat_map(|v| [v, v, v, 255]).collect();
|
||||
color_type = png::ColorType::Rgba;
|
||||
} else if color_type == png::ColorType::GrayscaleAlpha {
|
||||
image = image
|
||||
.chunks_exact(2)
|
||||
.flat_map(|v| [v[0], v[0], v[0], v[1]])
|
||||
.collect();
|
||||
color_type = png::ColorType::Rgba;
|
||||
}
|
||||
}
|
||||
|
||||
// Re-encode
|
||||
let start = std::time::Instant::now();
|
||||
let reencoded = run_encode(&args, (width, height), color_type, bit_depth, &image);
|
||||
let elapsed = start.elapsed().as_nanos() as u64;
|
||||
|
||||
// And decode again
|
||||
image2.resize(image.len(), 0);
|
||||
let start2 = std::time::Instant::now();
|
||||
run_decode(&reencoded, &mut image2);
|
||||
let elapsed2 = start2.elapsed().as_nanos() as u64;
|
||||
|
||||
assert_eq!(image, image2);
|
||||
|
||||
// Stats
|
||||
dir_uncompressed += image.len();
|
||||
dir_compressed += reencoded.len();
|
||||
dir_pixels += (width * height) as u64;
|
||||
dir_encode_time += elapsed;
|
||||
dir_decode_time += elapsed2;
|
||||
}
|
||||
if dir_uncompressed > 0 {
|
||||
println!(
|
||||
"{:45}{:6.2}%{:8} mps {:6.2} GiB/s {:8} mps {:6.2} GiB/s",
|
||||
directory.display(),
|
||||
100.0 * dir_compressed as f64 / dir_uncompressed as f64,
|
||||
dir_pixels * 1000 / dir_encode_time,
|
||||
dir_uncompressed as f64 / (dir_encode_time as f64 * 1e-9 * (1 << 30) as f64),
|
||||
dir_pixels * 1000 / dir_decode_time,
|
||||
dir_uncompressed as f64 / (dir_decode_time as f64 * 1e-9 * (1 << 30) as f64)
|
||||
);
|
||||
}
|
||||
|
||||
total_uncompressed += dir_uncompressed;
|
||||
total_compressed += dir_compressed;
|
||||
total_pixels += dir_pixels;
|
||||
total_encode_time += dir_encode_time;
|
||||
total_decode_time += dir_decode_time;
|
||||
}
|
||||
|
||||
println!();
|
||||
println!(
|
||||
"{:44}{:7.3}%{:8} mps {:6.3} GiB/s {:8} mps {:6.3} GiB/s",
|
||||
"Total",
|
||||
100.0 * total_compressed as f64 / total_uncompressed as f64,
|
||||
total_pixels * 1000 / total_encode_time,
|
||||
total_uncompressed as f64 / (total_encode_time as f64 * 1e-9 * (1 << 30) as f64),
|
||||
total_pixels * 1000 / total_decode_time,
|
||||
total_uncompressed as f64 / (total_decode_time as f64 * 1e-9 * (1 << 30) as f64)
|
||||
);
|
||||
}
|
55
vendor/png/examples/png-generate.rs
vendored
Normal file
55
vendor/png/examples/png-generate.rs
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// For reading and opening files
|
||||
use png::text_metadata::{ITXtChunk, ZTXtChunk};
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::BufWriter;
|
||||
|
||||
fn main() {
|
||||
let path = env::args()
|
||||
.nth(1)
|
||||
.expect("Expected a filename to output to.");
|
||||
let file = File::create(path).unwrap();
|
||||
let w = &mut BufWriter::new(file);
|
||||
|
||||
let mut encoder = png::Encoder::new(w, 2, 1); // Width is 2 pixels and height is 1.
|
||||
encoder.set_color(png::ColorType::Rgba);
|
||||
encoder.set_depth(png::BitDepth::Eight);
|
||||
// Adding text chunks to the header
|
||||
encoder
|
||||
.add_text_chunk(
|
||||
"Testing tEXt".to_string(),
|
||||
"This is a tEXt chunk that will appear before the IDAT chunks.".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
encoder
|
||||
.add_ztxt_chunk(
|
||||
"Testing zTXt".to_string(),
|
||||
"This is a zTXt chunk that is compressed in the png file.".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
encoder
|
||||
.add_itxt_chunk(
|
||||
"Testing iTXt".to_string(),
|
||||
"iTXt chunks support all of UTF8. Example: हिंदी.".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut writer = encoder.write_header().unwrap();
|
||||
|
||||
let data = [255, 0, 0, 255, 0, 0, 0, 255]; // An array containing a RGBA sequence. First pixel is red and second pixel is black.
|
||||
writer.write_image_data(&data).unwrap(); // Save
|
||||
|
||||
// We can add a tEXt/zTXt/iTXt at any point before the encoder is dropped from scope. These chunks will be at the end of the png file.
|
||||
let tail_ztxt_chunk = ZTXtChunk::new(
|
||||
"Comment".to_string(),
|
||||
"A zTXt chunk after the image data.".to_string(),
|
||||
);
|
||||
writer.write_text_chunk(&tail_ztxt_chunk).unwrap();
|
||||
|
||||
// The fields of the text chunk are public, so they can be mutated before being written to the file.
|
||||
let mut tail_itxt_chunk = ITXtChunk::new("Author".to_string(), "सायंतन खान".to_string());
|
||||
tail_itxt_chunk.compressed = true;
|
||||
tail_itxt_chunk.language_tag = "hi".to_string();
|
||||
tail_itxt_chunk.translated_keyword = "लेखक".to_string();
|
||||
writer.write_text_chunk(&tail_itxt_chunk).unwrap();
|
||||
}
|
381
vendor/png/examples/pngcheck.rs
vendored
Normal file
381
vendor/png/examples/pngcheck.rs
vendored
Normal file
@ -0,0 +1,381 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
extern crate getopts;
|
||||
extern crate glob;
|
||||
extern crate png;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::path::Path;
|
||||
|
||||
use getopts::{Matches, Options, ParsingStyle};
|
||||
use term::{color, Attr};
|
||||
|
||||
fn parse_args() -> Matches {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let mut opts = Options::new();
|
||||
opts.optflag("c", "", "colorize output (for ANSI terminals)")
|
||||
.optflag("q", "", "test quietly (output only errors)")
|
||||
.optflag(
|
||||
"t",
|
||||
"",
|
||||
"print contents of tEXt/zTXt/iTXt chunks (can be used with -q)",
|
||||
)
|
||||
.optflag("v", "", "test verbosely (print most chunk data)")
|
||||
.parsing_style(ParsingStyle::StopAtFirstFree);
|
||||
if args.len() > 1 {
|
||||
match opts.parse(&args[1..]) {
|
||||
Ok(matches) => return matches,
|
||||
Err(err) => println!("{}", err),
|
||||
}
|
||||
}
|
||||
println!("{}", opts.usage("Usage: pngcheck [-cpt] [file ...]"));
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Config {
|
||||
quiet: bool,
|
||||
verbose: bool,
|
||||
color: bool,
|
||||
text: bool,
|
||||
}
|
||||
|
||||
fn display_interlaced(i: bool) -> &'static str {
|
||||
if i {
|
||||
"interlaced"
|
||||
} else {
|
||||
"non-interlaced"
|
||||
}
|
||||
}
|
||||
|
||||
fn display_image_type(bits: u8, color: png::ColorType) -> String {
|
||||
use png::ColorType::*;
|
||||
format!(
|
||||
"{}-bit {}",
|
||||
bits,
|
||||
match color {
|
||||
Grayscale => "grayscale",
|
||||
Rgb => "RGB",
|
||||
Indexed => "palette",
|
||||
GrayscaleAlpha => "grayscale+alpha",
|
||||
Rgba => "RGB+alpha",
|
||||
}
|
||||
)
|
||||
}
|
||||
// channels after expansion of tRNS
|
||||
fn final_channels(c: png::ColorType, trns: bool) -> u8 {
|
||||
use png::ColorType::*;
|
||||
match c {
|
||||
Grayscale => 1 + u8::from(trns),
|
||||
Rgb => 3,
|
||||
Indexed => 3 + u8::from(trns),
|
||||
GrayscaleAlpha => 2,
|
||||
Rgba => 4,
|
||||
}
|
||||
}
|
||||
fn check_image<P: AsRef<Path>>(c: Config, fname: P) -> io::Result<()> {
|
||||
// TODO improve performance by resusing allocations from decoder
|
||||
use png::Decoded::*;
|
||||
let mut t = term::stdout()
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "could not open terminal"))?;
|
||||
let data = &mut vec![0; 10 * 1024][..];
|
||||
let mut reader = io::BufReader::new(File::open(&fname)?);
|
||||
let fname = fname.as_ref().to_string_lossy();
|
||||
let n = reader.read(data)?;
|
||||
let mut buf = &data[..n];
|
||||
let mut pos = 0;
|
||||
let mut decoder = png::StreamingDecoder::new();
|
||||
// Image data
|
||||
let mut width = 0;
|
||||
let mut height = 0;
|
||||
let mut color = png::ColorType::Grayscale;
|
||||
let mut bits = 0;
|
||||
let mut trns = false;
|
||||
let mut interlaced = false;
|
||||
let mut compressed_size = 0;
|
||||
let mut n_chunks = 0;
|
||||
let mut have_idat = false;
|
||||
macro_rules! c_ratio(
|
||||
// TODO add palette entries to compressed_size
|
||||
() => ({
|
||||
compressed_size as f32/(
|
||||
height as u64 *
|
||||
(width as u64 * final_channels(color, trns) as u64 * bits as u64 + 7)>>3
|
||||
) as f32
|
||||
});
|
||||
);
|
||||
let display_error = |err| -> Result<_, io::Error> {
|
||||
let mut t = term::stdout()
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "could not open terminal"))?;
|
||||
if c.verbose {
|
||||
if c.color {
|
||||
print!(": ");
|
||||
t.fg(color::RED)?;
|
||||
writeln!(t, "{}", err)?;
|
||||
t.attr(Attr::Bold)?;
|
||||
write!(t, "ERRORS DETECTED")?;
|
||||
t.reset()?;
|
||||
} else {
|
||||
println!(": {}", err);
|
||||
print!("ERRORS DETECTED")
|
||||
}
|
||||
println!(" in {}", fname);
|
||||
} else {
|
||||
if !c.quiet {
|
||||
if c.color {
|
||||
t.fg(color::RED)?;
|
||||
t.attr(Attr::Bold)?;
|
||||
write!(t, "ERROR")?;
|
||||
t.reset()?;
|
||||
write!(t, ": ")?;
|
||||
t.fg(color::YELLOW)?;
|
||||
writeln!(t, "{}", fname)?;
|
||||
t.reset()?;
|
||||
} else {
|
||||
println!("ERROR: {}", fname)
|
||||
}
|
||||
}
|
||||
print!("{}: ", fname);
|
||||
if c.color {
|
||||
t.fg(color::RED)?;
|
||||
writeln!(t, "{}", err)?;
|
||||
t.reset()?;
|
||||
} else {
|
||||
println!("{}", err);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
if c.verbose {
|
||||
print!("File: ");
|
||||
if c.color {
|
||||
t.attr(Attr::Bold)?;
|
||||
write!(t, "{}", fname)?;
|
||||
t.reset()?;
|
||||
} else {
|
||||
print!("{}", fname);
|
||||
}
|
||||
print!(" ({}) bytes", data.len())
|
||||
}
|
||||
loop {
|
||||
if buf.is_empty() {
|
||||
// circumvent borrow checker
|
||||
assert!(!data.is_empty());
|
||||
let n = reader.read(data)?;
|
||||
|
||||
// EOF
|
||||
if n == 0 {
|
||||
println!("ERROR: premature end of file {}", fname);
|
||||
break;
|
||||
}
|
||||
buf = &data[..n];
|
||||
}
|
||||
match decoder.update(buf, &mut Vec::new()) {
|
||||
Ok((_, ImageEnd)) => {
|
||||
if !have_idat {
|
||||
// This isn't beautiful. But it works.
|
||||
display_error(png::DecodingError::IoError(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"IDAT chunk missing",
|
||||
)))?;
|
||||
break;
|
||||
}
|
||||
if !c.verbose && !c.quiet {
|
||||
if c.color {
|
||||
t.fg(color::GREEN)?;
|
||||
t.attr(Attr::Bold)?;
|
||||
write!(t, "OK")?;
|
||||
t.reset()?;
|
||||
write!(t, ": ")?;
|
||||
t.fg(color::YELLOW)?;
|
||||
write!(t, "{}", fname)?;
|
||||
t.reset()?;
|
||||
} else {
|
||||
print!("OK: {}", fname)
|
||||
}
|
||||
println!(
|
||||
" ({}x{}, {}{}, {}, {:.1}%)",
|
||||
width,
|
||||
height,
|
||||
display_image_type(bits, color),
|
||||
(if trns { "+trns" } else { "" }),
|
||||
display_interlaced(interlaced),
|
||||
100.0 * (1.0 - c_ratio!())
|
||||
)
|
||||
} else if !c.quiet {
|
||||
println!();
|
||||
if c.color {
|
||||
t.fg(color::GREEN)?;
|
||||
t.attr(Attr::Bold)?;
|
||||
write!(t, "No errors detected ")?;
|
||||
t.reset()?;
|
||||
} else {
|
||||
print!("No errors detected ");
|
||||
}
|
||||
println!(
|
||||
"in {} ({} chunks, {:.1}% compression)",
|
||||
fname,
|
||||
n_chunks,
|
||||
100.0 * (1.0 - c_ratio!()),
|
||||
)
|
||||
}
|
||||
break;
|
||||
}
|
||||
Ok((n, res)) => {
|
||||
buf = &buf[n..];
|
||||
pos += n;
|
||||
match res {
|
||||
Header(w, h, b, c, i) => {
|
||||
width = w;
|
||||
height = h;
|
||||
bits = b as u8;
|
||||
color = c;
|
||||
interlaced = i;
|
||||
}
|
||||
ChunkBegin(len, type_str) => {
|
||||
use png::chunk;
|
||||
n_chunks += 1;
|
||||
if c.verbose {
|
||||
let chunk = type_str;
|
||||
println!();
|
||||
print!(" chunk ");
|
||||
if c.color {
|
||||
t.fg(color::YELLOW)?;
|
||||
write!(t, "{:?}", chunk)?;
|
||||
t.reset()?;
|
||||
} else {
|
||||
print!("{:?}", chunk)
|
||||
}
|
||||
print!(
|
||||
" at offset {:#07x}, length {}",
|
||||
pos - 4, // substract chunk name length
|
||||
len
|
||||
)
|
||||
}
|
||||
match type_str {
|
||||
chunk::IDAT => {
|
||||
have_idat = true;
|
||||
compressed_size += len
|
||||
}
|
||||
chunk::tRNS => {
|
||||
trns = true;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
ImageData => {
|
||||
//println!("got {} bytes of image data", data.len())
|
||||
}
|
||||
ChunkComplete(_, type_str) if c.verbose => {
|
||||
use png::chunk::*;
|
||||
if type_str == IHDR {
|
||||
println!();
|
||||
print!(
|
||||
" {} x {} image, {}{}, {}",
|
||||
width,
|
||||
height,
|
||||
display_image_type(bits, color),
|
||||
(if trns { "+trns" } else { "" }),
|
||||
display_interlaced(interlaced),
|
||||
);
|
||||
}
|
||||
}
|
||||
AnimationControl(actl) => {
|
||||
println!();
|
||||
print!(" {} frames, {} plays", actl.num_frames, actl.num_plays,);
|
||||
}
|
||||
FrameControl(fctl) => {
|
||||
println!();
|
||||
println!(
|
||||
" sequence #{}, {} x {} pixels @ ({}, {})",
|
||||
fctl.sequence_number,
|
||||
fctl.width,
|
||||
fctl.height,
|
||||
fctl.x_offset,
|
||||
fctl.y_offset,
|
||||
/*fctl.delay_num,
|
||||
fctl.delay_den,
|
||||
fctl.dispose_op,
|
||||
fctl.blend_op,*/
|
||||
);
|
||||
print!(
|
||||
" {}/{} s delay, dispose: {}, blend: {}",
|
||||
fctl.delay_num,
|
||||
if fctl.delay_den == 0 {
|
||||
100
|
||||
} else {
|
||||
fctl.delay_den
|
||||
},
|
||||
fctl.dispose_op,
|
||||
fctl.blend_op,
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
//println!("{} {:?}", n, res)
|
||||
}
|
||||
Err(err) => {
|
||||
let _ = display_error(err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.text {
|
||||
println!("Parsed tEXt chunks:");
|
||||
for text_chunk in &decoder.info().unwrap().uncompressed_latin1_text {
|
||||
println!("{:#?}", text_chunk);
|
||||
}
|
||||
|
||||
println!("Parsed zTXt chunks:");
|
||||
for text_chunk in &decoder.info().unwrap().compressed_latin1_text {
|
||||
let mut cloned_text_chunk = text_chunk.clone();
|
||||
cloned_text_chunk.decompress_text()?;
|
||||
println!("{:#?}", cloned_text_chunk);
|
||||
}
|
||||
|
||||
println!("Parsed iTXt chunks:");
|
||||
for text_chunk in &decoder.info().unwrap().utf8_text {
|
||||
let mut cloned_text_chunk = text_chunk.clone();
|
||||
cloned_text_chunk.decompress_text()?;
|
||||
println!("{:#?}", cloned_text_chunk);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let m = parse_args();
|
||||
|
||||
let config = Config {
|
||||
quiet: m.opt_present("q"),
|
||||
verbose: m.opt_present("v"),
|
||||
color: m.opt_present("c"),
|
||||
text: m.opt_present("t"),
|
||||
};
|
||||
|
||||
for file in m.free {
|
||||
let result = if file.contains('*') {
|
||||
glob::glob(&file)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
|
||||
.and_then(|mut glob| {
|
||||
glob.try_for_each(|entry| {
|
||||
entry
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
|
||||
.and_then(|file| check_image(config, file))
|
||||
})
|
||||
})
|
||||
} else {
|
||||
check_image(config, &file)
|
||||
};
|
||||
|
||||
result.unwrap_or_else(|err| {
|
||||
println!("{}: {}", file, err);
|
||||
std::process::exit(1)
|
||||
});
|
||||
}
|
||||
}
|
198
vendor/png/examples/show.rs
vendored
Normal file
198
vendor/png/examples/show.rs
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
use glium::{
|
||||
backend::glutin::Display,
|
||||
glutin::{
|
||||
self, dpi,
|
||||
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
|
||||
event_loop::ControlFlow,
|
||||
},
|
||||
texture::{ClientFormat, RawImage2d},
|
||||
BlitTarget, Rect, Surface,
|
||||
};
|
||||
use std::{borrow::Cow, env, fs::File, io, path};
|
||||
|
||||
/// Load the image using `png`
|
||||
fn load_image(path: &path::PathBuf) -> io::Result<RawImage2d<'static, u8>> {
|
||||
use png::ColorType::*;
|
||||
let mut decoder = png::Decoder::new(File::open(path)?);
|
||||
decoder.set_transformations(png::Transformations::normalize_to_color8());
|
||||
let mut reader = decoder.read_info()?;
|
||||
let mut img_data = vec![0; reader.output_buffer_size()];
|
||||
let info = reader.next_frame(&mut img_data)?;
|
||||
|
||||
let (data, format) = match info.color_type {
|
||||
Rgb => (img_data, ClientFormat::U8U8U8),
|
||||
Rgba => (img_data, ClientFormat::U8U8U8U8),
|
||||
Grayscale => (
|
||||
{
|
||||
let mut vec = Vec::with_capacity(img_data.len() * 3);
|
||||
for g in img_data {
|
||||
vec.extend([g, g, g].iter().cloned())
|
||||
}
|
||||
vec
|
||||
},
|
||||
ClientFormat::U8U8U8,
|
||||
),
|
||||
GrayscaleAlpha => (
|
||||
{
|
||||
let mut vec = Vec::with_capacity(img_data.len() * 3);
|
||||
for ga in img_data.chunks(2) {
|
||||
let g = ga[0];
|
||||
let a = ga[1];
|
||||
vec.extend([g, g, g, a].iter().cloned())
|
||||
}
|
||||
vec
|
||||
},
|
||||
ClientFormat::U8U8U8U8,
|
||||
),
|
||||
_ => unreachable!("uncovered color type"),
|
||||
};
|
||||
|
||||
Ok(RawImage2d {
|
||||
data: Cow::Owned(data),
|
||||
width: info.width,
|
||||
height: info.height,
|
||||
format,
|
||||
})
|
||||
}
|
||||
|
||||
fn main_loop(files: Vec<path::PathBuf>) -> io::Result<()> {
|
||||
let mut files = files.into_iter();
|
||||
let image = load_image(&files.next().unwrap())?;
|
||||
|
||||
let event_loop = glutin::event_loop::EventLoop::new();
|
||||
let window_builder = glutin::window::WindowBuilder::new().with_title("Show Example");
|
||||
let context_builder = glutin::ContextBuilder::new().with_vsync(true);
|
||||
let display = glium::Display::new(window_builder, context_builder, &event_loop)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
|
||||
resize_window(&display, &image);
|
||||
let mut texture = glium::Texture2d::new(&display, image).unwrap();
|
||||
draw(&display, &texture);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => exit(control_flow),
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
state: ElementState::Pressed,
|
||||
virtual_keycode: code,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match code {
|
||||
Some(VirtualKeyCode::Escape) => exit(control_flow),
|
||||
Some(VirtualKeyCode::Right) => match &files.next() {
|
||||
Some(path) => {
|
||||
match load_image(path) {
|
||||
Ok(image) => {
|
||||
resize_window(&display, &image);
|
||||
texture = glium::Texture2d::new(&display, image).unwrap();
|
||||
draw(&display, &texture);
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Error: {}", err);
|
||||
exit(control_flow);
|
||||
}
|
||||
};
|
||||
}
|
||||
None => exit(control_flow),
|
||||
},
|
||||
_ => {}
|
||||
},
|
||||
Event::RedrawRequested(_) => draw(&display, &texture),
|
||||
_ => {}
|
||||
});
|
||||
}
|
||||
|
||||
fn draw(display: &glium::Display, texture: &glium::Texture2d) {
|
||||
let frame = display.draw();
|
||||
fill_v_flipped(
|
||||
&texture.as_surface(),
|
||||
&frame,
|
||||
glium::uniforms::MagnifySamplerFilter::Linear,
|
||||
);
|
||||
frame.finish().unwrap();
|
||||
}
|
||||
|
||||
fn exit(control_flow: &mut ControlFlow) {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
|
||||
fn fill_v_flipped<S1, S2>(src: &S1, target: &S2, filter: glium::uniforms::MagnifySamplerFilter)
|
||||
where
|
||||
S1: Surface,
|
||||
S2: Surface,
|
||||
{
|
||||
let src_dim = src.get_dimensions();
|
||||
let src_rect = Rect {
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
width: src_dim.0 as u32,
|
||||
height: src_dim.1 as u32,
|
||||
};
|
||||
let target_dim = target.get_dimensions();
|
||||
let target_rect = BlitTarget {
|
||||
left: 0,
|
||||
bottom: target_dim.1,
|
||||
width: target_dim.0 as i32,
|
||||
height: -(target_dim.1 as i32),
|
||||
};
|
||||
src.blit_color(&src_rect, target, &target_rect, filter);
|
||||
}
|
||||
|
||||
fn resize_window(display: &Display, image: &RawImage2d<'static, u8>) {
|
||||
let mut width = image.width;
|
||||
let mut height = image.height;
|
||||
if width < 50 && height < 50 {
|
||||
width *= 10;
|
||||
height *= 10;
|
||||
}
|
||||
display
|
||||
.gl_window()
|
||||
.window()
|
||||
.set_inner_size(dpi::LogicalSize::new(f64::from(width), f64::from(height)));
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() < 2 {
|
||||
println!("Usage: show files [...]");
|
||||
} else {
|
||||
let mut files = vec![];
|
||||
for file in args.iter().skip(1) {
|
||||
match if file.contains('*') {
|
||||
(|| -> io::Result<_> {
|
||||
for entry in glob::glob(file)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err.msg))?
|
||||
{
|
||||
files.push(
|
||||
entry
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::Other, "glob error"))?,
|
||||
)
|
||||
}
|
||||
Ok(())
|
||||
})()
|
||||
} else {
|
||||
files.push(path::PathBuf::from(file));
|
||||
Ok(())
|
||||
} {
|
||||
Ok(_) => (),
|
||||
Err(err) => {
|
||||
println!("{}: {}", file, err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// "tests/pngsuite/pngsuite.png"
|
||||
match main_loop(files) {
|
||||
Ok(_) => (),
|
||||
Err(err) => println!("Error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user