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