ft.rs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 Dyne.org foundation
  4. *
  5. * This program is free sofreetypeware: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Sofreetypeware Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use freetype::face::LoadFlag as FtLoadFlag;
  19. use std::sync::Arc;
  20. pub type FreetypeFace = freetype::Face<&'static [u8]>;
  21. pub type SpritePtr = Arc<Sprite>;
  22. pub struct Sprite {
  23. pub bmp: Vec<u8>,
  24. pub bmp_width: usize,
  25. pub bmp_height: usize,
  26. pub bearing_x: f32,
  27. pub bearing_y: f32,
  28. pub has_fixed_sizes: bool,
  29. pub has_color: bool,
  30. }
  31. fn load_ft_glyph<'a>(
  32. face: &'a FreetypeFace,
  33. glyph_id: u32,
  34. flags: FtLoadFlag,
  35. ) -> Option<&'a freetype::GlyphSlot> {
  36. //debug!("load_glyph {} flags={flags:?}", glyph_id);
  37. if let Err(err) = face.load_glyph(glyph_id, flags) {
  38. error!(target: "text", "error loading glyph {glyph_id}: {err}");
  39. return None
  40. }
  41. //debug!("load_glyph {} [done]", glyph_id);
  42. // https://gist.github.com/jokertarot/7583938?permalink_comment_id=3327566#gistcomment-3327566
  43. let glyph = face.glyph();
  44. glyph.render_glyph(freetype::RenderMode::Normal).ok()?;
  45. Some(glyph)
  46. }
  47. pub fn render_glyph(face: &FreetypeFace, glyph_id: u32) -> Option<Sprite> {
  48. // If color is available then attempt to load it.
  49. // Otherwise fallback to black and white.
  50. let glyph = if face.has_color() {
  51. match load_ft_glyph(face, glyph_id, FtLoadFlag::DEFAULT | FtLoadFlag::COLOR) {
  52. Some(glyph) => glyph,
  53. None => load_ft_glyph(face, glyph_id, FtLoadFlag::DEFAULT)?,
  54. }
  55. } else {
  56. load_ft_glyph(face, glyph_id, FtLoadFlag::DEFAULT)?
  57. };
  58. let bmp = glyph.bitmap();
  59. let buffer = bmp.buffer();
  60. let bmp_width = bmp.width() as usize;
  61. let bmp_height = bmp.rows() as usize;
  62. let bearing_x = glyph.bitmap_left() as f32;
  63. let bearing_y = glyph.bitmap_top() as f32;
  64. let has_fixed_sizes = face.has_fixed_sizes();
  65. let pixel_mode = bmp.pixel_mode().unwrap();
  66. let bmp = match pixel_mode {
  67. freetype::bitmap::PixelMode::Bgra => {
  68. let mut tdata = vec![];
  69. tdata.resize(4 * bmp_width * bmp_height, 0);
  70. // Convert from BGRA to RGBA
  71. for i in 0..bmp_width * bmp_height {
  72. let idx = i * 4;
  73. let b = buffer[idx];
  74. let g = buffer[idx + 1];
  75. let r = buffer[idx + 2];
  76. let a = buffer[idx + 3];
  77. tdata[idx] = r;
  78. tdata[idx + 1] = g;
  79. tdata[idx + 2] = b;
  80. tdata[idx + 3] = a;
  81. }
  82. tdata
  83. }
  84. freetype::bitmap::PixelMode::Gray => {
  85. // Convert from greyscale to RGBA8
  86. let tdata: Vec<_> =
  87. buffer.iter().flat_map(|coverage| vec![255, 255, 255, *coverage]).collect();
  88. tdata
  89. }
  90. freetype::bitmap::PixelMode::Mono => {
  91. // Convert from mono to RGBA8
  92. let tdata: Vec<_> =
  93. buffer.iter().flat_map(|coverage| vec![255, 255, 255, *coverage]).collect();
  94. tdata
  95. }
  96. _ => panic!("unsupport pixel mode: {:?}", pixel_mode),
  97. };
  98. Some(Sprite {
  99. bmp,
  100. bmp_width,
  101. bmp_height,
  102. bearing_x,
  103. bearing_y,
  104. has_fixed_sizes,
  105. has_color: face.has_color(),
  106. })
  107. }
  108. #[cfg(test)]
  109. mod tests {
  110. use super::*;
  111. #[test]
  112. fn render_simple() {
  113. let ftlib = freetype::Library::init().unwrap();
  114. let font_data = include_bytes!("../../ibm-plex-mono-regular.otf") as &[u8];
  115. let face = ftlib.new_memory_face2(font_data, 0).unwrap();
  116. // glyph 11 in IBM plex mono regular is 'h'
  117. let glyph = render_glyph(&face, 11).unwrap();
  118. }
  119. #[test]
  120. fn render_custom_glyph() {
  121. let ftlib = freetype::Library::init().unwrap();
  122. let font_data = include_bytes!("../../darkirc-emoji-svg.ttf") as &[u8];
  123. let face = ftlib.new_memory_face2(font_data, 0).unwrap();
  124. let glyph = render_glyph(&face, 4).unwrap();
  125. }
  126. }