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::{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<Self::SystemResourcesType, SystemResourcesError> {
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<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) {
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(())
}
}