use crate::error::Result;
use crate::graphics::{self, Canvas, DrawParams, Rectangle};
use crate::input;
use crate::math::Vec2;
use crate::window;
use crate::Context;
#[derive(Debug)]
pub struct ScreenScaler {
canvas: Canvas,
mode: ScalingMode,
screen_rect: Rectangle,
outer_width: i32,
outer_height: i32,
}
impl ScreenScaler {
pub fn new(
ctx: &mut Context,
inner_width: i32,
inner_height: i32,
outer_width: i32,
outer_height: i32,
mode: ScalingMode,
) -> Result<ScreenScaler> {
let canvas = Canvas::new(ctx, inner_width, inner_height)?;
let screen_rect =
get_screen_rect(mode, inner_width, inner_height, outer_width, outer_height);
Ok(ScreenScaler {
canvas,
mode,
screen_rect,
outer_width,
outer_height,
})
}
pub fn with_window_size(
ctx: &mut Context,
inner_width: i32,
inner_height: i32,
mode: ScalingMode,
) -> Result<ScreenScaler> {
let (outer_width, outer_height) = window::get_size(ctx);
ScreenScaler::new(
ctx,
inner_width,
inner_height,
outer_width,
outer_height,
mode,
)
}
pub fn draw(&self, ctx: &mut Context) {
graphics::set_texture(ctx, &self.canvas.texture);
graphics::push_quad(
ctx,
self.screen_rect.x,
self.screen_rect.y,
self.screen_rect.x + self.screen_rect.width,
self.screen_rect.y + self.screen_rect.height,
0.0,
0.0,
1.0,
1.0,
&DrawParams::new(),
);
}
pub fn set_outer_size(&mut self, outer_width: i32, outer_height: i32) {
if outer_width != self.outer_width || outer_height != self.outer_height {
self.outer_width = outer_width;
self.outer_height = outer_height;
self.screen_rect = get_screen_rect(
self.mode,
self.canvas().width(),
self.canvas().height(),
outer_width,
outer_height,
);
}
}
pub fn canvas(&self) -> &Canvas {
&self.canvas
}
pub fn mode(&self) -> ScalingMode {
self.mode
}
pub fn set_mode(&mut self, mode: ScalingMode) {
self.mode = mode;
self.screen_rect = get_screen_rect(
self.mode,
self.canvas().width(),
self.canvas().height(),
self.outer_width,
self.outer_height,
);
}
pub fn project(&self, position: Vec2<f32>) -> Vec2<f32> {
let (width, height) = self.canvas().size();
Vec2::new(
project_impl(
position.x,
self.screen_rect.x,
self.screen_rect.width,
width as f32,
),
project_impl(
position.y,
self.screen_rect.y,
self.screen_rect.height,
height as f32,
),
)
}
pub fn unproject(&self, position: Vec2<f32>) -> Vec2<f32> {
let (width, height) = self.canvas().size();
Vec2::new(
unproject_impl(
position.x,
self.screen_rect.x,
self.screen_rect.width,
width as f32,
),
unproject_impl(
position.y,
self.screen_rect.y,
self.screen_rect.height,
height as f32,
),
)
}
pub fn mouse_position(&self, ctx: &Context) -> Vec2<f32> {
self.project(input::get_mouse_position(ctx))
}
pub fn mouse_x(&self, ctx: &Context) -> f32 {
let width = self.canvas().width();
project_impl(
input::get_mouse_x(ctx),
self.screen_rect.x,
self.screen_rect.width,
width as f32,
)
}
pub fn mouse_y(&self, ctx: &Context) -> f32 {
let height = self.canvas().height();
project_impl(
input::get_mouse_y(ctx),
self.screen_rect.y,
self.screen_rect.height,
height as f32,
)
}
}
fn project_impl(window_pos: f32, rect_pos: f32, rect_size: f32, real_size: f32) -> f32 {
(real_size * (window_pos - rect_pos)) / rect_size
}
fn unproject_impl(screen_pos: f32, rect_pos: f32, rect_size: f32, real_size: f32) -> f32 {
rect_pos + ((rect_size * screen_pos) / real_size)
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ScalingMode {
Fixed,
Stretch,
ShowAll,
ShowAllPixelPerfect,
Crop,
CropPixelPerfect,
}
pub fn get_screen_rect(
mode: ScalingMode,
inner_width: i32,
inner_height: i32,
outer_width: i32,
outer_height: i32,
) -> Rectangle {
let f_inner_width = inner_width as f32;
let f_inner_height = inner_height as f32;
let f_outer_width = outer_width as f32;
let f_outer_height = outer_height as f32;
let internal_aspect_ratio = f_inner_width / f_inner_height;
let screen_aspect_ratio = f_outer_width / f_outer_height;
match mode {
ScalingMode::Fixed => {
let screen_x = (outer_width - inner_width) / 2;
let screen_y = (outer_height - inner_height) / 2;
Rectangle::new(
screen_x as f32,
screen_y as f32,
inner_width as f32,
inner_height as f32,
)
}
ScalingMode::Stretch => Rectangle::new(0.0, 0.0, outer_width as f32, outer_height as f32),
ScalingMode::ShowAll => {
let scale_factor = if internal_aspect_ratio > screen_aspect_ratio {
f_outer_width / f_inner_width
} else {
f_outer_height / f_inner_height
};
let screen_width = (f_inner_width * scale_factor).ceil();
let screen_height = (f_inner_height * scale_factor).ceil();
let screen_x = ((f_outer_width - screen_width) / 2.0).ceil();
let screen_y = ((f_outer_height - screen_height) / 2.0).ceil();
Rectangle::new(screen_x, screen_y, screen_width, screen_height)
}
ScalingMode::ShowAllPixelPerfect => {
let mut scale_factor = if internal_aspect_ratio > screen_aspect_ratio {
outer_width / inner_width
} else {
outer_height / inner_height
};
if scale_factor == 0 {
scale_factor = 1;
}
let screen_width = inner_width * scale_factor;
let screen_height = inner_height * scale_factor;
let screen_x = (outer_width - screen_width) / 2;
let screen_y = (outer_height - screen_height) / 2;
Rectangle::new(
screen_x as f32,
screen_y as f32,
screen_width as f32,
screen_height as f32,
)
}
ScalingMode::Crop => {
let scale_x = f_outer_width / f_inner_width;
let scale_y = f_outer_height / f_inner_height;
let scale_factor = scale_x.max(scale_y);
let screen_width = (f_inner_width * scale_factor).ceil();
let screen_height = (f_inner_height * scale_factor).ceil();
let screen_x = ((f_outer_width - screen_width) / 2.0).ceil();
let screen_y = ((f_outer_height - screen_height) / 2.0).ceil();
Rectangle::new(screen_x, screen_y, screen_width, screen_height)
}
ScalingMode::CropPixelPerfect => {
let mut scale_factor = if internal_aspect_ratio > screen_aspect_ratio {
f_outer_height / f_inner_height
} else {
f_outer_width / f_inner_width
}
.ceil() as i32;
if scale_factor == 0 {
scale_factor = 1;
}
let screen_width = inner_width * scale_factor;
let screen_height = inner_height * scale_factor;
let screen_x = (outer_width - screen_width) / 2;
let screen_y = (outer_height - screen_height) / 2;
Rectangle::new(
screen_x as f32,
screen_y as f32,
screen_width as f32,
screen_height as f32,
)
}
}
}