roast2d_internal 0.3.0-alpha.4

Roast2D internal crate
Documentation
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),
}

/// Backend state, used to create shaders or other wgpu resources
#[derive(Debug, Clone)]
pub struct BackendState {
    pub device: wgpu::Device,
    pub queue: wgpu::Queue,
    pub surface_config: wgpu::SurfaceConfiguration,
}

/// Render subsystem
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,
        }
    }

    /// Set the sprite 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)
    }

    /// Send the renderer args
    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;
        // calculate scale
        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
    }
}