mod.rs 17 KB

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