pub mod renderer {
use px8;
use gfx::{Scale, Screen};
use sdl2::surface::Surface;
use sdl2::VideoSubsystem;
use sdl2::render;
use sdl2::rect::Rect;
use sdl2::rect::Point;
use sdl2::pixels::PixelFormatEnum;
use num;
use time::PreciseTime;
#[derive(Clone, Debug)]
pub enum RendererError {
}
pub type RendererResult<T> = Result<T, RendererError>;
pub struct Renderer {
pub renderer: render::Renderer<'static>,
pub texture: render::Texture,
buffer_rgb: Vec<u8>,
window_width: u32,
window_height: u32,
aspect_ratio: f32,
texture_width: u32,
texture_height: u32,
viewport_width: u32,
viewport_height: u32,
viewport_offset: Point,
frame: u32,
}
impl Renderer {
pub fn new(sdl_video: VideoSubsystem,
screen: &mut Screen,
fullscreen: bool,
opengl: bool,
scale: Scale)
-> RendererResult<Renderer> {
info!("[SDL] Creating window fullscreen={:?} opengl={:?}",
fullscreen,
opengl);
let mut window_builder =
sdl_video.window("PX8",
(screen.width as usize * scale.factor()) as u32,
(screen.height as usize * scale.factor()) as u32);
let wb = if fullscreen {
window_builder.fullscreen()
} else {
window_builder.resizable().position_centered()
};
let mut window = (if opengl { wb.opengl() } else { wb }).build().unwrap();
let pixels =
[0xff, 0x0, 0x4d, 0xff, 0x2c, 0x6c, 0xff, 0xff, 0xff, 0xff, 0x76, 0x9f, 0xff, 0x0,
0x4d, 0xff, 0x8b, 0xae, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x8b, 0xae, 0xff, 0x0, 0x4d, 0xff, 0x76, 0x9f, 0xff,
0xff, 0xff, 0xff, 0x2c, 0x6c, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x2c, 0x6c,
0xff, 0xff, 0xff, 0xff, 0x76, 0x9f, 0xff, 0x0, 0x4d, 0xff, 0x8b, 0xae, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8b,
0xae, 0xff, 0x0, 0x4d, 0xff, 0x76, 0x9f, 0xff, 0xff, 0xff, 0xff, 0x2c, 0x6c,
0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x2c, 0x6c, 0xff, 0xff, 0xff, 0xff, 0x77,
0xa0, 0xff, 0x0, 0x4d, 0xff, 0x8c, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8c, 0xaf, 0xff, 0x0, 0x4d, 0xff,
0x77, 0xa0, 0xff, 0xff, 0xff, 0xff, 0x2c, 0x6c, 0xff, 0x0, 0x4d, 0xff, 0xff,
0xff, 0xff, 0xd3, 0xe0, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff,
0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d,
0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0xd3,
0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3, 0xe0, 0xff, 0x0, 0x4d,
0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0,
0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff,
0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0xd3, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xd3, 0xe0, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0,
0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff,
0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0xd3, 0xe0,
0xff, 0xff, 0xff, 0xff, 0x45, 0x7d, 0xff, 0x3a, 0x75, 0xff, 0x0, 0x4d, 0xff, 0x0,
0x4d, 0xff, 0x0, 0x4e, 0xff, 0x41, 0x2e, 0xff, 0x7d, 0x12, 0xff, 0x0, 0x51, 0xff,
0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x36, 0x34, 0xff, 0x7a, 0x14, 0xff, 0x40, 0x2f,
0xff, 0x0, 0x4d, 0xff, 0x3a, 0x75, 0xff, 0x45, 0x7d, 0xff, 0x0, 0x4d, 0xff, 0x0,
0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4e, 0xff, 0x59, 0x23, 0xff,
0xab, 0x0, 0xff, 0x0, 0x53, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x4a, 0x2a,
0xff, 0xa7, 0x0, 0xff, 0x57, 0x24, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0,
0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff,
0x0, 0x4e, 0xff, 0x59, 0x23, 0xff, 0xab, 0x0, 0xff, 0x0, 0x53, 0xff, 0x0, 0x4d,
0xff, 0x0, 0x4d, 0xff, 0x4a, 0x2a, 0xff, 0xa7, 0x0, 0xff, 0x57, 0x24, 0xff, 0x0,
0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x45, 0x7d, 0xff, 0x3a, 0x75, 0xff,
0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4e, 0xff, 0x41, 0x2e, 0xff, 0x7d, 0x12,
0xff, 0x0, 0x51, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x36, 0x34, 0xff, 0x7a,
0x14, 0xff, 0x40, 0x2f, 0xff, 0x0, 0x4d, 0xff, 0x3a, 0x75, 0xff, 0x45, 0x7d,
0xff, 0xff, 0xff, 0xff, 0xd3, 0xe0, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0,
0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff,
0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d,
0xff, 0xd3, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3, 0xe0, 0xff,
0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d,
0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0,
0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0xd3, 0xe0, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xd3, 0xe0, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d,
0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0,
0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff,
0xd3, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x4d, 0xff, 0x2c, 0x6c, 0xff, 0xff,
0xff, 0xff, 0x77, 0xa0, 0xff, 0x0, 0x4d, 0xff, 0x8c, 0xaf, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8c, 0xaf, 0xff,
0x0, 0x4d, 0xff, 0x77, 0xa0, 0xff, 0xff, 0xff, 0xff, 0x2c, 0x6c, 0xff, 0x0, 0x4d,
0xff, 0x0, 0x4d, 0xff, 0x2c, 0x6c, 0xff, 0xff, 0xff, 0xff, 0x76, 0x9f, 0xff, 0x0,
0x4d, 0xff, 0x8b, 0xae, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x8b, 0xae, 0xff, 0x0, 0x4d, 0xff, 0x76, 0x9f, 0xff,
0xff, 0xff, 0xff, 0x2c, 0x6c, 0xff, 0x0, 0x4d, 0xff, 0x0, 0x4d, 0xff, 0x2c, 0x6c,
0xff, 0xff, 0xff, 0xff, 0x76, 0x9f, 0xff, 0x0, 0x4d, 0xff, 0x8b, 0xae, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8b,
0xae, 0xff, 0x0, 0x4d, 0xff, 0x76, 0x9f, 0xff, 0xff, 0xff, 0xff, 0x2c, 0x6c,
0xff, 0x0, 0x4d];
let mut v_order: Vec<u8> = Vec::new();
v_order.extend(pixels.iter().cloned());
let surface = Surface::from_data(v_order.as_mut_slice(),
16,
16,
16 * 3,
PixelFormatEnum::RGB24);
window.set_icon(surface.unwrap());
info!("[SDL] Creating renderer");
let renderer = window
.renderer()
.accelerated()
.present_vsync()
.build()
.unwrap();
info!("[SDL] Creating texture");
let texture_width = screen.width as u32;
let texture_height = screen.height as u32;
let texture = renderer
.create_texture(PixelFormatEnum::BGR24,
render::TextureAccess::Streaming,
texture_width,
texture_height)
.unwrap();
Ok(Renderer {
renderer: renderer,
texture: texture,
buffer_rgb: vec![0; 0],
window_width: 0,
window_height: 0,
aspect_ratio: 0.0,
texture_width: texture_width,
texture_height: texture_height,
viewport_width: 0,
viewport_height: 0,
viewport_offset: Point::new(0, 0),
frame: 0,
})
}
pub fn blit(&mut self, screen: &mut Screen) {
if (self.aspect_ratio != screen.aspect_ratio) || (self.viewport_width == 0) {
self.aspect_ratio = screen.aspect_ratio;
self.update_viewport(screen);
}
if (self.texture_width != screen.width as u32) ||
(self.texture_height != screen.height as u32) {
self.texture_width = screen.width as u32;
self.texture_height = screen.height as u32;
self.texture = self.renderer
.create_texture(PixelFormatEnum::BGR24,
render::TextureAccess::Streaming,
self.texture_width,
self.texture_height)
.unwrap();
}
let src_buffer = &screen.frame_buffer;
let rgb_buffer_len = src_buffer.len() * 3;
if self.buffer_rgb.len() != rgb_buffer_len {
self.buffer_rgb = vec![0; rgb_buffer_len];
}
let mut rgb_buffer = &mut self.buffer_rgb;
let mut palette = px8::PALETTE.lock().unwrap();
let mut j = 0;
let mut cached_pixel: u8 = 0;
let mut rgb = palette.get_rgb(cached_pixel as u32);
let start = PreciseTime::now();
for pixel in src_buffer.iter() {
if *pixel != cached_pixel {
rgb = palette.get_rgb(*pixel as u32);
cached_pixel = *pixel;
}
rgb_buffer[j] = rgb.b;
rgb_buffer[j + 1] = rgb.g;
rgb_buffer[j + 2] = rgb.r;
j += 3;
}
let t1 = PreciseTime::now();
self.texture
.update(None, &rgb_buffer, screen.width * 3)
.unwrap();
let t2 = PreciseTime::now();
if self.viewport_offset.x() != 0 || self.viewport_offset.y() != 0 {
self.renderer.clear();
}
self.renderer
.copy(&self.texture,
Some(Rect::new(0, 0, screen.width as u32, screen.height as u32)),
Some(Rect::new(self.viewport_offset.x(),
self.viewport_offset.y(),
self.viewport_width,
self.viewport_height)))
.unwrap();
let t3 = PreciseTime::now();
self.renderer.present();
let t4 = PreciseTime::now();
if cfg!(feature = "blit_perf") {
if self.frame % 60 == 0 {
info!("gen_rgb:{} update_tex:{} copy_tex:{} present:{}",
start.to(t1),
t1.to(t2),
t2.to(t3),
t3.to(t4))
}
}
self.frame += 1;
}
pub fn update_viewport(&mut self, screen: &Screen) {
let (w, h) = self.get_dimensions();
self.window_width = w;
self.window_height = h;
let window_aspect_ratio = (w as f32) / (h as f32);
let viewport_aspect_ratio = screen.aspect_ratio;
self.viewport_offset = if viewport_aspect_ratio > window_aspect_ratio {
self.viewport_width = self.window_width;
self.viewport_height = (self.viewport_width as f32 / viewport_aspect_ratio) as u32;
Point::new(0, (self.window_height - self.viewport_height) as i32 / 2)
} else {
self.viewport_height = self.window_height;
self.viewport_width = (self.viewport_height as f32 * viewport_aspect_ratio) as u32;
Point::new((self.window_width - self.viewport_width) as i32 / 2, 0)
};
}
pub fn get_dimensions(&mut self) -> (u32, u32) {
self.renderer.window().unwrap().size()
}
pub fn window_coords_to_viewport_coords(&mut self,
screen: &Screen,
window_x: i32,
window_y: i32)
-> (i32, i32) {
let viewport_x = ((window_x - self.viewport_offset.x()) as f32 /
self.viewport_width as f32 *
screen.width as f32) as i32;
let viewport_y = ((window_y - self.viewport_offset.y()) as f32 /
self.viewport_height as f32 *
screen.height as f32) as i32;
(num::clamp(viewport_x, 0, (screen.width - 1) as i32),
num::clamp(viewport_y, 0, (screen.height - 1) as i32))
}
}
}