use super::{Rect, TextureInfo, VideoDevice};
use crate::{app::settings::Settings, backends::SDL_CONTEXT};
use rustzx_core::zx::constants::{SCREEN_HEIGHT, SCREEN_WIDTH};
use sdl2::{
pixels::PixelFormatEnum as PixelFormat,
rect::Rect as SdlRect,
render::{Canvas, Texture, TextureCreator},
video::{Window, WindowContext},
};
use std::collections::HashMap;
pub struct VideoSdl {
renderer: Canvas<Window>,
texture_creator: TextureCreator<WindowContext>,
texteres: HashMap<TextureInfo, Texture>,
next_tex_id: usize,
}
impl VideoSdl {
pub fn new(settings: &Settings) -> VideoSdl {
let mut video_subsystem = None;
SDL_CONTEXT.with(|sdl| {
video_subsystem = sdl.borrow_mut().video().ok();
});
if let Some(video) = video_subsystem {
let (width, height) = (
SCREEN_WIDTH * settings.scale,
SCREEN_HEIGHT * settings.scale,
);
let window = video
.window("RustZX", width as u32, height as u32)
.position_centered()
.opengl()
.build()
.expect("[ERROR] Sdl window build fail");
let renderer = window
.into_canvas()
.present_vsync()
.build()
.expect("[ERROR] Sdl Canvas build error");
let texture_creator = renderer.texture_creator();
VideoSdl {
renderer,
texture_creator,
texteres: HashMap::new(),
next_tex_id: 0,
}
} else {
panic!("[ERROR] Sdl video init fail!");
}
}
}
impl VideoDevice for VideoSdl {
fn gen_texture(&mut self, width: u32, height: u32) -> TextureInfo {
let id = self.next_tex_id;
let tex = self
.texture_creator
.create_texture_streaming(PixelFormat::ABGR8888, width, height)
.expect("[ERROR] Sdl texture creation error");
let tex_info = TextureInfo { id, width, height };
self.texteres.insert(tex_info, tex);
self.next_tex_id += 1;
tex_info
}
fn set_title(&mut self, title: &str) {
self.renderer.window_mut().set_title(title).unwrap();
}
fn update_texture(&mut self, tex: TextureInfo, buffer: &[u8]) {
let tex_sdl = self
.texteres
.get_mut(&tex)
.expect("[ERROR] Wrong texrure ID on update");
tex_sdl
.with_lock(None, |out, pitch| {
for y in 0..tex.height {
for x in 0..tex.width {
let offset_dest = (y * pitch as u32 + x * 4) as usize;
let offset_src = (y * tex.width * 4 + x * 4) as usize;
out[offset_dest..(4 + offset_dest)]
.clone_from_slice(&buffer[offset_src..(4 + offset_src)]);
}
}
})
.expect("[ERROR] Texture update error");
}
fn begin(&mut self) {
self.renderer.clear();
}
fn draw_texture_2d(&mut self, tex: TextureInfo, rect: Option<Rect>) {
let tex = self
.texteres
.get_mut(&tex)
.expect("[ERROR] Wrong texrure ID on draw");
let dest_rect = rect.map(|rect| SdlRect::new(rect.x, rect.y, rect.w, rect.h));
self.renderer
.copy(tex, None, dest_rect)
.expect("[ERROR] Can't draw texture");
}
fn end(&mut self) {
self.renderer.present();
}
}