arcdps_imgui\fonts/
atlas.rs

1use bitflags::bitflags;
2use std::cell;
3use std::f32;
4use std::ops::{Deref, DerefMut};
5use std::os::raw::{c_int, c_uchar, c_void};
6use std::ptr;
7use std::slice;
8
9use crate::fonts::font::Font;
10use crate::fonts::glyph_ranges::FontGlyphRanges;
11use crate::internal::{ImVector, RawCast};
12use crate::sys;
13use crate::TextureId;
14
15bitflags! {
16    /// Font atlas configuration flags
17    #[repr(transparent)]
18    pub struct FontAtlasFlags: u32 {
19        /// Don't round the height to next power of two
20        const NO_POWER_OF_TWO_HEIGHT = sys::ImFontAtlasFlags_NoPowerOfTwoHeight;
21        /// Don't build software mouse cursors into the atlas
22        const NO_MOUSE_CURSORS = sys::ImFontAtlasFlags_NoMouseCursors;
23        /// Don't build thick line textures into the atlas
24        const NO_BAKED_LINES = sys::ImFontAtlasFlags_NoBakedLines;
25    }
26}
27
28/// A font identifier
29#[derive(Copy, Clone, Debug, Eq, PartialEq)]
30pub struct FontId(pub(crate) *const Font);
31
32/// A font atlas that builds a single texture
33#[repr(C)]
34pub struct FontAtlas {
35    locked: bool,
36    /// Configuration flags
37    pub flags: FontAtlasFlags,
38    /// Texture identifier
39    pub tex_id: TextureId,
40    /// Texture width desired by user before building the atlas.
41    ///
42    /// Must be a power-of-two. If you have many glyphs and your graphics API has texture size
43    /// restrictions, you may want to increase texture width to decrease the height.
44    pub tex_desired_width: i32,
45    /// Padding between glyphs within texture in pixels.
46    ///
47    /// Defaults to 1. If your rendering method doesn't rely on bilinear filtering, you may set
48    /// this to 0.
49    pub tex_glyph_padding: i32,
50
51    tex_pixels_alpha8: *mut u8,
52    tex_pixels_rgba32: *mut u32,
53    tex_width: i32,
54    tex_height: i32,
55    tex_uv_scale: [f32; 2],
56    tex_uv_white_pixel: [f32; 2],
57    fonts: ImVector<*mut Font>,
58    custom_rects: sys::ImVector_ImFontAtlasCustomRect,
59    config_data: sys::ImVector_ImFontConfig,
60    tex_uv_lines: [[f32; 4]; 64],
61    pack_id_mouse_cursors: i32,
62    pack_id_lines: i32,
63}
64
65unsafe impl RawCast<sys::ImFontAtlas> for FontAtlas {}
66
67impl FontAtlas {
68    #[doc(alias = "AddFontDefault", alias = "AddFont")]
69    pub fn add_font(&mut self, font_sources: &[FontSource<'_>]) -> FontId {
70        let (head, tail) = font_sources.split_first().unwrap();
71        let font_id = self.add_font_internal(head, false);
72        for font in tail {
73            self.add_font_internal(font, true);
74        }
75        font_id
76    }
77    fn add_font_internal(&mut self, font_source: &FontSource<'_>, merge_mode: bool) -> FontId {
78        let mut raw_config = sys_font_config_default();
79        raw_config.MergeMode = merge_mode;
80        let raw_font = match font_source {
81            FontSource::DefaultFontData { config } => unsafe {
82                if let Some(config) = config {
83                    config.apply_to_raw_config(&mut raw_config, self.raw_mut());
84                }
85                sys::ImFontAtlas_AddFontDefault(self.raw_mut(), &raw_config)
86            },
87            FontSource::TtfData {
88                data,
89                size_pixels,
90                config,
91            } => {
92                if let Some(config) = config {
93                    unsafe {
94                        config.apply_to_raw_config(&mut raw_config, self.raw_mut());
95                    }
96                }
97                // We can't guarantee `data` is alive when the font atlas is built, so
98                // make a copy and move ownership of the data to the atlas
99                let data_copy = unsafe {
100                    let ptr = sys::igMemAlloc(data.len()) as *mut u8;
101                    assert!(!ptr.is_null());
102                    slice::from_raw_parts_mut(ptr, data.len())
103                };
104                data_copy.copy_from_slice(data);
105                raw_config.FontData = data_copy.as_mut_ptr() as *mut c_void;
106                raw_config.FontDataSize = data_copy.len() as i32;
107                raw_config.FontDataOwnedByAtlas = true;
108                raw_config.SizePixels = *size_pixels;
109                unsafe { sys::ImFontAtlas_AddFont(self.raw_mut(), &raw_config) }
110            }
111        };
112        FontId(raw_font as *const _)
113    }
114    pub fn fonts(&self) -> Vec<FontId> {
115        let mut result = Vec::new();
116        unsafe {
117            for &font in self.fonts.as_slice() {
118                result.push((*font).id());
119            }
120        }
121        result
122    }
123    pub fn get_font(&self, id: FontId) -> Option<&Font> {
124        unsafe {
125            for &font in self.fonts.as_slice() {
126                if id == FontId(font) {
127                    return Some(&*(font as *const Font));
128                }
129            }
130        }
131        None
132    }
133    /// Returns true if the font atlas has been built
134    #[doc(alias = "IsBuilt")]
135    pub fn is_built(&self) -> bool {
136        unsafe { sys::ImFontAtlas_IsBuilt(self.raw() as *const sys::ImFontAtlas as *mut _) }
137    }
138    /// Builds a 1 byte per-pixel font atlas texture
139    #[doc(alias = "GetTextDataAsAlpha8")]
140    pub fn build_alpha8_texture(&mut self) -> FontAtlasTexture<'_> {
141        let mut pixels: *mut c_uchar = ptr::null_mut();
142        let mut width: c_int = 0;
143        let mut height: c_int = 0;
144        let mut bytes_per_pixel: c_int = 0;
145        unsafe {
146            sys::ImFontAtlas_GetTexDataAsAlpha8(
147                self.raw_mut(),
148                &mut pixels,
149                &mut width,
150                &mut height,
151                &mut bytes_per_pixel,
152            );
153            assert!(width >= 0, "font texture width must be positive");
154            assert!(height >= 0, "font texture height must be positive");
155            assert!(
156                bytes_per_pixel >= 0,
157                "font texture bytes per pixel must be positive"
158            );
159            let height = height as usize;
160            // Check multiplication to avoid constructing an invalid slice in case of overflow
161            let pitch = width
162                .checked_mul(bytes_per_pixel)
163                .expect("Overflow in font texture pitch calculation")
164                as usize;
165            FontAtlasTexture {
166                width: width as u32,
167                height: height as u32,
168                data: slice::from_raw_parts(pixels, pitch * height),
169            }
170        }
171    }
172    /// Builds a 4 byte per-pixel font atlas texture
173    #[doc(alias = "GetTextDataAsRGBA32")]
174    pub fn build_rgba32_texture(&mut self) -> FontAtlasTexture<'_> {
175        let mut pixels: *mut c_uchar = ptr::null_mut();
176        let mut width: c_int = 0;
177        let mut height: c_int = 0;
178        let mut bytes_per_pixel: c_int = 0;
179        unsafe {
180            sys::ImFontAtlas_GetTexDataAsRGBA32(
181                self.raw_mut(),
182                &mut pixels,
183                &mut width,
184                &mut height,
185                &mut bytes_per_pixel,
186            );
187            assert!(width >= 0, "font texture width must be positive");
188            assert!(height >= 0, "font texture height must be positive");
189            assert!(
190                bytes_per_pixel >= 0,
191                "font texture bytes per pixel must be positive"
192            );
193            let height = height as usize;
194            // Check multiplication to avoid constructing an invalid slice in case of overflow
195            let pitch = width
196                .checked_mul(bytes_per_pixel)
197                .expect("Overflow in font texture pitch calculation")
198                as usize;
199            FontAtlasTexture {
200                width: width as u32,
201                height: height as u32,
202                data: slice::from_raw_parts(pixels, pitch * height),
203            }
204        }
205    }
206    /// Clears the font atlas completely (both input and output data)
207    #[doc(alias = "Clear")]
208    pub fn clear(&mut self) {
209        unsafe {
210            sys::ImFontAtlas_Clear(self.raw_mut());
211        }
212    }
213    /// Clears output font data (glyph storage, UV coordinates)
214    #[doc(alias = "ClearFonts")]
215    pub fn clear_fonts(&mut self) {
216        unsafe {
217            sys::ImFontAtlas_ClearFonts(self.raw_mut());
218        }
219    }
220    /// Clears output texture data.
221    ///
222    /// Can be used to save RAM once the texture has been transferred to the GPU.
223    #[doc(alias = "ClearTexData")]
224    pub fn clear_tex_data(&mut self) {
225        unsafe {
226            sys::ImFontAtlas_ClearTexData(self.raw_mut());
227        }
228    }
229    /// Clears all the data used to build the textures and fonts
230    #[doc(alias = "ClearInputData")]
231    pub fn clear_input_data(&mut self) {
232        unsafe {
233            sys::ImFontAtlas_ClearInputData(self.raw_mut());
234        }
235    }
236}
237
238#[test]
239#[cfg(test)]
240fn test_font_atlas_memory_layout() {
241    use std::mem;
242    assert_eq!(
243        mem::size_of::<FontAtlas>(),
244        mem::size_of::<sys::ImFontAtlas>()
245    );
246    assert_eq!(
247        mem::align_of::<FontAtlas>(),
248        mem::align_of::<sys::ImFontAtlas>()
249    );
250    use sys::ImFontAtlas;
251    macro_rules! assert_field_offset {
252        ($l:ident, $r:ident) => {
253            assert_eq!(
254                memoffset::offset_of!(FontAtlas, $l),
255                memoffset::offset_of!(ImFontAtlas, $r)
256            );
257        };
258    }
259    assert_field_offset!(locked, Locked);
260    assert_field_offset!(flags, Flags);
261    assert_field_offset!(tex_id, TexID);
262    assert_field_offset!(tex_desired_width, TexDesiredWidth);
263    assert_field_offset!(tex_glyph_padding, TexGlyphPadding);
264    assert_field_offset!(tex_pixels_alpha8, TexPixelsAlpha8);
265    assert_field_offset!(tex_pixels_rgba32, TexPixelsRGBA32);
266    assert_field_offset!(tex_width, TexWidth);
267    assert_field_offset!(tex_height, TexHeight);
268    assert_field_offset!(tex_uv_scale, TexUvScale);
269    assert_field_offset!(tex_uv_white_pixel, TexUvWhitePixel);
270    assert_field_offset!(fonts, Fonts);
271    assert_field_offset!(custom_rects, CustomRects);
272    assert_field_offset!(config_data, ConfigData);
273    assert_field_offset!(tex_uv_lines, TexUvLines);
274    assert_field_offset!(pack_id_mouse_cursors, PackIdMouseCursors);
275    assert_field_offset!(pack_id_lines, PackIdLines);
276}
277
278/// A source for binary font data
279#[derive(Clone, Debug)]
280pub enum FontSource<'a> {
281    /// Default font included with the library (ProggyClean.ttf)
282    DefaultFontData { config: Option<FontConfig> },
283    /// Binary TTF/OTF font data
284    TtfData {
285        data: &'a [u8],
286        size_pixels: f32,
287        config: Option<FontConfig>,
288    },
289}
290
291/// Configuration settings for a font
292#[derive(Clone, Debug)]
293pub struct FontConfig {
294    /// Size in pixels for the rasterizer
295    pub size_pixels: f32,
296    /// Horizontal oversampling
297    pub oversample_h: i32,
298    /// Vertical oversampling
299    pub oversample_v: i32,
300    /// Align every glyph to pixel boundary
301    pub pixel_snap_h: bool,
302    /// Extra spacing (in pixels) between glyphs
303    pub glyph_extra_spacing: [f32; 2],
304    /// Offset for all glyphs in this font
305    pub glyph_offset: [f32; 2],
306    /// Unicode ranges to use from this font
307    pub glyph_ranges: FontGlyphRanges,
308    /// Minimum advance_x for glyphs
309    pub glyph_min_advance_x: f32,
310    /// Maximum advance_x for glyphs
311    pub glyph_max_advance_x: f32,
312    /// Settings for a custom font rasterizer if used
313    pub rasterizer_flags: u32,
314    /// Brighten (>1.0) or darken (<1.0) font output
315    pub rasterizer_multiply: f32,
316    /// Explicitly specify the ellipsis character.
317    ///
318    /// With multiple font sources the first specified ellipsis is used.
319    pub ellipsis_char: Option<char>,
320    pub name: Option<String>,
321}
322
323impl Default for FontConfig {
324    fn default() -> FontConfig {
325        FontConfig {
326            size_pixels: 0.0,
327            oversample_h: 3,
328            oversample_v: 1,
329            pixel_snap_h: false,
330            glyph_extra_spacing: [0.0, 0.0],
331            glyph_offset: [0.0, 0.0],
332            glyph_ranges: FontGlyphRanges::default(),
333            glyph_min_advance_x: 0.0,
334            glyph_max_advance_x: f32::MAX,
335            rasterizer_flags: 0,
336            rasterizer_multiply: 1.0,
337            ellipsis_char: None,
338            name: None,
339        }
340    }
341}
342
343impl FontConfig {
344    fn apply_to_raw_config(&self, raw: &mut sys::ImFontConfig, atlas: *mut sys::ImFontAtlas) {
345        raw.SizePixels = self.size_pixels;
346        raw.OversampleH = self.oversample_h;
347        raw.OversampleV = self.oversample_v;
348        raw.PixelSnapH = self.pixel_snap_h;
349        raw.GlyphExtraSpacing = self.glyph_extra_spacing.into();
350        raw.GlyphOffset = self.glyph_offset.into();
351        raw.GlyphRanges = unsafe { self.glyph_ranges.to_ptr(atlas) };
352        raw.GlyphMinAdvanceX = self.glyph_min_advance_x;
353        raw.GlyphMaxAdvanceX = self.glyph_max_advance_x;
354        raw.RasterizerFlags = self.rasterizer_flags;
355        raw.RasterizerMultiply = self.rasterizer_multiply;
356        raw.EllipsisChar = self.ellipsis_char.map(|x| x as u16).unwrap_or(0xffff);
357        if let Some(name) = self.name.as_ref() {
358            let bytes = name.as_bytes();
359            let mut len = bytes.len().min(raw.Name.len() - 1);
360            while !name.is_char_boundary(len) {
361                len -= 1;
362            }
363            unsafe {
364                bytes.as_ptr().copy_to(raw.Name.as_mut_ptr() as _, len);
365                raw.Name[len] = 0;
366            }
367        }
368    }
369}
370
371fn sys_font_config_default() -> sys::ImFontConfig {
372    unsafe {
373        let heap_allocated = sys::ImFontConfig_ImFontConfig();
374        let copy = *heap_allocated;
375        sys::ImFontConfig_destroy(heap_allocated);
376        copy
377    }
378}
379
380#[test]
381fn test_font_config_default() {
382    let sys_font_config = sys_font_config_default();
383    let font_config = FontConfig::default();
384    assert_eq!(font_config.size_pixels, sys_font_config.SizePixels);
385    assert_eq!(font_config.oversample_h, sys_font_config.OversampleH);
386    assert_eq!(font_config.oversample_v, sys_font_config.OversampleV);
387    assert_eq!(font_config.pixel_snap_h, sys_font_config.PixelSnapH);
388    assert_eq!(
389        font_config.glyph_extra_spacing[0],
390        sys_font_config.GlyphExtraSpacing.x
391    );
392    assert_eq!(
393        font_config.glyph_extra_spacing[1],
394        sys_font_config.GlyphExtraSpacing.y
395    );
396    assert_eq!(font_config.glyph_offset[0], sys_font_config.GlyphOffset.x);
397    assert_eq!(font_config.glyph_offset[1], sys_font_config.GlyphOffset.y);
398    assert_eq!(
399        font_config.glyph_min_advance_x,
400        sys_font_config.GlyphMinAdvanceX
401    );
402    assert_eq!(
403        font_config.glyph_max_advance_x,
404        sys_font_config.GlyphMaxAdvanceX
405    );
406    assert_eq!(
407        font_config.rasterizer_multiply,
408        sys_font_config.RasterizerMultiply
409    );
410}
411
412/// Handle to a font atlas texture
413#[derive(Clone, Debug)]
414pub struct FontAtlasTexture<'a> {
415    /// Texture width (in pixels)
416    pub width: u32,
417    /// Texture height (in pixels)
418    pub height: u32,
419    /// Raw texture data (in bytes).
420    ///
421    /// The format depends on which function was called to obtain this data.
422    pub data: &'a [u8],
423}
424
425/// A font atlas that can be shared between contexts
426#[derive(Debug)]
427pub struct SharedFontAtlas(pub(crate) *mut sys::ImFontAtlas);
428
429impl SharedFontAtlas {
430    #[doc(alias = "ImFontAtlas", alias = "ImFontAtlas::ImFontAtlas")]
431    pub fn create() -> SharedFontAtlas {
432        SharedFontAtlas(unsafe { sys::ImFontAtlas_ImFontAtlas() })
433    }
434}
435
436impl Drop for SharedFontAtlas {
437    #[doc(alias = "ImFontAtlas::Destory")]
438    fn drop(&mut self) {
439        unsafe { sys::ImFontAtlas_destroy(self.0) };
440    }
441}
442
443impl Deref for SharedFontAtlas {
444    type Target = FontAtlas;
445    fn deref(&self) -> &FontAtlas {
446        unsafe { &*(self.0 as *const FontAtlas) }
447    }
448}
449
450impl DerefMut for SharedFontAtlas {
451    fn deref_mut(&mut self) -> &mut FontAtlas {
452        unsafe { &mut *(self.0 as *mut FontAtlas) }
453    }
454}
455
456/// An immutably borrowed reference to a (possibly shared) font atlas
457pub enum FontAtlasRef<'a> {
458    Owned(&'a FontAtlas),
459    Shared(&'a cell::RefMut<'a, SharedFontAtlas>),
460}
461
462impl<'a> Deref for FontAtlasRef<'a> {
463    type Target = FontAtlas;
464    fn deref(&self) -> &FontAtlas {
465        use self::FontAtlasRef::*;
466        match self {
467            Owned(atlas) => atlas,
468            Shared(cell) => cell,
469        }
470    }
471}
472
473/// A mutably borrowed reference to a (possibly shared) font atlas
474pub enum FontAtlasRefMut<'a> {
475    Owned(&'a mut FontAtlas),
476    Shared(cell::RefMut<'a, SharedFontAtlas>),
477}
478
479impl<'a> Deref for FontAtlasRefMut<'a> {
480    type Target = FontAtlas;
481    fn deref(&self) -> &FontAtlas {
482        use self::FontAtlasRefMut::*;
483        match self {
484            Owned(atlas) => atlas,
485            Shared(cell) => cell,
486        }
487    }
488}
489
490impl<'a> DerefMut for FontAtlasRefMut<'a> {
491    fn deref_mut(&mut self) -> &mut FontAtlas {
492        use self::FontAtlasRefMut::*;
493        match self {
494            Owned(atlas) => atlas,
495            Shared(cell) => cell,
496        }
497    }
498}