1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! Functions and types relating to screen scaling.

use crate::graphics::Rectangle;

/// Defines the different ways that a game's screen can be scaled.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ScreenScaling {
    /// The game will always be displayed at its native resolution, with no scaling applied.
    /// If the window is bigger than the native resolution, letterboxing will be applied.
    /// If the window is smaller than the native resolution, it will be cropped.
    None,

    /// The screen will be stretched to fill the window, without trying to preserve the original
    /// aspect ratio. Distortion/stretching/squashing may occur.
    Stretch,

    /// The entire screen will be displayed as large as possible while maintaining the original
    /// aspect ratio. Letterboxing may occur.
    ShowAll,

    /// Works the same as ShowAll, but will only scale by integer values.
    ShowAllPixelPerfect,

    /// The screen will fill the entire window, maintaining the original aspect ratio but
    /// potentially being cropped.
    Crop,

    /// Works the same as Crop, but will only scale by integer values.
    CropPixelPerfect,

    /// The screen will resize to match the size of the window. More of the scene will be shown on
    /// bigger windows, and less of the scene will be shown on smaller windows.
    ///
    /// If the scaling mode changes, the internal resolution will return to its original value.
    Resize,
}

impl ScreenScaling {
    pub(crate) fn get_screen_rect(
        self,
        internal_width: i32,
        internal_height: i32,
        window_width: i32,
        window_height: i32,
    ) -> Rectangle {
        let f_internal_width = internal_width as f32;
        let f_internal_height = internal_height as f32;
        let f_window_width = window_width as f32;
        let f_window_height = window_height as f32;

        let internal_aspect_ratio = f_internal_width / f_internal_height;
        let screen_aspect_ratio = f_window_width / f_window_height;

        match self {
            ScreenScaling::None => {
                let screen_x = (window_width - internal_width) / 2;
                let screen_y = (window_height - internal_height) / 2;

                Rectangle::new(
                    screen_x as f32,
                    screen_y as f32,
                    internal_width as f32,
                    internal_height as f32,
                )
            }
            ScreenScaling::Stretch | ScreenScaling::Resize => {
                Rectangle::new(0.0, 0.0, window_width as f32, window_height as f32)
            }
            ScreenScaling::ShowAll => {
                let scale_factor = if internal_aspect_ratio > screen_aspect_ratio {
                    f_window_width / f_internal_width
                } else {
                    f_window_height / f_internal_height
                };

                let screen_width = (f_internal_width * scale_factor).ceil();
                let screen_height = (f_internal_height * scale_factor).ceil();
                let screen_x = ((f_window_width - screen_width) / 2.0).ceil();
                let screen_y = ((f_window_height - screen_height) / 2.0).ceil();

                Rectangle::new(screen_x, screen_y, screen_width, screen_height)
            }
            ScreenScaling::ShowAllPixelPerfect => {
                let mut scale_factor = if internal_aspect_ratio > screen_aspect_ratio {
                    window_width / internal_width
                } else {
                    window_height / internal_height
                };

                if scale_factor == 0 {
                    scale_factor = 1;
                }

                let screen_width = internal_width * scale_factor;
                let screen_height = internal_height * scale_factor;
                let screen_x = (window_width - screen_width) / 2;
                let screen_y = (window_height - screen_height) / 2;

                Rectangle::new(
                    screen_x as f32,
                    screen_y as f32,
                    screen_width as f32,
                    screen_height as f32,
                )
            }
            ScreenScaling::Crop => {
                let scale_x = f_window_width / f_internal_width;
                let scale_y = f_window_height / f_internal_height;
                let scale_factor = scale_x.max(scale_y);

                let screen_width = (f_internal_width * scale_factor).ceil();
                let screen_height = (f_internal_height * scale_factor).ceil();
                let screen_x = ((f_window_width - screen_width) / 2.0).ceil();
                let screen_y = ((f_window_height - screen_height) / 2.0).ceil();

                Rectangle::new(screen_x, screen_y, screen_width, screen_height)
            }
            ScreenScaling::CropPixelPerfect => {
                let mut scale_factor = if internal_aspect_ratio > screen_aspect_ratio {
                    f_window_height / f_internal_height
                } else {
                    f_window_width / f_internal_width
                }
                .ceil() as i32;

                if scale_factor == 0 {
                    scale_factor = 1;
                }

                let screen_width = internal_width * scale_factor;
                let screen_height = internal_height * scale_factor;
                let screen_x = (window_width - screen_width) / 2;
                let screen_y = (window_height - screen_height) / 2;

                Rectangle::new(
                    screen_x as f32,
                    screen_y as f32,
                    screen_width as f32,
                    screen_height as f32,
                )
            }
        }
    }
}