update Standard system resource with variable screen size support

This commit is contained in:
Gered 2023-03-29 15:40:37 -04:00
parent 7143430769
commit f2f1d83edf

View file

@ -1,10 +1,9 @@
use byte_slice_cast::AsByteSlice;
use crate::audio::queue::AudioQueue; use crate::audio::queue::AudioQueue;
use crate::audio::{Audio, TARGET_AUDIO_CHANNELS, TARGET_AUDIO_FREQUENCY}; use crate::audio::{Audio, TARGET_AUDIO_CHANNELS, TARGET_AUDIO_FREQUENCY};
use crate::graphics::bitmap::rgb::RgbaBitmap; use crate::graphics::bitmap::rgb::RgbaBitmap;
use crate::graphics::font::BitmaskFont; 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::keyboard::Keyboard;
use crate::system::input_devices::mouse::cursor::CustomMouseCursor; use crate::system::input_devices::mouse::cursor::CustomMouseCursor;
use crate::system::input_devices::mouse::Mouse; use crate::system::input_devices::mouse::Mouse;
@ -18,22 +17,48 @@ const DEFAULT_SCALE_FACTOR: u32 = 3;
pub struct StandardConfig { pub struct StandardConfig {
screen_width: u32, screen_width: u32,
screen_height: u32, screen_height: u32,
fixed_screen_size: bool,
initial_scale_factor: u32, initial_scale_factor: u32,
integer_scaling: bool, 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 { impl StandardConfig {
/// Returns a new [`DosLikeConfig`] with a default configuration. /// Creates a configuration that will use a fixed screen size at a set scaling factor. Any window resizing
pub fn new() -> Self { /// 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 { StandardConfig {
screen_width: DEFAULT_SCREEN_WIDTH, screen_width,
screen_height: DEFAULT_SCREEN_HEIGHT, screen_height,
initial_scale_factor: DEFAULT_SCALE_FACTOR, 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 /// Sets an integer scaling factor for the [`System`] being built to up-scale the virtual
/// framebuffer to when displaying it on screen. /// framebuffer to when displaying it on screen.
@ -41,13 +66,6 @@ impl StandardConfig {
self.initial_scale_factor = scale_factor; self.initial_scale_factor = scale_factor;
self 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 { impl SystemResourcesConfig for StandardConfig {
@ -59,8 +77,6 @@ impl SystemResourcesConfig for StandardConfig {
audio_subsystem: &sdl2::AudioSubsystem, audio_subsystem: &sdl2::AudioSubsystem,
mut window: sdl2::video::Window, mut window: sdl2::video::Window,
) -> Result<Self::SystemResourcesType, SystemResourcesError> { ) -> Result<Self::SystemResourcesType, SystemResourcesError> {
let texture_pixel_size = 4; // 32-bit ARGB format
let window_width = self.screen_width * self.initial_scale_factor; let window_width = self.screen_width * self.initial_scale_factor;
let window_height = self.screen_height * 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) { if let Err(error) = window.set_size(window_width, window_height) {
@ -74,9 +90,6 @@ impl SystemResourcesConfig for StandardConfig {
Ok(canvas) => canvas, Ok(canvas) => canvas,
Err(error) => return Err(SystemResourcesError::SDLError(error.to_string())), 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 // TODO: newer versions of rust-sdl2 support this directly off the WindowCanvas struct
unsafe { unsafe {
@ -90,23 +103,14 @@ impl SystemResourcesConfig for StandardConfig {
); );
} }
// create an SDL texture which we will be uploading to every frame to display the // create the SDL framebuffer at the initial logical screen size
// application's framebuffer
let sdl_texture = match sdl_canvas.create_texture_streaming( let framebuffer = SdlFramebuffer::new(&mut sdl_canvas, self.screen_width, self.screen_height, false)?;
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;
// create the Bitmap object that will be exposed to the application acting as the system // create the Bitmap object that will be exposed to the application acting as the system
// backbuffer // 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, Ok(bmp) => bmp,
Err(error) => return Err(SystemResourcesError::SDLError(error.to_string())), Err(error) => return Err(SystemResourcesError::SDLError(error.to_string())),
}; };
@ -137,11 +141,12 @@ impl SystemResourcesConfig for StandardConfig {
Ok(Standard { Ok(Standard {
sdl_canvas, sdl_canvas,
sdl_texture, framebuffer,
sdl_texture_pitch, scale_factor: self.initial_scale_factor,
fixed_screen_size: self.fixed_screen_size,
audio, audio,
audio_queue, audio_queue,
video: framebuffer, video: screen_bitmap,
font, font,
keyboard, keyboard,
mouse, mouse,
@ -152,8 +157,9 @@ impl SystemResourcesConfig for StandardConfig {
pub struct Standard { pub struct Standard {
sdl_canvas: sdl2::render::WindowCanvas, sdl_canvas: sdl2::render::WindowCanvas,
sdl_texture: sdl2::render::Texture, framebuffer: SdlFramebuffer,
sdl_texture_pitch: usize, scale_factor: u32,
fixed_screen_size: bool,
/// An [`Audio`] instance that allows interacting with the system's audio output device. /// An [`Audio`] instance that allows interacting with the system's audio output device.
pub audio: Audio, pub audio: Audio,
@ -210,19 +216,8 @@ impl SystemResources for Standard {
fn display(&mut self) -> Result<(), SystemResourcesError> { fn display(&mut self) -> Result<(), SystemResourcesError> {
self.cursor.render(&mut self.video); self.cursor.render(&mut self.video);
self.framebuffer.display(&mut self.sdl_canvas, &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.cursor.hide(&mut self.video); self.cursor.hide(&mut self.video);
Ok(()) Ok(())
} }
@ -233,6 +228,13 @@ impl SystemResources for Standard {
} }
fn handle_event(&mut self, event: &SystemEvent) -> Result<bool, SystemResourcesError> { fn handle_event(&mut self, event: &SystemEvent) -> Result<bool, SystemResourcesError> {
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) { if self.keyboard.handle_event(event) {
return Ok(true); return Ok(true);
} }
@ -252,3 +254,21 @@ impl SystemResources for Standard {
self.video.height() 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(())
}
}