use std::{fmt::Debug, rc::Rc};
use anyhow::Result;
use glam::{UVec2, Vec2};
use image::DynamicImage;
use crate::{
font::FontManager,
renderer::{
circle::CircleRenderer,
default::DefaultRenderer,
line::LineRenderer,
screen::ScreenRenderer,
traits::{PostRenderer, SpriteRenderer},
},
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(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) inv_dpi_scale_factor: f64,
pub(crate) backend_state: BackendState,
pub(crate) renderer: Rc<dyn SpriteRenderer>,
pub(crate) send_renderer_data: Option<Vec<u8>>,
pub(crate) post_renderer: Box<dyn PostRenderer>,
pub(crate) line_renderer: Rc<dyn SpriteRenderer>,
pub(crate) circle_renderer: Rc<dyn SpriteRenderer>,
}
impl Render {
pub(crate) fn new(backend_state: BackendState) -> Self {
let renderer = Rc::new(DefaultRenderer::new(&backend_state));
let line_renderer = Rc::new(LineRenderer::new(&backend_state));
let circle_renderer = Rc::new(CircleRenderer::new(&backend_state));
let post_renderer = Box::new(ScreenRenderer::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,
inv_dpi_scale_factor: 1.0,
backend_state,
renderer,
send_renderer_data: None,
post_renderer,
line_renderer,
circle_renderer,
}
}
pub(crate) fn set_renderer(
&mut self,
renderer: Rc<dyn SpriteRenderer>,
) -> Rc<dyn SpriteRenderer> {
self.send_renderer_data = None;
core::mem::replace(&mut self.renderer, renderer)
}
pub(crate) fn send_renderer_data(&mut self, data: Vec<u8>) {
self.send_renderer_data = Some(data);
}
pub(crate) fn set_post_renderer(&mut self, post_renderer: Box<dyn PostRenderer>) {
self.post_renderer = post_renderer;
}
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;
self.inv_dpi_scale_factor = 1.0 / 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,
self.inv_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
}
}