use sdl2::{
pixels::{Color, PixelFormatEnum},
rect::{Point, Rect},
render::{Texture, TextureCreator, TextureValueError},
surface::Surface,
video::WindowContext,
};
pub struct RenderableCharacter {
/** The width of the character, indicating where to break into a newline */
pub width: u16,
/** The height of the character, derived from data's length divided by width */
pub height: u16,
/** The raw alpha layer of the character */
pub data: &'static [u8],
/** The offset to draw the character at */
pub offset: Point,
/** The amount to advance the x position of the cursor when drawing - f64 due to internally being used as a f64, despite being stored as f32 (only f64 to save some cpu cycles converting f32->f64) */
pub advance_width: f64,
}
/**
A trait describing a generated font.
We use traits implemented by each font because it's somehow optimized better in preliminary testing(?)
*/
#[allow(unused)]
pub trait BakedFont {
fn font_scale_y() -> f32;
fn has_char(&self, character: char) -> bool;
fn get_char_bytes(&self, character: char) -> &'static [u8];
fn get_char(&self, character: char) -> RenderableCharacter {
let bytes = self.get_char_bytes(character);
let width = u16::from_le_bytes(bytes[0..2].try_into().unwrap());
let offset_x = i32::from_le_bytes(bytes[2..6].try_into().unwrap());
let offset_y = i32::from_le_bytes(bytes[6..10].try_into().unwrap());
let advance_width = f64::from(f32::from_le_bytes(bytes[10..14].try_into().unwrap()));
let data = &bytes[14..];
let height = if data.is_empty() {
0
} else {
data.len() as u16 / width
};
RenderableCharacter {
width,
height,
offset: Point::new(offset_x, offset_y),
data,
advance_width,
}
}
}
impl RenderableCharacter {
/** Alpha value of colour is currently ignored! */
pub fn to_surface(&self, colour: Color) -> Surface<'static> {
let mut surface = Surface::new(
if self.width == 0 {
1
} else {
self.width.into()
},
if self.height == 0 {
1
} else {
self.height.into()
},
PixelFormatEnum::RGBA32,
)
.unwrap();
if self.width != 0 || self.height != 0 {
surface.with_lock_mut(|buffer: &mut [u8]| {
let mut idx: usize = 0;
for pixel in self.data {
let index = idx * 4;
if index < buffer.len() {
buffer[index] = colour.r; // Red
buffer[index + 1] = colour.g; // Green
buffer[index + 2] = colour.b; // Blue
buffer[index + 3] = *pixel; // Alpha
}
idx += 1;
}
});
} else {
// Blank
surface.with_lock_mut(|buffer: &mut [u8]| {
for index in 0..buffer.len() {
buffer[index] = 0;
}
})
}
surface
}
/** Colour Alpha Channel is ignored */
pub fn to_texture<'a>(
&self,
texture_creator: &'a TextureCreator<WindowContext>,
colour: Color,
) -> Result<Texture<'a>, TextureValueError> {
let surface = self.to_surface(colour);
surface.as_texture(texture_creator)
}
pub fn to_rect(&self, x: i32, y: i32) -> Rect {
Rect::new(
x + self.offset.x,
y + self.offset.y,
i32::from(self.width).try_into().unwrap(),
i32::from(self.height).try_into().unwrap(),
)
}
}