Skip to main content

gpui/text_system/
glyph.rs

1use crate::{
2    Bounds, DevicePixels, FontId, ForegroundDependency, Pixels, Point, PreparedRasterStyle,
3    RasterColorExt, Rgba8, Size,
4};
5use derive_more::Deref;
6use std::hash::{Hash, Hasher};
7
8/// An identifier for a specific glyph, as returned by [`WindowTextSystem::layout_line`](crate::WindowTextSystem::layout_line).
9#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
10#[repr(C)]
11pub struct GlyphId(pub u32);
12
13/// Parameters for rendering a glyph, used as cache keys for glyph atlas entries.
14///
15/// This struct identifies a specific glyph rendering configuration including
16/// font, size, subpixel positioning, and scale factor. It's used to look up
17/// cached pixels, placement metadata, and sprite atlas entries.
18#[derive(Clone, Debug, PartialEq)]
19#[expect(missing_docs)]
20pub struct RenderGlyphParams {
21    pub font_id: FontId,
22    pub glyph_id: GlyphId,
23    pub font_size: Pixels,
24    pub subpixel_variant: Point<u8>,
25    pub scale_factor: f32,
26    pub raster_style: PreparedRasterStyle,
27}
28
29/// The kind of glyph image requested from a rasterizer.
30#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
31pub enum GlyphRenderMode {
32    /// A color-independent, one-channel coverage mask.
33    Grayscale,
34    /// A color-independent, three-channel subpixel coverage mask.
35    Subpixel,
36    /// A color glyph whose pixels include their final RGB values.
37    Color,
38}
39
40impl GlyphRenderMode {
41    /// Returns the byte format required for this render mode.
42    pub const fn rasterized_format(self) -> RasterizedGlyphFormat {
43        match self {
44            Self::Grayscale => RasterizedGlyphFormat::AlphaMask,
45            Self::Subpixel => RasterizedGlyphFormat::BgraSubpixelMask,
46            Self::Color => RasterizedGlyphFormat::BgraColor,
47        }
48    }
49}
50
51/// A scene request before a platform rasterizer normalizes its cache-relevant settings.
52#[derive(Clone, Copy, Debug)]
53pub struct RasterStyleRequest {
54    /// The canonical font instance containing the glyph.
55    pub font_id: FontId,
56    /// The glyph whose artwork will be rasterized.
57    pub glyph_id: GlyphId,
58    /// The exact application color. Rasterizers may only retain a normalized derivative of it.
59    pub scene_color: crate::Rgba,
60    /// The requested kind of glyph image.
61    pub requested_mode: GlyphRenderMode,
62    /// Which foreground channels can alter the selected artwork.
63    pub foreground_dependency: ForegroundDependency,
64}
65
66impl RasterStyleRequest {
67    /// Quantizes the foreground after removing channels unused by the artwork.
68    pub fn normalized_color(self) -> Rgba8 {
69        let color = self.scene_color.quantize_raster_color();
70        if self.foreground_dependency == ForegroundDependency::AlphaOnly {
71            return Rgba8::new(0, 0, 0, color.alpha);
72        }
73
74        color
75    }
76}
77
78/// The byte layout supplied by a glyph rasterizer.
79#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
80pub enum RasterizedGlyphFormat {
81    /// One byte of coverage per pixel.
82    AlphaMask,
83    /// Four bytes per pixel in blue, green, red, unused order.
84    BgraSubpixelMask,
85    /// Four bytes per pixel in blue, green, red, straight-alpha order.
86    BgraColor,
87}
88
89/// A glyph raster produced by a platform rasterizer.
90#[derive(Clone, Debug)]
91pub struct RasterizedGlyph {
92    /// Placement relative to the glyph's baseline origin.
93    pub bounds: Bounds<DevicePixels>,
94    /// Pixel dimensions of the supplied buffer.
95    pub size: Size<DevicePixels>,
96    /// Byte layout of `pixels`.
97    pub format: RasterizedGlyphFormat,
98    /// Pixel bytes. Color pixels use straight alpha.
99    pub pixels: Vec<u8>,
100}
101
102impl RasterizedGlyph {
103    /// Returns a successful empty glyph in the requested format.
104    pub fn empty(format: RasterizedGlyphFormat) -> Self {
105        Self {
106            bounds: Bounds::default(),
107            size: Size::default(),
108            format,
109            pixels: Vec::new(),
110        }
111    }
112
113    /// Checks the buffer dimensions and byte count promised by `format`.
114    pub fn validate(&self) -> crate::Result<()> {
115        anyhow::ensure!(
116            self.bounds.size == self.size,
117            "glyph raster bounds size {:?} does not match buffer size {:?}",
118            self.bounds.size,
119            self.size
120        );
121
122        let width: usize = self
123            .size
124            .width
125            .0
126            .try_into()
127            .map_err(|_| anyhow::anyhow!("glyph raster width is negative"))?;
128
129        let height: usize = self
130            .size
131            .height
132            .0
133            .try_into()
134            .map_err(|_| anyhow::anyhow!("glyph raster height is negative"))?;
135
136        if width == 0 || height == 0 {
137            anyhow::ensure!(
138                width == 0 && height == 0 && self.pixels.is_empty(),
139                "empty glyph raster must have zero size and no pixels"
140            );
141            return Ok(());
142        }
143
144        let bytes_per_pixel = match self.format {
145            RasterizedGlyphFormat::AlphaMask => 1,
146            RasterizedGlyphFormat::BgraSubpixelMask | RasterizedGlyphFormat::BgraColor => 4,
147        };
148        let expected_len = width
149            .checked_mul(height)
150            .and_then(|pixels| pixels.checked_mul(bytes_per_pixel))
151            .ok_or_else(|| anyhow::anyhow!("glyph raster byte count overflow"))?;
152
153        anyhow::ensure!(
154            self.pixels.len() == expected_len,
155            "glyph raster format {:?} requires {expected_len} bytes for {}x{}, got {}",
156            self.format,
157            width,
158            height,
159            self.pixels.len()
160        );
161
162        Ok(())
163    }
164}
165
166/// A glyph raster whose placement and pixel buffer have passed validation.
167#[derive(Clone, Debug, Deref)]
168pub struct ValidatedRasterizedGlyph(RasterizedGlyph);
169
170impl ValidatedRasterizedGlyph {
171    /// Validates a glyph raster before it can be used by an atlas builder.
172    pub fn new(glyph: RasterizedGlyph) -> crate::Result<Self> {
173        glyph.validate()?;
174
175        Ok(Self(glyph))
176    }
177}
178
179/// A cached glyph's GPU tile and the placement metadata for its uploaded pixels.
180#[derive(Clone, Copy, Debug, Eq, PartialEq)]
181pub struct GlyphAtlasEntry {
182    /// The GPU tile, or `None` for a successfully empty glyph.
183    pub tile: Option<crate::AtlasTile>,
184    /// Placement relative to the glyph's baseline origin.
185    pub bounds: Bounds<DevicePixels>,
186    /// Byte layout of the cached pixels.
187    pub format: RasterizedGlyphFormat,
188}
189
190impl Eq for RenderGlyphParams {}
191
192impl Hash for RenderGlyphParams {
193    fn hash<H: Hasher>(&self, state: &mut H) {
194        self.font_id.0.hash(state);
195        self.glyph_id.0.hash(state);
196        self.font_size.0.to_bits().hash(state);
197        self.subpixel_variant.hash(state);
198        self.scale_factor.to_bits().hash(state);
199        self.raster_style.hash(state);
200    }
201}