use std;
use std::collections::HashMap;
use std::path::Path;
extern crate sdl2;
use sdl2::image::ImageRWops;
use sdl2::image::LoadSurface;
use sdl2::image::LoadTexture;
use sdl2::pixels;
use sdl2::render;
use sdl2::rwops;
use sdl2::surface;
use event::{self, Event};
use shape;
use util;
pub struct Window {
event_pump: sdl2::EventPump,
timer_subsystem: sdl2::TimerSubsystem,
canvas: sdl2::render::Canvas<sdl2::video::Window>,
foreground_color: pixels::Color,
font: Option<Font>,
running: bool,
event_queue: std::vec::Vec<Event>,
target_ticks_per_frame: u32,
ticks_at_previous_frame: u32,
}
impl Window {
pub fn new(name: &str, width: u16, height: u16) -> Self {
let sdl_context = sdl2::init().unwrap();
let timer_subsystem = sdl_context.timer().unwrap();
sdl2::image::init(sdl2::image::InitFlag::all()).unwrap();
let video_subsystem = sdl_context.video().unwrap();
let event_pump = sdl_context.event_pump().unwrap();
let sdl_window_builder = video_subsystem.window(name, width as u32, height as u32);
let sdl_window = sdl_window_builder.build().unwrap();
let mut canvas = sdl_window.into_canvas().build().unwrap();
canvas.set_blend_mode(render::BlendMode::Blend);
let mut window = Window {
timer_subsystem: timer_subsystem,
event_pump: event_pump,
canvas: canvas,
running: true,
event_queue: vec![],
foreground_color: pixels::Color::RGBA(0, 0, 0, 255),
target_ticks_per_frame: (1000.0 / 60.0) as u32,
ticks_at_previous_frame: 0,
font: None,
};
window.clear();
window.canvas.present();
window.set_color(255, 255, 255, 255);
let font = window
.load_font(DEFAULT_FONT_BYTES, DEFAULT_FONT_STR.to_string())
.unwrap();
window.font = Some(font);
window
}
pub fn next_frame(&mut self) -> bool {
if !self.running {
return false;
}
self.canvas.present();
let mut current_ticks = self.timer_subsystem.ticks();
while current_ticks - self.ticks_at_previous_frame < self.target_ticks_per_frame {
self.timer_subsystem.delay(3);
current_ticks = self.timer_subsystem.ticks();
}
self.ticks_at_previous_frame = current_ticks;
loop {
let sdl_event = self.event_pump.poll_event();
match sdl_event {
None => break,
Some(sdl_event) => match Event::from_sdl2_event(sdl_event) {
Some(Event::Quit) => self.quit(),
Some(e) => (self.event_queue.push(e)),
None => (),
},
};
}
true
}
pub fn has_event(&self) -> bool {
self.event_queue.len() > 0
}
pub fn next_event(&mut self) -> Event {
self.event_queue.remove(0)
}
pub fn is_key_down(&self, key: event::Key) -> bool {
self.event_pump.keyboard_state().is_scancode_pressed(key)
}
pub fn is_mouse_button_down(&self, button: event::MouseButton) -> bool {
let mouse_state = self.event_pump.mouse_state();
mouse_state.is_mouse_button_pressed(button)
}
pub fn mouse_position(&self) -> (i32, i32) {
let mouse_state = self.event_pump.mouse_state();
(mouse_state.x(), mouse_state.y())
}
pub fn set_font(&mut self, font: Font) {
self.font = Some(font)
}
pub fn quit(&mut self) {
self.running = false;
}
}
impl Window {
pub fn set_color(&mut self, red: u8, green: u8, blue: u8, alpha: u8) {
self.foreground_color = pixels::Color::RGBA(red, green, blue, alpha);
}
fn prepare_to_draw(&mut self) {
self.canvas.set_draw_color(self.foreground_color);
}
pub fn draw_rect(&mut self, rect: shape::Rect) {
self.prepare_to_draw();
self.canvas.draw_rect(rect).unwrap();
}
pub fn fill_rect(&mut self, rect: shape::Rect) {
self.prepare_to_draw();
self.canvas.fill_rect(rect).unwrap();
}
pub fn draw_point(&mut self, point: shape::Point) {
self.prepare_to_draw();
self.canvas.draw_point(point).unwrap();
}
pub fn draw_polygon(&mut self, polygon: shape::Polygon) {
self.prepare_to_draw();
self.canvas.draw_points(&polygon[..]).unwrap();
}
pub fn draw_image(&mut self, image: &mut Image, x: i32, y: i32) {
util::set_texture_color(&self.foreground_color, &mut image.texture);
self.canvas
.copy(
&(image.texture),
Some(shape::Rect::new(
x,
y,
image.get_width() as u32,
image.get_height() as u32,
)),
None,
)
.unwrap();
}
pub fn print(&mut self, text: &str, x: i32, y: i32) -> shape::Rect {
self.prepare_to_draw();
let font = match self.font {
Some(ref mut r) => r,
None => panic!("no font set on window"),
};
util::set_texture_color(&self.foreground_color, &mut font.texture);
let mut current_x = x;
for ch in text.chars() {
let font_rect = match font.get_rect(ch) {
None => {
current_x += 5;
continue;
}
Some(r) => r,
};
let rect = shape::Rect::new(current_x, y, font_rect.width(), font_rect.height());
self.canvas
.copy(&(font.texture), Some(*font_rect), rect)
.unwrap();
current_x += font_rect.width() as i32;
}
shape::Rect::new(x, y, (current_x - x) as u32, font.get_height() as u32)
}
pub fn clear(&mut self) {
self.canvas.set_draw_color(pixels::Color::RGB(0, 0, 0));
self.canvas.clear();
}
pub fn clear_to_color(&mut self, r: u8, g: u8, b: u8) {
self.canvas.set_draw_color(pixels::Color::RGB(r, g, b));
self.canvas.clear();
}
}
pub struct Image {
texture: render::Texture,
width: u32,
height: u32,
}
impl Image {
pub fn get_width(&self) -> u32 {
self.width
}
pub fn get_height(&self) -> u32 {
self.height
}
}
pub struct Font {
texture: render::Texture,
chars: HashMap<char, shape::Rect>,
height: u32,
}
impl Font {
pub fn is_printable(&self, ch: char) -> bool {
self.chars.contains_key(&ch)
}
pub fn len(&self) -> usize {
self.chars.len()
}
pub fn get_height(&self) -> u32 {
self.height
}
fn get_rect(&self, ch: char) -> Option<&shape::Rect> {
self.chars.get(&ch)
}
}
const DEFAULT_FONT_BYTES: &'static [u8] = include_bytes!("default_font.png");
const DEFAULT_FONT_STR: &'static str =
" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?-+/():;%&`'*#=[]\"";
impl Window {
pub fn load_image_from_file(&self, filename: &Path) -> Result<Image, String> {
let mut texture = try!(self.canvas.texture_creator().load_texture(&filename));
texture.set_blend_mode(render::BlendMode::Blend);
Ok(Image {
width: texture.query().width,
height: texture.query().height,
texture: texture,
})
}
pub fn load_image(&self, data: &[u8]) -> Result<Image, String> {
let rwops = try!(rwops::RWops::from_bytes(data));
let surf: surface::Surface = try!(rwops.load());
let mut texture = match self
.canvas
.texture_creator()
.create_texture_from_surface(&surf)
{
Ok(t) => t,
Err(e) => return Err(e.to_string()),
};
texture.set_blend_mode(render::BlendMode::Blend);
Ok(Image {
width: texture.query().width,
height: texture.query().height,
texture: texture,
})
}
fn parse_image_font(&self, surf: surface::Surface, string: String) -> Result<Font, String> {
if util::string_has_duplicate_chars(string.clone()) {
return Err("image font string has duplicate characters".to_string());
}
let surf = surf;
let mut chars: HashMap<char, shape::Rect> = HashMap::new();
let surf_width = surf.width();
let surf_height = surf.height();
let mut current_rect: Option<shape::Rect> = None;
surf.with_lock(|pixels| {
let border_color = pixels[0];
for i in 0..(surf_width as usize) {
if pixels[i] == border_color {
match current_rect {
Some(mut rect) => {
let c = match string.chars().nth(chars.len()) {
Some(c) => c,
None => {
return;
}
};
rect = shape::Rect::new(
rect.x(),
rect.y(),
((i as i32) - rect.x()) as u32,
rect.height(),
);
chars.insert(c, rect.clone());
current_rect = None;
}
None => (),
}
} else {
match current_rect {
Some(_) => (),
None => {
current_rect = Some(shape::Rect::new(i as i32, 0, 1, surf_height));
}
}
}
}
});
let mut texture = match self
.canvas
.texture_creator()
.create_texture_from_surface(&surf)
{
Ok(t) => t,
Err(e) => return Err(e.to_string()),
};
texture.set_blend_mode(render::BlendMode::Blend);
Ok(Font {
height: texture.query().height,
texture: texture,
chars: chars,
})
}
pub fn load_font_from_file(&self, filename: &Path, string: String) -> Result<Font, String> {
let surf: surface::Surface = try!(LoadSurface::from_file(filename));
self.parse_image_font(surf, string)
}
pub fn load_font(&self, data: &[u8], string: String) -> Result<Font, String> {
let rwops = try!(rwops::RWops::from_bytes(data));
let surf: surface::Surface = try!(rwops.load());
self.parse_image_font(surf, string)
}
}