use crate::color::Rgba8;
use crate::core::{Context, Window};
use crate::gfx::{Draw, Surface, TextureFormat};
use crate::math::{Numeric, RectF, Vec2F, Vec2U};
#[cfg(feature = "lua")]
pub type ScreenObj = fey_lua::UserDataOf<Screen>;
#[cfg(feature = "lua")]
pub type ScreenRef = mlua::UserDataRef<Screen>;
#[cfg(feature = "lua")]
pub type ScreenMut = mlua::UserDataRefMut<Screen>;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ScreenMode {
Frame { size: Vec2U, fractional: bool },
Fill { scale: f32 },
}
impl ScreenMode {
#[inline]
pub fn frame(size: impl Into<Vec2U>, fractional: bool) -> Self {
Self::Frame {
size: size.into(),
fractional,
}
}
#[inline]
pub const fn fill(scale: f32) -> Self {
Self::Fill { scale }
}
}
#[derive(Debug)]
pub struct Screen {
surface: Surface,
#[cfg(feature = "lua")]
surface_userdata: mlua::AnyUserData,
pub mode: ScreenMode,
scr_rect: RectF,
win_rect: RectF,
scale: f32,
mouse_pos: Vec2F,
}
fn surface_size(window: &Window, scale: f32) -> Vec2U {
assert!(scale > 0.0);
let scale = scale * window.scale_factor();
window.pixel_size() / scale.to_u32()
}
impl Screen {
pub fn new(ctx: &Context, mode: ScreenMode) -> Self {
let size = match mode {
ScreenMode::Frame { size, .. } => {
assert_ne!(size.x, 0);
assert_ne!(size.y, 0);
size
}
ScreenMode::Fill { scale } => surface_size(&ctx.window, scale),
};
let surface = ctx.graphics.create_surface(size, TextureFormat::Rgba8);
let mut screen = Self {
#[cfg(feature = "lua")]
surface_userdata: {
let lua = ctx.lua.upgrade();
lua.create_userdata(surface.clone()).unwrap()
},
surface,
mode,
scr_rect: RectF::ZERO,
win_rect: RectF::ZERO,
scale: 0.0,
mouse_pos: Vec2F::ZERO,
};
screen.update(ctx);
screen
}
pub fn new_frame(ctx: &Context, size: impl Into<Vec2U>, fractional: bool) -> Self {
Self::new(ctx, ScreenMode::frame(size, fractional))
}
pub fn new_fill(ctx: &Context, scale: f32) -> Self {
Self::new(ctx, ScreenMode::fill(scale))
}
#[inline]
pub fn surface(&self) -> &Surface {
&self.surface
}
#[cfg(feature = "lua")]
pub fn surface_userdata(&self) -> &mlua::AnyUserData {
&self.surface_userdata
}
#[inline]
pub fn size(&self) -> Vec2U {
self.surface.size()
}
#[inline]
pub fn width(&self) -> u32 {
self.surface.width()
}
#[inline]
pub fn height(&self) -> u32 {
self.surface.height()
}
#[inline]
pub fn window_rect(&self) -> &RectF {
&self.win_rect
}
#[inline]
pub fn scale(&self) -> f32 {
self.scale
}
#[inline]
pub fn mouse_pos(&self) -> Vec2F {
self.mouse_pos
}
#[inline]
pub fn mouse_x(&self) -> f32 {
self.mouse_pos.x
}
#[inline]
pub fn mouse_y(&self) -> f32 {
self.mouse_pos.y
}
#[inline]
pub fn update(&mut self, ctx: &Context) {
let (scr_size, fractional) = match self.mode {
ScreenMode::Frame { size, fractional } => (size, fractional),
ScreenMode::Fill { scale } => (surface_size(&ctx.window, scale), true),
};
assert_ne!(scr_size, Vec2U::ZERO);
if self.surface.size() != scr_size {
self.surface = ctx.graphics.create_surface(scr_size, TextureFormat::Rgba8);
}
let scr_size = scr_size.to_f32();
self.scr_rect = RectF::sized(scr_size);
let win_size = ctx.window.size().to_f32();
let (win_rect, scale) = RectF::sized(win_size).fitted(scr_size, fractional);
self.win_rect = win_rect;
self.scale = scale;
self.mouse_pos = win_rect.map_pos(ctx.mouse.pos(), &self.scr_rect).round();
}
#[inline]
pub fn map_pos(&self, pos: Vec2F) -> Vec2F {
self.win_rect.map_pos(pos, &self.scr_rect)
}
#[inline]
pub fn set_as_draw_surface(&self, draw: &mut Draw, clear_color: impl Into<Option<Rgba8>>) {
draw.set_surface(Some(self.surface.clone()), clear_color.into());
}
#[inline]
pub fn draw_to_window(&self, draw: &mut Draw, clear_color: impl Into<Option<Rgba8>>) {
draw.set_surface(None, clear_color.into());
draw.textured_quad(&self.surface, self.win_rect);
}
}