1 extern crate glium;
2 extern crate glob;
3 extern crate png;
4 
5 use std::borrow::Cow;
6 use std::env;
7 use std::error::Error;
8 use std::fs::File;
9 use std::io;
10 use std::path;
11 
12 use glium::backend::glutin::Display;
13 use glium::glutin::{self, dpi, Event, VirtualKeyCode};
14 use glium::texture::{ClientFormat, RawImage2d};
15 use glium::{BlitTarget, Rect, Surface};
16 
17 /// Load the image using `png`
load_image(path: &path::PathBuf) -> io::Result<RawImage2d<'static, u8>>18 fn load_image(path: &path::PathBuf) -> io::Result<RawImage2d<'static, u8>> {
19     use png::ColorType::*;
20     let decoder = png::Decoder::new(File::open(path)?);
21     let (info, mut reader) = decoder.read_info()?;
22     let mut img_data = vec![0; info.buffer_size()];
23     reader.next_frame(&mut img_data)?;
24 
25     let (data, format) = match info.color_type {
26         RGB => (img_data, ClientFormat::U8U8U8),
27         RGBA => (img_data, ClientFormat::U8U8U8U8),
28         Grayscale => (
29             {
30                 let mut vec = Vec::with_capacity(img_data.len() * 3);
31                 for g in img_data {
32                     vec.extend([g, g, g].iter().cloned())
33                 }
34                 vec
35             },
36             ClientFormat::U8U8U8,
37         ),
38         GrayscaleAlpha => (
39             {
40                 let mut vec = Vec::with_capacity(img_data.len() * 3);
41                 for ga in img_data.chunks(2) {
42                     let g = ga[0];
43                     let a = ga[1];
44                     vec.extend([g, g, g, a].iter().cloned())
45                 }
46                 vec
47             },
48             ClientFormat::U8U8U8U8,
49         ),
50         _ => unreachable!("uncovered color type"),
51     };
52 
53     Ok(RawImage2d {
54         data: Cow::Owned(data),
55         width: info.width,
56         height: info.height,
57         format: format,
58     })
59 }
60 
main_loop(files: Vec<path::PathBuf>) -> io::Result<()>61 fn main_loop(files: Vec<path::PathBuf>) -> io::Result<()> {
62     use glium::glutin::{KeyboardInput, WindowEvent};
63 
64     let mut files = files.iter();
65     let image = load_image(files.next().unwrap())?;
66 
67     let mut events_loop = glutin::EventsLoop::new();
68     let window = glutin::WindowBuilder::new();
69     let context = glutin::ContextBuilder::new().with_vsync(true);
70 
71     let display = Display::new(window, context, &events_loop)
72         .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
73     // building the display, ie. the main object
74     resize_window(&display, &image);
75     let mut opengl_texture = glium::Texture2d::new(&display, image).unwrap();
76 
77     let mut stop = false;
78     let mut res = Ok(());
79     'main: loop {
80         let frame = display.draw();
81         fill_v_flipped(
82             &opengl_texture.as_surface(),
83             &frame,
84             glium::uniforms::MagnifySamplerFilter::Linear,
85         );
86         frame.finish().unwrap();
87 
88         // polling and handling the events received by the window
89         events_loop.poll_events(|event| {
90             if stop {
91                 return;
92             }
93             match event {
94                 Event::WindowEvent {
95                     event: WindowEvent::CloseRequested,
96                     ..
97                 } => {
98                     stop = true;
99                     return;
100                 }
101                 Event::WindowEvent {
102                     event:
103                         WindowEvent::KeyboardInput {
104                             input:
105                                 KeyboardInput {
106                                     state: glutin::ElementState::Pressed,
107                                     virtual_keycode: code,
108                                     ..
109                                 },
110                             ..
111                         },
112                     ..
113                 } => match code {
114                     Some(VirtualKeyCode::Escape) => {
115                         stop = true;
116                         return;
117                     }
118                     Some(VirtualKeyCode::Right) => match files.next() {
119                         Some(path) => {
120                             let image = match load_image(path) {
121                                 Ok(image) => image,
122                                 Err(err) => {
123                                     stop = true;
124                                     res = Err(err);
125                                     return;
126                                 }
127                             };
128                             resize_window(&display, &image);
129                             opengl_texture = glium::Texture2d::new(&display, image).unwrap();
130                         }
131                         None => {
132                             stop = true;
133                             return;
134                         }
135                     },
136                     _ => (),
137                 },
138                 _ => (),
139             }
140         });
141 
142         if stop {
143             break 'main;
144         }
145     }
146     res
147 }
148 
fill_v_flipped<S1, S2>(src: &S1, target: &S2, filter: glium::uniforms::MagnifySamplerFilter) where S1: Surface, S2: Surface,149 fn fill_v_flipped<S1, S2>(src: &S1, target: &S2, filter: glium::uniforms::MagnifySamplerFilter)
150 where
151     S1: Surface,
152     S2: Surface,
153 {
154     let src_dim = src.get_dimensions();
155     let src_rect = Rect {
156         left: 0,
157         bottom: 0,
158         width: src_dim.0 as u32,
159         height: src_dim.1 as u32,
160     };
161     let target_dim = target.get_dimensions();
162     let target_rect = BlitTarget {
163         left: 0,
164         bottom: target_dim.1,
165         width: target_dim.0 as i32,
166         height: -(target_dim.1 as i32),
167     };
168     src.blit_color(&src_rect, target, &target_rect, filter);
169 }
170 
resize_window(display: &Display, image: &RawImage2d<'static, u8>)171 fn resize_window(display: &Display, image: &RawImage2d<'static, u8>) {
172     let mut width = image.width;
173     let mut height = image.height;
174     if width < 50 && height < 50 {
175         width *= 10;
176         height *= 10;
177     } else if width < 5 && height < 5 {
178         width *= 10;
179         height *= 10;
180     }
181     display
182         .gl_window()
183         .window()
184         .set_inner_size(dpi::LogicalSize::new(f64::from(width), f64::from(height)));
185 }
186 
main()187 fn main() {
188     let args: Vec<String> = env::args().collect();
189     if args.len() < 2 {
190         println!("Usage: show files [...]");
191     } else {
192         let mut files = vec![];
193         for file in args.iter().skip(1) {
194             match if file.contains("*") {
195                 (|| -> io::Result<_> {
196                     for entry in glob::glob(&file)
197                         .map_err(|err| io::Error::new(io::ErrorKind::Other, err.msg))?
198                     {
199                         files.push(
200                             entry
201                                 .map_err(|_| io::Error::new(io::ErrorKind::Other, "glob error"))?,
202                         )
203                     }
204                     Ok(())
205                 })()
206             } else {
207                 files.push(path::PathBuf::from(file));
208                 Ok(())
209             } {
210                 Ok(_) => (),
211                 Err(err) => {
212                     println!("{}: {}", file, err);
213                     break;
214                 }
215             }
216         }
217         // "tests/pngsuite/pngsuite.png"
218         match main_loop(files) {
219             Ok(_) => (),
220             Err(err) => println!("Error: {}", err),
221         }
222     }
223 }
224