From f2f1d83edf406bcd2c014f671ada7fc6c88b5550 Mon Sep 17 00:00:00 2001 From: gered Date: Wed, 29 Mar 2023 15:40:37 -0400 Subject: [PATCH] update Standard system resource with variable screen size support --- ggdt/src/system/res/standard.rs | 120 +++++++++++++++++++------------- 1 file changed, 70 insertions(+), 50 deletions(-) diff --git a/ggdt/src/system/res/standard.rs b/ggdt/src/system/res/standard.rs index 700fa07..b6e229a 100644 --- a/ggdt/src/system/res/standard.rs +++ b/ggdt/src/system/res/standard.rs @@ -1,10 +1,9 @@ -use byte_slice_cast::AsByteSlice; - use crate::audio::queue::AudioQueue; use crate::audio::{Audio, TARGET_AUDIO_CHANNELS, TARGET_AUDIO_FREQUENCY}; use crate::graphics::bitmap::rgb::RgbaBitmap; use crate::graphics::font::BitmaskFont; -use crate::system::event::{SystemEvent, SystemEventHandler}; +use crate::system::event::{SystemEvent, SystemEventHandler, WindowEvent}; +use crate::system::framebuffer::{calculate_logical_screen_size, SdlFramebuffer}; use crate::system::input_devices::keyboard::Keyboard; use crate::system::input_devices::mouse::cursor::CustomMouseCursor; use crate::system::input_devices::mouse::Mouse; @@ -18,22 +17,48 @@ const DEFAULT_SCALE_FACTOR: u32 = 3; pub struct StandardConfig { screen_width: u32, screen_height: u32, + fixed_screen_size: bool, initial_scale_factor: u32, integer_scaling: bool, } +impl Default for StandardConfig { + /// Returns a new [`StandardConfig`] with a default configuration. + fn default() -> Self { + StandardConfig::fixed_screen_size(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT, false) + } +} + impl StandardConfig { - /// Returns a new [`DosLikeConfig`] with a default configuration. - pub fn new() -> Self { + /// Creates a configuration that will use a fixed screen size at a set scaling factor. Any window resizing + /// will simply scale up or down the final image on screen, but the application will always use the same + /// logical screen resolution, `screen_width` and `screen_height`, at runtime. + pub fn fixed_screen_size(screen_width: u32, screen_height: u32, integer_scaling: bool) -> Self { StandardConfig { - screen_width: DEFAULT_SCREEN_WIDTH, - screen_height: DEFAULT_SCREEN_HEIGHT, + screen_width, + screen_height, initial_scale_factor: DEFAULT_SCALE_FACTOR, - integer_scaling: false, + integer_scaling, + fixed_screen_size: true, } } - // TODO: add customization ability for setting different screen dimensions instead of it being hardcoded + /// Creates a configuration that allows the screen size to be automatically updated at runtime to match the + /// current window size, including any arbitrary user window resizing. The final image on screen will always be + /// scaled up by the factor given. The logical screen size at runtime (as seen by the application code) is + /// always based on: + /// + /// `logical_screen_width = ceil(window_width / scale_factor)` + /// `logical_screen_height = ceil(window_height / scale_factor)` + pub fn variable_screen_size(initial_width: u32, initial_height: u32) -> Self { + StandardConfig { + screen_width: initial_width, + screen_height: initial_height, + initial_scale_factor: DEFAULT_SCALE_FACTOR, + integer_scaling: false, + fixed_screen_size: false, + } + } /// Sets an integer scaling factor for the [`System`] being built to up-scale the virtual /// framebuffer to when displaying it on screen. @@ -41,13 +66,6 @@ impl StandardConfig { self.initial_scale_factor = scale_factor; self } - - /// Enables or disables restricting the final rendered output to always be integer scaled, - /// even if that result will not fully fill the area of the window. - pub fn integer_scaling(mut self, enable: bool) -> Self { - self.integer_scaling = enable; - self - } } impl SystemResourcesConfig for StandardConfig { @@ -59,8 +77,6 @@ impl SystemResourcesConfig for StandardConfig { audio_subsystem: &sdl2::AudioSubsystem, mut window: sdl2::video::Window, ) -> Result { - let texture_pixel_size = 4; // 32-bit ARGB format - let window_width = self.screen_width * self.initial_scale_factor; let window_height = self.screen_height * self.initial_scale_factor; if let Err(error) = window.set_size(window_width, window_height) { @@ -74,9 +90,6 @@ impl SystemResourcesConfig for StandardConfig { Ok(canvas) => canvas, Err(error) => return Err(SystemResourcesError::SDLError(error.to_string())), }; - if let Err(error) = sdl_canvas.set_logical_size(self.screen_width, self.screen_height) { - return Err(SystemResourcesError::SDLError(error.to_string())); - }; // TODO: newer versions of rust-sdl2 support this directly off the WindowCanvas struct unsafe { @@ -90,23 +103,14 @@ impl SystemResourcesConfig for StandardConfig { ); } - // create an SDL texture which we will be uploading to every frame to display the - // application's framebuffer + // create the SDL framebuffer at the initial logical screen size - let sdl_texture = match sdl_canvas.create_texture_streaming( - Some(sdl2::pixels::PixelFormatEnum::ARGB8888), - self.screen_width, - self.screen_height, - ) { - Ok(texture) => texture, - Err(error) => return Err(SystemResourcesError::SDLError(error.to_string())), - }; - let sdl_texture_pitch = (sdl_texture.query().width * texture_pixel_size) as usize; + let framebuffer = SdlFramebuffer::new(&mut sdl_canvas, self.screen_width, self.screen_height, false)?; // create the Bitmap object that will be exposed to the application acting as the system // backbuffer - let framebuffer = match RgbaBitmap::new(self.screen_width, self.screen_height) { + let screen_bitmap = match RgbaBitmap::new(self.screen_width, self.screen_height) { Ok(bmp) => bmp, Err(error) => return Err(SystemResourcesError::SDLError(error.to_string())), }; @@ -137,11 +141,12 @@ impl SystemResourcesConfig for StandardConfig { Ok(Standard { sdl_canvas, - sdl_texture, - sdl_texture_pitch, + framebuffer, + scale_factor: self.initial_scale_factor, + fixed_screen_size: self.fixed_screen_size, audio, audio_queue, - video: framebuffer, + video: screen_bitmap, font, keyboard, mouse, @@ -152,8 +157,9 @@ impl SystemResourcesConfig for StandardConfig { pub struct Standard { sdl_canvas: sdl2::render::WindowCanvas, - sdl_texture: sdl2::render::Texture, - sdl_texture_pitch: usize, + framebuffer: SdlFramebuffer, + scale_factor: u32, + fixed_screen_size: bool, /// An [`Audio`] instance that allows interacting with the system's audio output device. pub audio: Audio, @@ -210,19 +216,8 @@ impl SystemResources for Standard { fn display(&mut self) -> Result<(), SystemResourcesError> { self.cursor.render(&mut self.video); - - let texture_pixels = self.video.pixels().as_byte_slice(); - if let Err(error) = self.sdl_texture.update(None, texture_pixels, self.sdl_texture_pitch) { - return Err(SystemResourcesError::SDLError(error.to_string())); - } - self.sdl_canvas.clear(); - if let Err(error) = self.sdl_canvas.copy(&self.sdl_texture, None, None) { - return Err(SystemResourcesError::SDLError(error)); - } - self.sdl_canvas.present(); - + self.framebuffer.display(&mut self.sdl_canvas, &self.video)?; self.cursor.hide(&mut self.video); - Ok(()) } @@ -233,6 +228,13 @@ impl SystemResources for Standard { } fn handle_event(&mut self, event: &SystemEvent) -> Result { + if let SystemEvent::Window(WindowEvent::SizeChanged(width, height)) = event { + if !self.fixed_screen_size { + self.resize_screen(*width as u32, *height as u32)?; + } + return Ok(true); + } + if self.keyboard.handle_event(event) { return Ok(true); } @@ -252,3 +254,21 @@ impl SystemResources for Standard { self.video.height() } } + +impl Standard { + fn resize_screen(&mut self, new_width: u32, new_height: u32) -> Result<(), SystemResourcesError> { + let (logical_width, logical_height) = calculate_logical_screen_size(new_width, new_height, self.scale_factor); + + let framebuffer = SdlFramebuffer::new(&mut self.sdl_canvas, logical_width, logical_height, false)?; + + let screen_bitmap = match RgbaBitmap::new(logical_width, logical_height) { + Ok(bmp) => bmp, + Err(error) => return Err(SystemResourcesError::SDLError(error.to_string())), + }; + + self.framebuffer = framebuffer; + self.video = screen_bitmap; + + Ok(()) + } +}