text2.rs 18 KB

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