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 #[repr(transparent)]
18 pub struct FontAtlasFlags: u32 {
19 const NO_POWER_OF_TWO_HEIGHT = sys::ImFontAtlasFlags_NoPowerOfTwoHeight;
21 const NO_MOUSE_CURSORS = sys::ImFontAtlasFlags_NoMouseCursors;
23 const NO_BAKED_LINES = sys::ImFontAtlasFlags_NoBakedLines;
25 }
26}
27
28#[derive(Copy, Clone, Debug, Eq, PartialEq)]
30pub struct FontId(pub(crate) *const Font);
31
32#[repr(C)]
34pub struct FontAtlas {
35 locked: bool,
36 pub flags: FontAtlasFlags,
38 pub tex_id: TextureId,
40 pub tex_desired_width: i32,
45 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 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 #[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 #[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 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 #[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 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 #[doc(alias = "Clear")]
208 pub fn clear(&mut self) {
209 unsafe {
210 sys::ImFontAtlas_Clear(self.raw_mut());
211 }
212 }
213 #[doc(alias = "ClearFonts")]
215 pub fn clear_fonts(&mut self) {
216 unsafe {
217 sys::ImFontAtlas_ClearFonts(self.raw_mut());
218 }
219 }
220 #[doc(alias = "ClearTexData")]
224 pub fn clear_tex_data(&mut self) {
225 unsafe {
226 sys::ImFontAtlas_ClearTexData(self.raw_mut());
227 }
228 }
229 #[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#[derive(Clone, Debug)]
280pub enum FontSource<'a> {
281 DefaultFontData { config: Option<FontConfig> },
283 TtfData {
285 data: &'a [u8],
286 size_pixels: f32,
287 config: Option<FontConfig>,
288 },
289}
290
291#[derive(Clone, Debug)]
293pub struct FontConfig {
294 pub size_pixels: f32,
296 pub oversample_h: i32,
298 pub oversample_v: i32,
300 pub pixel_snap_h: bool,
302 pub glyph_extra_spacing: [f32; 2],
304 pub glyph_offset: [f32; 2],
306 pub glyph_ranges: FontGlyphRanges,
308 pub glyph_min_advance_x: f32,
310 pub glyph_max_advance_x: f32,
312 pub rasterizer_flags: u32,
314 pub rasterizer_multiply: f32,
316 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#[derive(Clone, Debug)]
414pub struct FontAtlasTexture<'a> {
415 pub width: u32,
417 pub height: u32,
419 pub data: &'a [u8],
423}
424
425#[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
456pub 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
473pub 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}