Skip to main content

i_slint_renderer_software/fonts/
pixelfont.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use crate::PhysicalLength;
5use crate::fixed::Fixed;
6use i_slint_core::graphics::{BitmapFont, BitmapGlyphs};
7use i_slint_core::textlayout::{FontMetrics, Glyph, TextShaper};
8
9use super::{GlyphRenderer, RenderableGlyph};
10
11// A font that is resolved to a specific pixel size.
12pub struct PixelFont {
13    pub bitmap_font: &'static BitmapFont,
14    pub glyphs: &'static BitmapGlyphs,
15    pub pixel_size: PhysicalLength,
16}
17
18impl PixelFont {
19    pub fn glyph_index_to_glyph_id(index: usize) -> core::num::NonZeroU16 {
20        core::num::NonZeroU16::new(index as u16 + 1).unwrap()
21    }
22    pub fn glyph_id_to_glyph_index(id: core::num::NonZeroU16) -> usize {
23        id.get() as usize - 1
24    }
25}
26
27impl GlyphRenderer for PixelFont {
28    fn render_glyph(&self, glyph_id: core::num::NonZeroU16) -> Option<RenderableGlyph> {
29        let glyph_index = Self::glyph_id_to_glyph_index(glyph_id);
30        let bitmap_glyph = &self.glyphs.glyph_data[glyph_index];
31        if bitmap_glyph.data.is_empty() {
32            // For example, ' ' has no glyph data
33            return None;
34        }
35        // t represent the target coordinate system, and s the source glyph coordinate system.
36        // We want to align the glyph such that Δ(hₜ+yₜ)+offset = hₛ+yₛ
37        // where hₜ is the integer height of the glyph in the target coordinate system
38        // and offset is smaller than Δ
39        // We also want that Δ(hₜ-1)+offset ≤ hₛ-1
40        // Similar for x but that's easier since x is not subtracted from the width
41        let delta = Fixed::<i32, 8>::from_fixed(self.scale_delta());
42        let src_x = Fixed::<i32, 8>::from_fixed(Fixed::<_, 6>(bitmap_glyph.x));
43        let src_y = Fixed::<i32, 8>::from_fixed(Fixed::<_, 6>(bitmap_glyph.y));
44        let h_plus_y = Fixed::<i32, 8>::from_integer(bitmap_glyph.height as i32) + src_y;
45        let h_plus_y = Fixed::<i32, 8>::from_fraction(h_plus_y.0, delta.0);
46        let off_y = Fixed::<i32, 8>(h_plus_y.0 & 0xff);
47        let height = (Fixed::from_integer(bitmap_glyph.height as i32 - 1) - off_y) / delta + 1;
48        let x = Fixed::from_fraction(src_x.0, delta.0);
49        let off_x = Fixed::<i32, 8>(-x.0 & 0xff);
50        let width = (Fixed::from_integer(bitmap_glyph.width as i32 - 1) - off_x) / delta + 1;
51        Some(RenderableGlyph {
52            x,
53            y: h_plus_y - Fixed::from_integer(height),
54            width: PhysicalLength::new(width as i16),
55            height: PhysicalLength::new(height as i16),
56            alpha_map: bitmap_glyph.data.as_slice().into(),
57            pixel_stride: bitmap_glyph.width as u16,
58            sdf: self.bitmap_font.sdf,
59        })
60    }
61    fn scale_delta(&self) -> Fixed<u16, 8> {
62        Fixed::try_from_fixed(Fixed::<u32, 8>::from_fraction(
63            self.glyphs.pixel_size as u32,
64            self.pixel_size.get() as u32,
65        ))
66        .unwrap()
67    }
68}
69
70impl TextShaper for PixelFont {
71    type LengthPrimitive = i16;
72    type Length = PhysicalLength;
73    fn shape_text<GlyphStorage: core::iter::Extend<Glyph<PhysicalLength>>>(
74        &self,
75        text: &str,
76        glyphs: &mut GlyphStorage,
77    ) {
78        let glyphs_iter = text.char_indices().map(|(byte_offset, char)| {
79            let glyph_index = self
80                .bitmap_font
81                .character_map
82                .binary_search_by_key(&char, |char_map_entry| char_map_entry.code_point)
83                .ok()
84                .map(|char_map_index| {
85                    self.bitmap_font.character_map[char_map_index].glyph_index as usize
86                });
87            let x_advance = glyph_index.map_or_else(
88                || self.pixel_size,
89                |glyph_index| {
90                    ((self.pixel_size.cast()
91                        * self.glyphs.glyph_data[glyph_index].x_advance as i32
92                        / self.glyphs.pixel_size as i32
93                        + euclid::Length::new(32))
94                        / 64)
95                        .cast()
96                },
97            );
98            Glyph {
99                glyph_id: glyph_index.map(Self::glyph_index_to_glyph_id),
100                advance: x_advance,
101                text_byte_offset: byte_offset,
102                ..Default::default()
103            }
104        });
105        glyphs.extend(glyphs_iter);
106    }
107
108    fn glyph_for_char(&self, ch: char) -> Option<Glyph<PhysicalLength>> {
109        self.bitmap_font
110            .character_map
111            .binary_search_by_key(&ch, |char_map_entry| char_map_entry.code_point)
112            .ok()
113            .map(|char_map_index| {
114                let glyph_index =
115                    self.bitmap_font.character_map[char_map_index].glyph_index as usize;
116                let bitmap_glyph = &self.glyphs.glyph_data[glyph_index];
117                let x_advance = ((self.pixel_size.cast() * bitmap_glyph.x_advance as i32
118                    / self.glyphs.pixel_size as i32
119                    + euclid::Length::new(32))
120                    / 64)
121                    .cast();
122                Glyph {
123                    glyph_id: Some(Self::glyph_index_to_glyph_id(glyph_index)),
124                    advance: x_advance,
125                    text_byte_offset: 0,
126                    ..Default::default()
127                }
128            })
129    }
130
131    fn max_lines(&self, max_height: PhysicalLength) -> usize {
132        (max_height / self.height()).get() as _
133    }
134}
135
136impl FontMetrics<PhysicalLength> for PixelFont {
137    fn ascent(&self) -> PhysicalLength {
138        (self.pixel_size.cast() * self.bitmap_font.ascent / self.bitmap_font.units_per_em).cast()
139    }
140
141    fn descent(&self) -> PhysicalLength {
142        (self.pixel_size.cast() * self.bitmap_font.descent / self.bitmap_font.units_per_em).cast()
143    }
144
145    fn height(&self) -> PhysicalLength {
146        // The descent is negative (relative to the baseline)
147        (self.pixel_size.cast() * (self.bitmap_font.ascent - self.bitmap_font.descent)
148            / self.bitmap_font.units_per_em)
149            .cast()
150    }
151
152    fn x_height(&self) -> PhysicalLength {
153        (self.pixel_size.cast() * self.bitmap_font.x_height / self.bitmap_font.units_per_em).cast()
154    }
155
156    fn cap_height(&self) -> PhysicalLength {
157        (self.pixel_size.cast() * self.bitmap_font.cap_height / self.bitmap_font.units_per_em)
158            .cast()
159    }
160}