aboutsummaryrefslogtreecommitdiffstats
path: root/src/font.rs
blob: 8f062229e74cf5a577d957b45e364ceed6c245c7 (plain) (blame)
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
use std::sync::LazyLock;

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 */
  pub advance_width: f32,
}
/**
 A trait describing a generated font.
 We use traits implemented by each font because it's somehow optimized better in preliminary testing(?)
*/
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 = f32::from_le_bytes(bytes[10..14].try_into().unwrap());
    let data = &bytes[14..];
    let height = if data.len() == 0 {
      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(),
    )
  }
}