Skip to main content

easy_sdl3_text/
data.rs

1use crate::*;
2use std::collections::{HashMap, HashSet};
3use sdl3::{pixels::Color, render::{Canvas, Texture, TextureCreator, TextureValueError, UpdateTextureError}, video::{Window, WindowContext}, Error};
4use ab_glyph::Font;
5
6
7
8/// This holds most of the arguments to `render_text_regular()` and `render_text_subpixel()`
9/// 
10/// These arguments (fields) are each likely to not change from call to call
11pub struct TextRenderingSettings<'a, 'b, F: ThreadSafeFont> {
12	/// NOTE: for `render_text_subpixel()`, this is converted to u32 (this is don to significantly cut down on the number of character textures to rasterize and cache)
13	pub size: f32,
14	#[allow(missing_docs)]
15	pub h_align: HAlign,
16	#[allow(missing_docs)]
17	pub v_align: VAlign,
18	#[allow(missing_docs)]
19	pub foreground: Color,
20	/// This only exists for `render_text_subpixel()`, with `render_text_regular()` you can set this to whatever you want and it won't affect anything
21	pub background: Color,
22	#[allow(missing_docs)]
23	pub canvas: &'a mut Canvas<Window>,
24	#[allow(missing_docs)]
25	pub texture_creator: &'b TextureCreator<WindowContext>,
26	#[allow(missing_docs)]
27	pub text_cache: &'a mut TextCache<'b, F>,
28}
29
30impl<'a, 'b, F: ThreadSafeFont> TextRenderingSettings<'a, 'b, F> {
31	/// Creates a new `TextRenderingSettings` that is meant to be used with `render_text_regular()`, but can also be used for subpixel rendering
32	#[allow(clippy::too_many_arguments)]
33	pub fn new_regular(size: f32, h_align: impl Into<HAlign>, v_align: impl Into<VAlign>, foreground: impl Into<Color>, canvas: &'a mut Canvas<Window>, texture_creator: &'b TextureCreator<WindowContext>, text_cache: &'a mut TextCache<'b, F>) -> Self {
34		Self {
35			size,
36			h_align: h_align.into(),
37			v_align: v_align.into(),
38			foreground: foreground.into(),
39			background: Color::RGB(127, 127, 127),
40			canvas,
41			texture_creator,
42			text_cache,
43		}
44	}
45	/// Creates a new `TextRenderingSettings` that is meant to be used with `render_text_subpixel()`, but can also be used for regular rendering
46	#[allow(clippy::too_many_arguments)]
47	pub fn new_subpixel(size: u32, h_align: impl Into<HAlign>, v_align: impl Into<VAlign>, foreground: impl Into<Color>, background: impl Into<Color>, canvas: &'a mut Canvas<Window>, texture_creator: &'b TextureCreator<WindowContext>, text_cache: &'a mut TextCache<'b, F>) -> Self {
48		Self {
49			size: size as f32,
50			h_align: h_align.into(),
51			v_align: v_align.into(),
52			foreground: foreground.into(),
53			background: background.into(),
54			canvas,
55			texture_creator,
56			text_cache,
57		}
58	}
59}
60
61
62
63/// Basically just a wrapper for `ab_glyph::Font` that also implements `Send` and `Sync`. As far as I know, all ab_glyph fonts already implement Send and Sync, but the Font trait for some reason doesn't
64pub trait ThreadSafeFont: Font + Send + Sync {}
65
66impl<F: Font + Send + Sync> ThreadSafeFont for F {}
67
68
69
70/// A cache for character textures (also holds the font)
71pub struct TextCache<'a, F: ThreadSafeFont> {
72	// (char, foreground) -> (texture, width, height, x_offset, y_offset)
73	pub(crate) map_regular: HashMap<(char, Color), (Texture<'a>, u32, u32, f32, f32)>,
74	pub(crate) set_regular: HashSet<(char, Color)>,
75	// NOTE: this can kinda look a bit nicer if `size` here is replaced with usize and `size` as input for `render_text_*()` is replaced with f32 (which allows for better text scaling), but that significantly increases the number of textures to rasterize and store
76	// (char, size, foreground, background) -> (texture, width, height, x_offset, y_offset)
77	pub(crate) map_subpixel: HashMap<(char, u32, Color, Color), (Texture<'a>, u32, u32, f32, f32)>,
78	pub(crate) set_subpixel: HashSet<(char, u32, Color, Color)>,
79	pub(crate) font: F,
80}
81
82impl<'a, F: ThreadSafeFont> TextCache<'a, F> {
83	/// Creates a new TextCache
84	#[inline]
85	pub fn new(font: F) -> Self {
86		Self {
87			map_regular: HashMap::new(),
88			set_regular: HashSet::new(),
89			map_subpixel: HashMap::new(),
90			set_subpixel: HashSet::new(),
91			font,
92		}
93	}
94	/// Switches this cache to a different font (and clears the cache so the characters can be re-rendered)
95	pub fn switch_font(&mut self, new_font: F) {
96		self.font = new_font;
97		self.clear();
98	}
99	/// Clears the cache, probably should only be done if the program is actually low on ram or vram
100	pub fn clear(&mut self) {
101		self.map_regular.clear();
102		self.set_regular.clear();
103		self.map_subpixel.clear();
104		self.set_subpixel.clear();
105	}
106}
107
108
109
110/// Horizontal alignment
111#[derive(Copy, Clone)]
112pub enum HAlign {
113	/// Treats the 'x' value as the left edge
114	Left,
115	/// Treats the 'x' value as the text middle
116	Center,
117	/// Treats the 'x' value as the right edge
118	Right,
119}
120
121impl HAlign {
122	pub(crate) fn get_offset(&self, width: f32) -> f32 {
123		match self {
124			Self::Left => 0.0,
125			Self::Center => width * -0.5,
126			Self::Right => -width,
127		}
128	}
129}
130
131/// Vertical alignment
132#[derive(Copy, Clone)]
133pub enum VAlign {
134	/// Treats the 'y' value as the top edge
135	Top,
136	/// Treats the 'y' value as the text middle
137	Center,
138	/// Treats the 'y' value as the bottom edge
139	Bottom,
140}
141
142impl VAlign {
143	pub(crate) fn get_offset(&self, height: f32) -> f32 {
144		match self {
145			Self::Top => height * TEXT_HEIGHT_MULT,
146			Self::Center => height * TEXT_HEIGHT_MULT * 0.5,
147			Self::Bottom => 0.0,
148		}
149	}
150}
151
152
153
154/// A wrapper for all errors that can occur while rendering text
155#[derive(Debug)]
156pub enum RenderTextError {
157	/// Wrapper for sdl3::Error
158	SdlError (Error),
159	/// Wrapper for sdl3::texture::TextureValueError
160	SdlTextureValueError (TextureValueError),
161	/// Wrapper for sdl3::texture::UpdateTextureError
162	SdlUpdateTextureError (UpdateTextureError),
163}
164
165impl From<Error> for RenderTextError {
166	fn from(value: Error) -> Self {
167		Self::SdlError (value)
168	}
169}
170
171impl From<TextureValueError> for RenderTextError {
172	fn from(value: TextureValueError) -> Self {
173		Self::SdlTextureValueError (value)
174	}
175}
176
177impl From<UpdateTextureError> for RenderTextError {
178	fn from(value: UpdateTextureError) -> Self {
179		Self::SdlUpdateTextureError (value)
180	}
181}
182
183impl std::fmt::Display for RenderTextError {
184	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185		match self {
186			Self::SdlError (err) => write!(f, "Render Text Error: {err}"),
187			Self::SdlTextureValueError (err) => write!(f, "Render Text Error: {err}"),
188			Self::SdlUpdateTextureError (err) => write!(f, "Render Text Error: {err}"),
189		}
190	}
191}
192
193impl std::error::Error for RenderTextError {}