Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

53
vendor/png/examples/change-png-info.rs vendored Normal file
View File

@@ -0,0 +1,53 @@
/// Tests "editing"/re-encoding of an image:
/// decoding, editing, re-encoding
use std::fs::File;
use std::io::{BufReader, BufWriter};
use std::path::Path;
use png::DecodingError::LimitsExceeded;
pub type BoxResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
fn main() -> BoxResult<()> {
// # Decode
// Read test image from pngsuite
let path_in = Path::new(r"./tests/pngsuite/basi0g01.png");
// The decoder is a build for reader and can be used to set various decoding options
// via `Transformations`. The default output transformation is `Transformations::IDENTITY`.
let decoder = png::Decoder::new(BufReader::new(File::open(path_in)?));
let mut reader = decoder.read_info()?;
// Allocate the output buffer.
let png_info = reader.info();
let mut buf = vec![0; reader.output_buffer_size().ok_or(LimitsExceeded)?];
println!("{png_info:?}");
// # Encode
let path_out = Path::new(r"./target/test_modified.png");
let file = File::create(path_out)?;
let ref mut w = BufWriter::new(file);
// Get defaults for interlaced parameter.
let mut info_out = png_info.clone();
let info_default = png::Info::default();
// Edit previous info
info_out.interlaced = info_default.interlaced;
let mut encoder = png::Encoder::with_info(w, info_out)?;
encoder.set_depth(png_info.bit_depth);
// Edit some attribute
encoder.add_text_chunk(
"Testing tEXt".to_string(),
"This is a tEXt chunk that will appear before the IDAT chunks.".to_string(),
)?;
// Save picture with changed info
let mut writer = encoder.write_header()?;
let mut counter = 0u8;
while let Ok(info) = reader.next_frame(&mut buf) {
let bytes = &buf[..info.buffer_size()];
writer.write_image_data(&bytes)?;
counter += 1;
println!("Written frame: {}", counter);
}
Ok(())
}

194
vendor/png/examples/corpus-bench.rs vendored Normal file
View File

@@ -0,0 +1,194 @@
use std::{fs, io::Cursor, 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::Balanced,
Speed::Best => png::Compression::High,
});
encoder.set_filter(match args.filter {
Filter::None => png::Filter::NoFilter,
Filter::Sub => png::Filter::Sub,
Filter::Up => png::Filter::Up,
Filter::Average => png::Filter::Avg,
Filter::Paeth => png::Filter::Paeth,
Filter::Adaptive => png::Filter::Adaptive,
});
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(Cursor::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(Cursor::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().unwrap()];
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
View 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();
}

295
vendor/png/examples/pngcheck.rs vendored Normal file
View File

@@ -0,0 +1,295 @@
#![allow(non_upper_case_globals)]
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use clap::Parser;
use png::chunk;
#[derive(Parser)]
#[command(about, version)]
struct Config {
/// test quietly (output only errors)
#[arg(short, long)]
quiet: bool,
/// test verbosely (print most chunk data)
#[arg(short, long)]
verbose: bool,
/// print contents of tEXt/zTXt/iTXt chunks (can be used with -q)
#[arg(short, long)]
text: bool,
paths: Vec<PathBuf>,
}
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 reusing allocations from decoder
use png::Decoded::*;
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> {
if c.verbose {
println!(": {}", err);
print!("ERRORS DETECTED");
println!(" in {}", fname);
} else {
if !c.quiet {
println!("ERROR: {}", fname)
}
print!("{}: ", fname);
println!("{}", err);
}
Ok(())
};
if c.verbose {
print!("File: ");
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, None) {
Ok((_, ChunkComplete(chunk::IEND))) => {
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 {
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!();
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 {
ChunkBegin(len, type_str) => {
n_chunks += 1;
if c.verbose {
let chunk = type_str;
println!();
print!(" chunk ");
print!("{:?}", chunk);
print!(
" at offset {:#07x}, length {}",
pos - 4, // subtract chunk name length
len
)
}
match type_str {
chunk::IDAT => {
have_idat = true;
compressed_size += len
}
chunk::tRNS => {
trns = true;
}
_ => (),
}
}
ChunkComplete(chunk::IHDR) => {
width = decoder.info().unwrap().width;
height = decoder.info().unwrap().height;
bits = decoder.info().unwrap().bit_depth as u8;
color = decoder.info().unwrap().color_type;
interlaced = decoder.info().unwrap().interlaced;
if c.verbose {
println!();
print!(
" {} x {} image, {}{}, {}",
width,
height,
display_image_type(bits, color),
(if trns { "+trns" } else { "" }),
display_interlaced(interlaced),
);
}
}
ChunkComplete(chunk::acTL) => {
let actl = decoder.info().unwrap().animation_control.unwrap();
println!();
print!(" {} frames, {} plays", actl.num_frames, actl.num_plays,);
}
ChunkComplete(chunk::fdAT) => {
let fctl = decoder.info().unwrap().frame_control.unwrap();
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,
);
}
ImageData => {
//println!("got {} bytes of image data", data.len())
}
_ => (),
}
//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 config = Config::parse();
for file in &config.paths {
let result = if let Some(glob) = file.to_str().filter(|n| n.contains('*')) {
glob::glob(glob)
.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.display(), err);
std::process::exit(1)
});
}
}