diff options
Diffstat (limited to 'third_party/rust/png/examples')
-rw-r--r-- | third_party/rust/png/examples/pngcheck.rs | 360 | ||||
-rw-r--r-- | third_party/rust/png/examples/show.rs | 199 |
2 files changed, 559 insertions, 0 deletions
diff --git a/third_party/rust/png/examples/pngcheck.rs b/third_party/rust/png/examples/pngcheck.rs new file mode 100644 index 0000000000..6cd0b5b7e2 --- /dev/null +++ b/third_party/rust/png/examples/pngcheck.rs @@ -0,0 +1,360 @@ +#![allow(non_upper_case_globals)] + +extern crate getopts; +extern crate glob; +extern crate png; + +use std::io; +use std::io::prelude::*; +use std::path::Path; +use std::fs::File; +use std::env; + +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 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(&format!("Usage: pngcheck [-cpt] [file ...]"))); + std::process::exit(0); +} + +#[derive(Clone, Copy)] +struct Config { + quiet: bool, + verbose: bool, + color: 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 + if trns { 1 } else { 0 }, + RGB => 3, + Indexed => 3 + if trns { 1 } else { 0 }, + 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(io::Error::new( + io::ErrorKind::Other, + "could not open terminal" + ))?; + let mut data = vec![0; 10*1024]; + let data_p = data.as_mut_ptr(); + let mut reader = io::BufReader::new(File::open(&fname)?); + let fname = fname.as_ref().to_string_lossy(); + let n = reader.read(&mut 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(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.len() == 0 { + // circumvent borrow checker + assert!(!data.is_empty()); + let n = reader.read(unsafe { + ::std::slice::from_raw_parts_mut(data_p, data.len()) + })?; + + // 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 { + display_error(png::DecodingError::Format("IDAT chunk missing".into()))?; + 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 = String::from_utf8_lossy(&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::*; + match 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 + } + } + } + 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") + }; + + 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) + }); + } +} diff --git a/third_party/rust/png/examples/show.rs b/third_party/rust/png/examples/show.rs new file mode 100644 index 0000000000..9185b8f77e --- /dev/null +++ b/third_party/rust/png/examples/show.rs @@ -0,0 +1,199 @@ +extern crate glium; +extern crate glob; +extern crate png; + +use std::env; +use std::io; +use std::fs::File; +use std::borrow::Cow; +use std::path; +use std::error::Error; + +use glium::{Surface, Rect, BlitTarget}; +use glium::texture::{RawImage2d, ClientFormat}; +use glium::glutin::{self, Event, VirtualKeyCode, dpi}; +use glium::backend::glutin::Display; + +/// Load the image using `png` +fn load_image(path: &path::PathBuf) -> io::Result<RawImage2d<'static, u8>> { + use png::ColorType::*; + let decoder = png::Decoder::new(File::open(path)?); + let (info, mut reader) = decoder.read_info()?; + let mut img_data = vec![0; info.buffer_size()]; + 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: format + }) +} + +fn main_loop(files: Vec<path::PathBuf>) -> io::Result<()> { + use glium::glutin::{KeyboardInput, WindowEvent}; + + let mut files = files.iter(); + let image = load_image(files.next().unwrap())?; + + let mut events_loop = glutin::EventsLoop::new(); + let window = glutin::WindowBuilder::new(); + let context = glutin::ContextBuilder::new() + .with_vsync(true); + + let display = Display::new(window, context, &events_loop) + .map_err(|err| io::Error::new( + io::ErrorKind::Other, + err.description() + ))?; + // building the display, ie. the main object + resize_window(&display, &image); + let mut opengl_texture = glium::Texture2d::new(&display, image).unwrap(); + + let mut stop = false; + let mut res = Ok(()); + 'main: loop { + let frame = display.draw(); + fill_v_flipped(&opengl_texture.as_surface(), &frame, glium::uniforms::MagnifySamplerFilter::Linear); + frame.finish().unwrap(); + + // polling and handling the events received by the window + events_loop.poll_events(|event| { + if stop {return;} + match event { + Event::WindowEvent {event: WindowEvent::CloseRequested, ..} => { + stop = true; + return; + } + Event::WindowEvent { + event: WindowEvent::KeyboardInput { + input: KeyboardInput { + state: glutin::ElementState::Pressed, + virtual_keycode: code, + .. + }, + .. + }, + .. + } => match code { + Some(VirtualKeyCode::Escape) => { + stop = true; + return; + } + Some(VirtualKeyCode::Right) => { + match files.next() { + Some(path) => { + let image = match load_image(path) { + Ok(image) => image, + Err(err) => { + stop = true; + res = Err(err); + return; + } + }; + resize_window(&display, &image); + opengl_texture = glium::Texture2d::new(&display, image).unwrap(); + }, + None => { + stop = true; + return; + } + } + }, + _ => () + }, + _ => () + } + }); + + if stop {break 'main;} + } + res +} + +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; + } else if width < 5 && height < 5 { + 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) + } + } +} |