use std::{fmt::Debug, rc::Rc};
use anyhow::Result;
use glam::{UVec2, Vec2};
use image::DynamicImage;
use crate::{
font::FontManager,
renderer::post::ScreenShader,
renderer::traits::{PostShader, Shader},
text::Text,
};
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub enum ScaleMode {
#[default]
Window,
Fixed {
width: u32,
height: u32,
},
FixedHeight(u32),
FixedWidth(u32),
}
#[derive(Debug, Clone)]
pub struct BackendState {
pub device: wgpu::Device,
pub queue: wgpu::Queue,
pub surface_config: wgpu::SurfaceConfiguration,
pub surface_view_format: wgpu::TextureFormat,
}
pub(crate) struct Render {
pub(crate) screen_scale: Vec2,
pub(crate) inv_screen_scale: Vec2,
pub(crate) screen_size: Vec2,
logical_size: Vec2,
scale_mode: ScaleMode,
pub(crate) dpi_scale_factor: f64,
pub(crate) backend_state: BackendState,
pub(crate) shader: Rc<dyn Shader>,
pub(crate) send_shader_data: Option<Vec<u8>>,
pub(crate) post_shader: Box<dyn PostShader>,
pub(crate) solid_shader: Rc<dyn Shader>,
pub(crate) circle_shader: Rc<dyn Shader>,
}
impl Render {
pub(crate) fn new(backend_state: BackendState) -> Self {
let renderer = Rc::new(crate::renderer::shader2d::default::DefaultShader::new(
&backend_state,
));
let solid_shader = Rc::new(crate::renderer::shader2d::solid::SolidShader::new(
&backend_state,
));
let circle_shader = Rc::new(crate::renderer::shader2d::circle::CircleShader::new(
&backend_state,
));
let post_shader = Box::new(ScreenShader::setup(&backend_state));
Self {
screen_scale: Vec2::splat(1.0),
inv_screen_scale: Vec2::splat(1.0),
screen_size: Vec2::default(),
logical_size: Vec2::default(),
scale_mode: ScaleMode::default(),
dpi_scale_factor: 1.0,
backend_state,
shader: renderer,
send_shader_data: None,
post_shader,
solid_shader,
circle_shader,
}
}
pub(crate) fn set_shader(&mut self, shader: Rc<dyn Shader>) -> Rc<dyn Shader> {
self.send_shader_data = None;
core::mem::replace(&mut self.shader, shader)
}
pub(crate) fn send_shader_data(&mut self, data: Vec<u8>) {
self.send_shader_data = Some(data);
}
pub(crate) fn set_post_shader(&mut self, post_shader: Box<dyn PostShader>) {
self.post_shader = post_shader;
}
pub(crate) fn resize(&mut self, size: UVec2, dpi_scale_factor: f64) {
self.screen_size = Vec2::new(size.x as f32, size.y as f32);
self.dpi_scale_factor = dpi_scale_factor;
match self.scale_mode {
ScaleMode::Window => {
self.screen_scale = Vec2::splat(1.0);
}
ScaleMode::Fixed { width, height } => {
self.screen_scale =
Vec2::new(size.x as f32 / width as f32, size.y as f32 / height as f32);
}
ScaleMode::FixedHeight(height) => {
self.screen_scale = Vec2::splat(size.y as f32 / height as f32);
}
ScaleMode::FixedWidth(width) => {
self.screen_scale = Vec2::splat(size.x as f32 / width as f32);
}
}
self.logical_size = (self.screen_size / self.screen_scale).ceil();
self.inv_screen_scale = 1.0 / self.screen_scale;
}
pub(crate) fn render_text_texture(
&mut self,
font_manager: &FontManager,
text: &Text,
) -> Result<(DynamicImage, UVec2)> {
let (buffer, size) = font_manager.render_text_texture(text, self.dpi_scale_factor as f32);
Ok((buffer.into(), size))
}
pub(crate) fn scale_mode(&self) -> ScaleMode {
self.scale_mode
}
pub(crate) fn set_scale_mode(&mut self, mode: ScaleMode) {
self.scale_mode = mode;
}
pub(crate) fn logical_size(&self) -> Vec2 {
self.logical_size
}
pub(crate) fn screen_size(&self) -> Vec2 {
self.screen_size
}
}