gpui/text_system/
glyph.rs1use 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#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
10#[repr(C)]
11pub struct GlyphId(pub u32);
12
13#[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#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
31pub enum GlyphRenderMode {
32 Grayscale,
34 Subpixel,
36 Color,
38}
39
40impl GlyphRenderMode {
41 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#[derive(Clone, Copy, Debug)]
53pub struct RasterStyleRequest {
54 pub font_id: FontId,
56 pub glyph_id: GlyphId,
58 pub scene_color: crate::Rgba,
60 pub requested_mode: GlyphRenderMode,
62 pub foreground_dependency: ForegroundDependency,
64}
65
66impl RasterStyleRequest {
67 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#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
80pub enum RasterizedGlyphFormat {
81 AlphaMask,
83 BgraSubpixelMask,
85 BgraColor,
87}
88
89#[derive(Clone, Debug)]
91pub struct RasterizedGlyph {
92 pub bounds: Bounds<DevicePixels>,
94 pub size: Size<DevicePixels>,
96 pub format: RasterizedGlyphFormat,
98 pub pixels: Vec<u8>,
100}
101
102impl RasterizedGlyph {
103 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 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#[derive(Clone, Debug, Deref)]
168pub struct ValidatedRasterizedGlyph(RasterizedGlyph);
169
170impl ValidatedRasterizedGlyph {
171 pub fn new(glyph: RasterizedGlyph) -> crate::Result<Self> {
173 glyph.validate()?;
174
175 Ok(Self(glyph))
176 }
177}
178
179#[derive(Clone, Copy, Debug, Eq, PartialEq)]
181pub struct GlyphAtlasEntry {
182 pub tile: Option<crate::AtlasTile>,
184 pub bounds: Bounds<DevicePixels>,
186 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}