| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free sofreetypeware: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Sofreetypeware Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use freetype::face::LoadFlag as FtLoadFlag;
- use std::sync::Arc;
- pub type FreetypeFace = freetype::Face<&'static [u8]>;
- pub type SpritePtr = Arc<Sprite>;
- pub struct Sprite {
- pub bmp: Vec<u8>,
- pub bmp_width: usize,
- pub bmp_height: usize,
- pub bearing_x: f32,
- pub bearing_y: f32,
- pub has_fixed_sizes: bool,
- pub has_color: bool,
- }
- fn load_ft_glyph<'a>(
- face: &'a FreetypeFace,
- glyph_id: u32,
- flags: FtLoadFlag,
- ) -> Option<&'a freetype::GlyphSlot> {
- //debug!("load_glyph {} flags={flags:?}", glyph_id);
- if let Err(err) = face.load_glyph(glyph_id, flags) {
- error!(target: "text", "error loading glyph {glyph_id}: {err}");
- return None
- }
- //debug!("load_glyph {} [done]", glyph_id);
- // https://gist.github.com/jokertarot/7583938?permalink_comment_id=3327566#gistcomment-3327566
- let glyph = face.glyph();
- glyph.render_glyph(freetype::RenderMode::Normal).ok()?;
- Some(glyph)
- }
- pub fn render_glyph(face: &FreetypeFace, glyph_id: u32) -> Option<Sprite> {
- // If color is available then attempt to load it.
- // Otherwise fallback to black and white.
- let glyph = if face.has_color() {
- match load_ft_glyph(face, glyph_id, FtLoadFlag::DEFAULT | FtLoadFlag::COLOR) {
- Some(glyph) => glyph,
- None => load_ft_glyph(face, glyph_id, FtLoadFlag::DEFAULT)?,
- }
- } else {
- load_ft_glyph(face, glyph_id, FtLoadFlag::DEFAULT)?
- };
- let bmp = glyph.bitmap();
- let buffer = bmp.buffer();
- let bmp_width = bmp.width() as usize;
- let bmp_height = bmp.rows() as usize;
- let bearing_x = glyph.bitmap_left() as f32;
- let bearing_y = glyph.bitmap_top() as f32;
- let has_fixed_sizes = face.has_fixed_sizes();
- let pixel_mode = bmp.pixel_mode().unwrap();
- let bmp = match pixel_mode {
- freetype::bitmap::PixelMode::Bgra => {
- let mut tdata = vec![];
- tdata.resize(4 * bmp_width * bmp_height, 0);
- // Convert from BGRA to RGBA
- for i in 0..bmp_width * bmp_height {
- let idx = i * 4;
- let b = buffer[idx];
- let g = buffer[idx + 1];
- let r = buffer[idx + 2];
- let a = buffer[idx + 3];
- tdata[idx] = r;
- tdata[idx + 1] = g;
- tdata[idx + 2] = b;
- tdata[idx + 3] = a;
- }
- tdata
- }
- freetype::bitmap::PixelMode::Gray => {
- // Convert from greyscale to RGBA8
- let tdata: Vec<_> =
- buffer.iter().flat_map(|coverage| vec![255, 255, 255, *coverage]).collect();
- tdata
- }
- freetype::bitmap::PixelMode::Mono => {
- // Convert from mono to RGBA8
- let tdata: Vec<_> =
- buffer.iter().flat_map(|coverage| vec![255, 255, 255, *coverage]).collect();
- tdata
- }
- _ => panic!("unsupport pixel mode: {:?}", pixel_mode),
- };
- Some(Sprite {
- bmp,
- bmp_width,
- bmp_height,
- bearing_x,
- bearing_y,
- has_fixed_sizes,
- has_color: face.has_color(),
- })
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- fn render_simple() {
- let ftlib = freetype::Library::init().unwrap();
- let font_data = include_bytes!("../../ibm-plex-mono-regular.otf") as &[u8];
- let face = ftlib.new_memory_face2(font_data, 0).unwrap();
- // glyph 11 in IBM plex mono regular is 'h'
- let glyph = render_glyph(&face, 11).unwrap();
- }
- #[test]
- fn render_custom_glyph() {
- let ftlib = freetype::Library::init().unwrap();
- let font_data = include_bytes!("../../darkirc-emoji-svg.ttf") as &[u8];
- let face = ftlib.new_memory_face2(font_data, 0).unwrap();
- let glyph = render_glyph(&face, 4).unwrap();
- }
- }
|