224 lines
6.8 KiB
Rust
224 lines
6.8 KiB
Rust
extern crate glium;
|
|
extern crate glob;
|
|
extern crate png;
|
|
|
|
use std::borrow::Cow;
|
|
use std::env;
|
|
use std::error::Error;
|
|
use std::fs::File;
|
|
use std::io;
|
|
use std::path;
|
|
|
|
use glium::backend::glutin::Display;
|
|
use glium::glutin::{self, dpi, Event, VirtualKeyCode};
|
|
use glium::texture::{ClientFormat, RawImage2d};
|
|
use glium::{BlitTarget, Rect, Surface};
|
|
|
|
/// 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))?;
|
|
// 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),
|
|
}
|
|
}
|
|
}
|