extern crate flic;
extern crate sdl2;
use std::env;
use std::path::PathBuf;
use flic::{FlicFile,RasterMut};
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::pixels::PixelFormatEnum;
const DEFAULT_SCREEN_W: u32 = 640;
const DEFAULT_SCREEN_H: u32 = 400;
const MIN_SCREEN_W: u32 = 320;
const MIN_SCREEN_H: u32 = 200;
fn main() {
let mut filenames: Vec<String> = env::args().skip(1).collect();
let mut flic: Option<FlicFile> = None;
let mut next_file: usize = 0;
usage();
if filenames.len() <= 0 {
return;
}
let sdl = sdl2::init().unwrap();
let video = sdl.video().unwrap();
let mut window
= video.window("QuickFLI", DEFAULT_SCREEN_W, DEFAULT_SCREEN_H)
.resizable()
.position_centered()
.opengl()
.build().unwrap();
let _ = window.set_minimum_size(MIN_SCREEN_W, MIN_SCREEN_H);
let mut canvas = window.into_canvas().build().unwrap();
let mut timer = sdl.timer().unwrap();
let mut event_pump = sdl.event_pump().unwrap();
let texture_creator = canvas.texture_creator();
let mut flic_w = 0;
let mut flic_h = 0;
let mut texture = None;
let mut buf = vec![0; flic_w * flic_h];
let mut pal = vec![0; 3 * 256];
let mut last_tstart: u32 = 0;
'mainloop: loop {
let msec = match flic {
Some(ref f) => f.speed_msec(),
None => 100,
};
let tnow = timer.ticks();
let tstart = if msec > 0 { tnow - tnow % msec } else { tnow };
let tend = tstart + msec;
let redraw = tstart > last_tstart;
if redraw {
if let (Some(ref mut flic), Some(ref mut texture)) = (flic.as_mut(), texture.as_mut()) {
match flic.read_next_frame(
&mut RasterMut::new(flic_w, flic_h, &mut buf, &mut pal)) {
Ok(_) => {
render_to_texture(texture, flic_w, flic_h, &buf, &pal);
present_to_screen(&mut canvas, texture);
},
Err(e) => {
println!("Error occurred - {}", e);
},
}
} else {
canvas.clear();
canvas.present();
}
last_tstart = tstart;
}
if !redraw || msec == 0 {
if let Some(e) = event_pump.wait_event_timeout(tend - tnow) {
match e {
Event::Quit {..}
| Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
break 'mainloop;
},
Event::KeyDown { keycode: Some(Keycode::Space), .. } => {
if filenames.len() > 1 {
flic = None;
}
},
_ => (),
}
}
if flic.is_none() && filenames.len() > 0 {
if next_file >= filenames.len() {
next_file = 0;
}
let path = PathBuf::from(&filenames[next_file]);
match FlicFile::open(path.as_path()) {
Ok(f) => {
println!("Loaded {}", &filenames[next_file]);
if f.width() as usize != flic_w || f.height() as usize != flic_h {
flic_w = f.width() as usize;
flic_h = f.height() as usize;
texture = texture_creator.create_texture_streaming(
PixelFormatEnum::RGB24,
flic_w as u32, flic_h as u32).ok();
buf = vec![0; flic_w * flic_h];
}
flic = Some(f);
last_tstart = 0;
next_file = next_file + 1;
},
Err(e) => {
println!("Error loading {} -- {}", &filenames[next_file], e);
filenames.remove(next_file);
},
}
}
}
}
}
fn usage() {
println!("QuickFLI - a simple player for 256 color VGA animations");
println!("");
println!("Give QuickFLI a list of FLICs to play.");
println!("<ESC> to abort playback.");
println!("<space> to go to next FLIC.");
}
fn render_to_texture(
texture: &mut sdl2::render::Texture,
w: usize, h: usize, buf: &[u8], pal: &[u8]) {
texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
for y in 0..h {
for x in 0..w {
let offset = pitch * y + 3 * x;
let c = buf[w * y + x] as usize;
buffer[offset + 0] = pal[3 * c + 0];
buffer[offset + 1] = pal[3 * c + 1];
buffer[offset + 2] = pal[3 * c + 2];
}
}
}).unwrap();
}
fn present_to_screen(
canvas: &mut sdl2::render::WindowCanvas,
texture: &sdl2::render::Texture) {
canvas.clear();
let _ = canvas.copy(&texture, None, None);
canvas.present();
}