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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
//! Graphics renderer functions.

use crate::{error::Result, image::Icon, prelude::*};
use std::num::NonZeroUsize;

pub(crate) use crate::{texture::TextureRenderer, window::WindowRenderer};

#[cfg(not(target_arch = "wasm32"))]
pub mod sdl;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) use sdl::Renderer;

#[cfg(target_arch = "wasm32")]
pub mod wasm;
#[cfg(target_arch = "wasm32")]
pub(crate) use wasm::Renderer;

const TEXTURE_CACHE_SIZE: usize = 256;
const TEXT_CACHE_SIZE: usize = 512;

/// Settings used to set up the renderer.
#[derive(Debug, Clone)]
pub(crate) struct RendererSettings {
    /// Base window title.
    pub(crate) title: String,
    /// Application icon.
    pub(crate) icon: Option<Icon>,
    /// Starting window X coordinate.
    pub(crate) x: Position,
    /// Starting window Y coordinate.
    pub(crate) y: Position,
    /// Starting window width.
    pub(crate) width: u32,
    /// Starting window height.
    pub(crate) height: u32,
    /// Rendering scale for x-coordinates.
    pub(crate) scale_x: f32,
    /// Rendering scale for y-coordinates.
    pub(crate) scale_y: f32,
    /// Audio queue sample rate. `None` uses device default.
    pub(crate) audio_sample_rate: Option<i32>,
    /// Audio queue channel count. 1 for mono, 2 for stereo, etc. `None` uses device default.
    pub(crate) audio_channels: Option<u8>,
    /// Audio queue buffer size. `None` uses devide default.
    pub(crate) audio_buffer_size: Option<u16>,
    /// Window fullscreen mode.
    pub(crate) fullscreen: bool,
    /// Sync [`Engine::on_update`] rate with monitor refresh rate.
    pub(crate) vsync: bool,
    /// Enable window resizing.
    pub(crate) resizable: bool,
    /// Disable window borders.
    pub(crate) borderless: bool,
    /// Enable high resolution mode, if supported.
    pub(crate) allow_highdpi: bool,
    /// Hide window.
    pub(crate) hidden: bool,
    /// Show frame rate per second in title bar.
    pub(crate) show_frame_rate: bool,
    /// Limit [`Engine::on_update`] to target frame frate per second.
    pub(crate) target_frame_rate: Option<usize>,
    /// Size of allowed texture cache before least-used entries are evicted.
    pub(crate) texture_cache_size: NonZeroUsize,
    /// Size of allowed font cache before least-used entries are evicted.
    pub(crate) text_cache_size: NonZeroUsize,
}

impl Default for RendererSettings {
    #[allow(clippy::expect_used)]
    fn default() -> Self {
        Self {
            title: String::new(),
            icon: None,
            x: Position::default(),
            y: Position::default(),
            width: 640,
            height: 480,
            scale_x: 1.0,
            scale_y: 1.0,
            audio_sample_rate: None,
            audio_channels: None,
            audio_buffer_size: None,
            fullscreen: false,
            vsync: false,
            resizable: false,
            borderless: false,
            allow_highdpi: false,
            hidden: false,
            show_frame_rate: false,
            target_frame_rate: None,
            texture_cache_size: TEXTURE_CACHE_SIZE.try_into().expect("valid cache size"),
            text_cache_size: TEXT_CACHE_SIZE.try_into().expect("valid cache size"),
        }
    }
}

/// Trait for operations on the underlying `Renderer`.
pub(crate) trait Rendering: Sized {
    /// Creates a new Renderer instance.
    fn new(settings: RendererSettings) -> Result<Self>;

    /// Clears the current canvas to the given clear color
    fn clear(&mut self) -> Result<()>;

    /// Sets the color used by the renderer to draw the current canvas.
    fn set_draw_color(&mut self, color: Color) -> Result<()>;

    /// Sets the clip rect used by the renderer to draw to the current canvas.
    fn clip(&mut self, rect: Option<Rect<i32>>) -> Result<()>;

    /// Sets the blend mode used by the renderer to drawing.
    fn blend_mode(&mut self, mode: BlendMode);

    /// Updates the canvas from the current back buffer.
    fn present(&mut self);

    /// Set the rendering scale of the current canvas. Drawing coordinates are scaled by x/y
    /// factors before being drawn to the canvas.
    fn scale(&mut self, x: f32, y: f32) -> Result<()>;

