flic 0.1.6

Autodesk Animator FLI and Autodesk Animator Pro FLC file encoder and decoder.
Documentation
//! FLIC browser.

extern crate flic;
extern crate sdl2;

use std::env;
use std::path::Path;
use flic::{FlicFile,RasterMut};
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::pixels::PixelFormatEnum;

const SCREEN_W: u32 = 640;
const SCREEN_H: u32 = 400;
const MIN_SCREEN_W: u32 = 320;
const MIN_SCREEN_H: u32 = 200;
const MAX_PSTAMP_W: u16 = 98;
const MAX_PSTAMP_H: u16 = 61;

fn main() {
    let dirname = env::args().nth(1);
    if dirname.is_none() {
        usage();
        return;
    }

    let dir = Path::new(dirname.as_ref().unwrap());
    if !dir.is_dir() {
        usage();
        return;
    }

    // Initialise SDL window.
    let sdl = sdl2::init().unwrap();
    let video = sdl.video().unwrap();

    let mut window
        = video.window("FLIC Browser", SCREEN_W, 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 event_pump = sdl.event_pump().unwrap();
    let texture_creator = canvas.texture_creator();
    let mut texture = texture_creator.create_texture_streaming(
            PixelFormatEnum::RGB24, SCREEN_W, SCREEN_H).unwrap();

    if let Ok(entries) = dir.read_dir() {
        let mut filenames = Vec::new();
        let mut count = 0;

        // Find FLIC files by extension.
        for entry in entries {
            if entry.is_err() {
                continue;
            }

            let path = entry.unwrap().path();
            if !path.is_file() || path.extension().is_none() {
                continue;
            }

            // Surely something better is possible...
            let ext = path.extension().unwrap()
                    .to_os_string().into_string().unwrap()
                    .to_lowercase();
            if ext != "flc" && ext != "fli" {
                continue;
            }

            filenames.push(path);
        }

        filenames.sort();

        let mut buf = [0; (SCREEN_W * SCREEN_H) as usize];
        let mut pal = [0; 3 * 256];

        // Render postage stamps.
        for filename in filenames {
            let mut flic = match FlicFile::open(&filename) {
                Ok(f) => f,
                Err(e) => {
                    println!("Error loading {} -- {}",
                            filename.to_string_lossy(), e);
                    continue;
                },
            };

            let (pstamp_w, pstamp_h) = flic::pstamp::get_pstamp_size(
                    MAX_PSTAMP_W, MAX_PSTAMP_H, flic.width(), flic.height());

            let gridx = count % 6;
            let gridy = count / 6;
            let x = 27 + 102 * gridx + (MAX_PSTAMP_W - pstamp_w) / 2;
            let y =  1 +  80 * gridy + (MAX_PSTAMP_H - pstamp_h) / 2;

            {
                let mut raster = RasterMut::with_offset(
                        x as usize, y as usize,
                        pstamp_w as usize, pstamp_h as usize, SCREEN_W as usize,
                        &mut buf, &mut pal);
                if let Err(e) = flic.read_postage_stamp(&mut raster) {
                    println!("Error reading postage stamp -- {}", e);
                    continue;
                }
            }

            draw_rect(&mut buf, x - 1, y - 1, pstamp_w + 2, pstamp_h + 2);

            count = count + 1;
            if count >= 6 * 5 {
                break;
            }
        }

        pal[3 * 255 + 0] = 0x98;
        pal[3 * 255 + 1] = 0x98;
        pal[3 * 255 + 2] = 0x98;
        render_to_texture(&mut texture,
                SCREEN_W as usize, SCREEN_H as usize, &buf, &pal);
    }

    present_to_screen(&mut canvas, &texture);

    'mainloop: loop {
        if let Some(e) = event_pump.wait_event_timeout(100) {
            match e {
                Event::Quit {..}
                | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                    break 'mainloop;
                },

                _ => (),
            }
        } else {
            present_to_screen(&mut canvas, &texture);
        }
    }
}

fn usage() {
    println!("Usage: browse <directory containing FLIC files>");
}

fn draw_rect(
        buf: &mut [u8], x: u16, y: u16, w: u16, h: u16) {
    let x = x as u32;
    let y = y as u32;
    let w = w as u32;
    let h = h as u32;
    let stride = SCREEN_W;
    let c = 0xFF;

    for i in x..(x + w) {
        buf[(stride * y + i) as usize] = c;
    }

    for i in y..(y + h) {
        buf[(stride * i + x) as usize] = c;
    }

    for i in y..(y + h) {
        buf[(stride * i + (x + w - 1)) as usize] = c;
    }

    for i in x..(x + w) {
        buf[(stride * (y + h - 1) + i) as usize] = c;
    }
}

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();
}