text2.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. use async_lock::Mutex;
  2. use freetype as ft;
  3. use miniquad::TextureId;
  4. use std::{
  5. collections::HashMap,
  6. sync::{Arc, Weak},
  7. };
  8. use crate::{
  9. error::Result,
  10. gfx2::{Rectangle, RenderApiPtr},
  11. util::ansi_texture,
  12. };
  13. // From https://sourceforge.net/projects/freetype/files/freetype2/2.6/
  14. //
  15. // * An `FT_Face' object can only be safely used from one thread at
  16. // a time.
  17. //
  18. // * An `FT_Library' object can now be used without modification
  19. // from multiple threads at the same time.
  20. //
  21. // * `FT_Face' creation and destruction with the same `FT_Library'
  22. // object can only be done from one thread at a time.
  23. //
  24. // One can use a single `FT_Library' object across threads as long
  25. // as a mutex lock is used around `FT_New_Face' and `FT_Done_Face'.
  26. // Any calls to `FT_Load_Glyph' and similar API are safe and do not
  27. // need the lock to be held as long as the same `FT_Face' is not
  28. // used from multiple threads at the same time.
  29. // Harfbuzz is threadsafe.
  30. // Notes:
  31. // * All ft init and face creation should happen at startup.
  32. // * FT faces protected behind an async Mutex
  33. // * Glyph cache. Key is (glyph_id, font_size)
  34. // * Glyph texture cache: (glyph_id, font_size, color)
  35. pub struct RenderedAtlas {
  36. pub uv_rects: Vec<Rectangle>,
  37. pub texture_id: TextureId,
  38. }
  39. const ATLAS_GAP: usize = 2;
  40. pub async fn make_texture_atlas(
  41. render_api: RenderApiPtr,
  42. font_size: f32,
  43. glyphs: &Vec<Glyph>,
  44. ) -> Result<RenderedAtlas> {
  45. // First compute total size of the atlas
  46. let mut total_width = ATLAS_GAP;
  47. let mut total_height = ATLAS_GAP;
  48. // Glyph IDs already rendered so we don't do it twice
  49. let mut rendered = vec![];
  50. for (idx, glyph) in glyphs.iter().enumerate() {
  51. let sprite = &glyph.sprite;
  52. assert_eq!(sprite.bmp.len(), 4 * sprite.bmp_width * sprite.bmp_height);
  53. // Already done this one so skip
  54. if rendered.contains(&glyph.glyph_id) {
  55. continue
  56. }
  57. rendered.push(glyph.glyph_id);
  58. total_width += sprite.bmp_width + ATLAS_GAP;
  59. total_height = std::cmp::max(total_height, sprite.bmp_height);
  60. }
  61. total_width += ATLAS_GAP;
  62. total_height += 2 * ATLAS_GAP;
  63. // Allocate the big texture now
  64. let mut atlas_bmp = vec![0; 4 * total_width * total_height];
  65. // For debug lines we want a single white pixel.
  66. atlas_bmp[0] = 255;
  67. atlas_bmp[1] = 255;
  68. atlas_bmp[2] = 255;
  69. atlas_bmp[3] = 255;
  70. // Calculate dimensions of final product first
  71. let mut current_x = ATLAS_GAP;
  72. let mut rendered_glyphs: Vec<u32> = vec![];
  73. let mut uv_rects: Vec<Rectangle> = vec![];
  74. for (idx, glyph) in glyphs.iter().enumerate() {
  75. let sprite = &glyph.sprite;
  76. // Did we already rendered this glyph?
  77. // If so just copy the UV rect from before.
  78. let mut uv_rect = None;
  79. for (rendered_glyph_id, rendered_uv_rect) in rendered_glyphs.iter().zip(uv_rects.iter()) {
  80. if *rendered_glyph_id == glyph.glyph_id {
  81. uv_rect = Some(rendered_uv_rect.clone());
  82. }
  83. }
  84. let uv_rect = match uv_rect {
  85. Some(uv_rect) => uv_rect,
  86. // Allocating a new glyph sprite in the atlas
  87. None => {
  88. copy_image(sprite, &mut atlas_bmp, total_width, current_x);
  89. // Compute UV coords
  90. let uv_rect = Rectangle {
  91. x: (ATLAS_GAP + current_x) as f32 / total_width as f32,
  92. y: ATLAS_GAP as f32 / total_height as f32,
  93. w: sprite.bmp_width as f32 / total_width as f32,
  94. h: sprite.bmp_height as f32 / total_height as f32,
  95. };
  96. current_x += sprite.bmp_width + ATLAS_GAP;
  97. uv_rect
  98. }
  99. };
  100. rendered_glyphs.push(glyph.glyph_id);
  101. uv_rects.push(uv_rect);
  102. }
  103. // Finally allocate the texture
  104. let texture_id =
  105. render_api.new_texture(total_width as u16, total_height as u16, atlas_bmp).await?;
  106. Ok(RenderedAtlas { uv_rects, texture_id })
  107. }
  108. fn copy_image(sprite: &Sprite, atlas_bmp: &mut Vec<u8>, total_width: usize, current_x: usize) {
  109. for i in 0..sprite.bmp_height {
  110. for j in 0..sprite.bmp_width {
  111. let off_dest = 4 * ((i + ATLAS_GAP) * total_width + j + current_x + ATLAS_GAP);
  112. let off_src = 4 * (i * sprite.bmp_width + j);
  113. atlas_bmp[off_dest] = sprite.bmp[off_src];
  114. atlas_bmp[off_dest + 1] = sprite.bmp[off_src + 1];
  115. atlas_bmp[off_dest + 2] = sprite.bmp[off_src + 2];
  116. atlas_bmp[off_dest + 3] = sprite.bmp[off_src + 3];
  117. }
  118. }
  119. }
  120. pub struct GlyphPositionIter<'a> {
  121. font_size: f32,
  122. glyphs: &'a Vec<Glyph>,
  123. current_x: f32,
  124. current_y: f32,
  125. i: usize,
  126. }
  127. impl<'a> GlyphPositionIter<'a> {
  128. pub fn new(font_size: f32, glyphs: &'a Vec<Glyph>, baseline_y: f32) -> Self {
  129. Self { font_size, glyphs, current_x: 0., current_y: baseline_y, i: 0 }
  130. }
  131. }
  132. impl<'a> Iterator for GlyphPositionIter<'a> {
  133. type Item = Rectangle;
  134. fn next(&mut self) -> Option<Self::Item> {
  135. assert!(self.i <= self.glyphs.len());
  136. if self.i == self.glyphs.len() {
  137. return None;
  138. }
  139. let glyph = &self.glyphs[self.i];
  140. let sprite = &glyph.sprite;
  141. let rect = if sprite.has_fixed_sizes {
  142. // Downscale by height
  143. let w = (sprite.bmp_width as f32 * self.font_size) / sprite.bmp_height as f32;
  144. let h = self.font_size;
  145. let x = self.current_x;
  146. let y = self.current_y - h;
  147. self.current_x += w;
  148. Rectangle { x, y, w, h }
  149. } else {
  150. let (w, h) = (sprite.bmp_width as f32, sprite.bmp_height as f32);
  151. let off_x = glyph.x_offset as f32 / 64.;
  152. let off_y = glyph.y_offset as f32 / 64.;
  153. let x = self.current_x + off_x + sprite.bearing_x;
  154. let y = self.current_y - off_y - sprite.bearing_y;
  155. let x_advance = glyph.x_advance;
  156. let y_advance = glyph.y_advance;
  157. self.current_x += x_advance;
  158. self.current_y += y_advance;
  159. Rectangle { x, y, w, h }
  160. };
  161. self.i += 1;
  162. Some(rect)
  163. }
  164. }
  165. pub struct TextShaper {
  166. font_faces: Mutex<FtFaces>,
  167. cache: Mutex<TextShaperCache>,
  168. }
  169. impl TextShaper {
  170. pub fn new() -> Arc<Self> {
  171. let ftlib = ft::Library::init().unwrap();
  172. let mut faces = vec![];
  173. let font_data = include_bytes!("../ibm-plex-mono-light.otf") as &[u8];
  174. let ft_face = ftlib.new_memory_face2(font_data, 0).unwrap();
  175. faces.push(ft_face);
  176. let font_data = include_bytes!("../NotoColorEmoji.ttf") as &[u8];
  177. let ft_face = ftlib.new_memory_face2(font_data, 0).unwrap();
  178. faces.push(ft_face);
  179. Arc::new(Self { font_faces: Mutex::new(FtFaces(faces)), cache: Mutex::new(HashMap::new()) })
  180. }
  181. pub fn split_into_substrs(
  182. font_faces: &Vec<FreetypeFace>,
  183. text: String,
  184. ) -> Vec<(usize, String)> {
  185. let mut current_idx = 0;
  186. let mut current_str = String::new();
  187. let mut substrs = vec![];
  188. 'next_char: for chr in text.chars() {
  189. let idx = 'get_idx: {
  190. for i in 0..font_faces.len() {
  191. let ft_face = &font_faces[i];
  192. if ft_face.get_char_index(chr as usize).is_some() {
  193. break 'get_idx i
  194. }
  195. }
  196. drop(font_faces);
  197. warn!(target: "text", "no font fallback for char: '{}'", chr);
  198. // Skip this char
  199. continue 'next_char
  200. };
  201. if current_idx != idx {
  202. if !current_str.is_empty() {
  203. // Push
  204. substrs.push((current_idx, current_str.clone()));
  205. }
  206. current_str.clear();
  207. current_idx = idx;
  208. }
  209. current_str.push(chr);
  210. }
  211. if !current_str.is_empty() {
  212. // Push
  213. substrs.push((current_idx, current_str));
  214. }
  215. substrs
  216. }
  217. pub async fn shape(&self, text: String, font_size: f32) -> Vec<Glyph> {
  218. // Lock font faces
  219. // Freetype faces are not threadsafe
  220. let faces = self.font_faces.lock().await;
  221. let mut cache = self.cache.lock().await;
  222. let substrs = Self::split_into_substrs(&faces.0, text.clone());
  223. let mut glyphs: Vec<Glyph> = vec![];
  224. let mut current_x = 0.;
  225. let mut current_y = 0.;
  226. for (face_idx, text) in substrs {
  227. //debug!("substr {}", text);
  228. let face = &faces.0[face_idx];
  229. if face.has_fixed_sizes() {
  230. // emojis required a fixed size
  231. //face.set_char_size(109 * 64, 0, 72, 72).unwrap();
  232. face.select_size(0).unwrap();
  233. } else {
  234. face.set_char_size(font_size as isize * 64, 0, 96, 96).unwrap();
  235. }
  236. let hb_font = harfbuzz_rs::Font::from_freetype_face(face.clone());
  237. let buffer = harfbuzz_rs::UnicodeBuffer::new()
  238. .set_cluster_level(harfbuzz_rs::ClusterLevel::MonotoneCharacters)
  239. .add_str(&text);
  240. let output = harfbuzz_rs::shape(&hb_font, buffer, &[]);
  241. let positions = output.get_glyph_positions();
  242. let infos = output.get_glyph_infos();
  243. let mut prev_cluster = 0;
  244. for (i, (position, info)) in positions.iter().zip(infos).enumerate() {
  245. let glyph_id = info.codepoint;
  246. // Index within this substr
  247. let curr_cluster = info.cluster as usize;
  248. // Skip first time
  249. if i != 0 {
  250. let substr = text[prev_cluster..curr_cluster].to_string();
  251. glyphs.last_mut().unwrap().substr = substr;
  252. }
  253. prev_cluster = curr_cluster;
  254. let x_offset = position.x_offset as f32 / 64.;
  255. let y_offset = position.y_offset as f32 / 64.;
  256. let x_advance = position.x_advance as f32 / 64.;
  257. let y_advance = position.y_advance as f32 / 64.;
  258. // Check cache
  259. // If it exists in the cache then skip
  260. // Relevant info:
  261. // * glyph_id
  262. // * font_size (for non-fixed size faces)
  263. // * face_idx
  264. let cache_key = CacheKey {
  265. glyph_id,
  266. font_size: if face.has_fixed_sizes() {
  267. FontSize::Fixed
  268. } else {
  269. FontSize::from(font_size)
  270. },
  271. face_idx,
  272. };
  273. //debug!(target: "text", "cache_key: {:?}", cache_key);
  274. if let Some(sprite) = cache.get(&cache_key) {
  275. let Some(sprite) = sprite.upgrade() else { break };
  276. let glyph = Glyph {
  277. glyph_id,
  278. substr: String::new(),
  279. sprite,
  280. x_offset,
  281. y_offset,
  282. x_advance,
  283. y_advance,
  284. };
  285. glyphs.push(glyph);
  286. continue
  287. }
  288. let mut flags = ft::face::LoadFlag::DEFAULT;
  289. if face.has_color() {
  290. flags |= ft::face::LoadFlag::COLOR;
  291. }
  292. // FIXME: glyph 884 hangs on android
  293. // For now just avoid using emojis on android
  294. //debug!("load_glyph {}", gid);
  295. face.load_glyph(glyph_id, flags).unwrap();
  296. //debug!("load_glyph {} [done]", gid);
  297. // https://gist.github.com/jokertarot/7583938?permalink_comment_id=3327566#gistcomment-3327566
  298. let glyph = face.glyph();
  299. glyph.render_glyph(ft::RenderMode::Normal).unwrap();
  300. let bmp = glyph.bitmap();
  301. let buffer = bmp.buffer();
  302. let bmp_width = bmp.width() as usize;
  303. let bmp_height = bmp.rows() as usize;
  304. let bearing_x = glyph.bitmap_left() as f32;
  305. let bearing_y = glyph.bitmap_top() as f32;
  306. let has_fixed_sizes = face.has_fixed_sizes();
  307. let pixel_mode = bmp.pixel_mode().unwrap();
  308. let bmp = match pixel_mode {
  309. ft::bitmap::PixelMode::Bgra => {
  310. let mut tdata = vec![];
  311. tdata.resize(4 * bmp_width * bmp_height, 0);
  312. // Convert from BGRA to RGBA
  313. for i in 0..bmp_width * bmp_height {
  314. let idx = i * 4;
  315. let b = buffer[idx];
  316. let g = buffer[idx + 1];
  317. let r = buffer[idx + 2];
  318. let a = buffer[idx + 3];
  319. tdata[idx] = r;
  320. tdata[idx + 1] = g;
  321. tdata[idx + 2] = b;
  322. tdata[idx + 3] = a;
  323. }
  324. tdata
  325. }
  326. ft::bitmap::PixelMode::Gray => {
  327. // Convert from greyscale to RGBA8
  328. let tdata: Vec<_> = buffer
  329. .iter()
  330. .flat_map(|coverage| vec![255, 255, 255, *coverage])
  331. .collect();
  332. tdata
  333. }
  334. _ => panic!("unsupport pixel mode: {:?}", pixel_mode),
  335. };
  336. let sprite = Arc::new(Sprite {
  337. bmp,
  338. bmp_width,
  339. bmp_height,
  340. bearing_x,
  341. bearing_y,
  342. has_fixed_sizes,
  343. });
  344. cache.insert(cache_key, Arc::downgrade(&sprite));
  345. let glyph = Glyph {
  346. glyph_id,
  347. substr: String::new(),
  348. sprite,
  349. x_offset,
  350. y_offset,
  351. x_advance,
  352. y_advance,
  353. };
  354. glyphs.push(glyph);
  355. }
  356. let substr = text[prev_cluster..].to_string();
  357. glyphs.last_mut().unwrap().substr = substr;
  358. }
  359. glyphs
  360. }
  361. }
  362. #[derive(Eq, Hash, PartialEq, Debug)]
  363. enum FontSize {
  364. Fixed,
  365. Size(u32),
  366. }
  367. impl FontSize {
  368. /// You can't use f32 in Hash and Eq impls
  369. fn from(size: f32) -> Self {
  370. Self::Size((size * 1000.).round() as u32)
  371. }
  372. }
  373. #[derive(Eq, Hash, PartialEq, Debug)]
  374. struct CacheKey {
  375. glyph_id: u32,
  376. font_size: FontSize,
  377. face_idx: usize,
  378. }
  379. pub type SpritePtr = Arc<Sprite>;
  380. pub struct Sprite {
  381. bmp: Vec<u8>,
  382. pub bmp_width: usize,
  383. pub bmp_height: usize,
  384. pub bearing_x: f32,
  385. pub bearing_y: f32,
  386. pub has_fixed_sizes: bool,
  387. }
  388. pub struct Glyph {
  389. pub glyph_id: u32,
  390. // Substring this glyph corresponds to
  391. pub substr: String,
  392. pub sprite: SpritePtr,
  393. // Normally these are i32, we provide the conversions
  394. pub x_offset: f32,
  395. pub y_offset: f32,
  396. pub x_advance: f32,
  397. pub y_advance: f32,
  398. }
  399. type FreetypeFace = ft::Face<&'static [u8]>;
  400. struct FtFaces(Vec<FreetypeFace>);
  401. unsafe impl Send for FtFaces {}
  402. unsafe impl Sync for FtFaces {}
  403. pub type TextShaperPtr = Arc<TextShaper>;
  404. type TextShaperCache = HashMap<CacheKey, Weak<Sprite>>;