    /// Set the font size for drawing text to the current canvas.
    fn font_size(&mut self, size: u32) -> Result<()>;

    /// Set the font style for drawing text to the current canvas.
    fn font_style(&mut self, style: FontStyle);

    /// Set the font family for drawing text to the current canvas.
    fn font_family(&mut self, font: &Font) -> Result<()>;

    /// Get clipboard text from the system clipboard.
    fn clipboard_text(&self) -> String;

    /// Set clipboard text to the system clipboard.
    fn set_clipboard_text(&self, value: &str) -> Result<()>;

    /// Open a URL in the default system browser.
    fn open_url(&self, url: &str) -> Result<()>;

    /// Draw text to the current canvas. `angle` must be in degrees.
    #[allow(clippy::too_many_arguments)]
    fn text(
        &mut self,
        position: Point<i32>,
        text: &str,
        wrap_width: Option<u32>,
        angle: Option<f64>,
        center: Option<Point<i32>>,
        flipped: Option<Flipped>,
        fill: Option<Color>,
        outline: u16,
    ) -> Result<(u32, u32)>;

    /// Returns the rendered dimensions of the given text using the current font
    /// as `(width, height)`.
    fn size_of(&self, text: &str, wrap_width: Option<u32>) -> Result<(u32, u32)>;

    /// Draw a pixel to the current canvas.
    fn point(&mut self, p: Point<i32>, color: Color) -> Result<()>;

    /// Draw a line to the current canvas.
    fn line(&mut self, line: Line<i32>, smooth: bool, width: u8, color: Color) -> Result<()>;

    /// Draw a cubic Bezier curve to the current canvas.
    fn bezier<I>(&mut self, ps: I, detail: i32, stroke: Option<Color>) -> Result<()>
    where
        I: Iterator<Item = Point<i32>>;

    /// Draw a triangle to the current canvas.
    fn triangle(
        &mut self,
        tri: Tri<i32>,
        smooth: bool,
        fill: Option<Color>,
        stroke: Option<Color>,
    ) -> Result<()>;

    /// Draw a rectangle to the current canvas.
    fn rect(
        &mut self,
        rect: Rect<i32>,
        radius: Option<i32>,
        fill: Option<Color>,
        stroke: Option<Color>,
    ) -> Result<()>;

    /// Draw a quadrilateral to the current canvas.
    fn quad(
        &mut self,
        quad: Quad<i32>,
        smooth: bool,
        fill: Option<Color>,
        stroke: Option<Color>,
    ) -> Result<()>;

    /// Draw a polygon to the current canvas.
    fn polygon<I>(
        &mut self,
        ps: I,
        smooth: bool,
        fill: Option<Color>,
        stroke: Option<Color>,
    ) -> Result<()>
    where
        I: Iterator<Item = Point<i32>>;

    /// Draw a ellipse to the current canvas.
    fn ellipse(
        &mut self,
        ellipse: Ellipse<i32>,
        smooth: bool,
        fill: Option<Color>,
        stroke: Option<Color>,
    ) -> Result<()>;

    /// Draw an arc to the current canvas.
    #[allow(clippy::too_many_arguments)]
    fn arc(
        &mut self,
        p: Point<i32>,
        radius: i32,
        start: i32,
        end: i32,
        mode: ArcMode,
        fill: Option<Color>,
        stroke: Option<Color>,
    ) -> Result<()>;

    /// Draw an image to the current canvas, optionally rotated about a `center`, flipped or
    /// tinted. `angle` must be in degrees.
    #[allow(clippy::too_many_arguments)]
    fn image(
        &mut self,
        img: &Image,
        src: Option<Rect<i32>>,
        dst: Option<Rect<i32>>,
        angle: f64,
        center: Option<Point<i32>>,
        flipped: Option<Flipped>,
        tint: Option<Color>,
    ) -> Result<()>;

    /// Return the current rendered target pixels as an array of bytes.
    fn to_bytes(&mut self) -> Result<Vec<u8>>;

    /// Connect a controller with the given joystick index to start receiving events.
    fn open_controller(&mut self, controller_id: ControllerId) -> Result<()>;

    /// Disconnect a controller with the given joystick index to stop receiving events.
    fn close_controller(&mut self, controller_id: ControllerId);
}