use crate::fonts::Font;
use crate::sys;
use std::ffi::{CString, c_char};
use std::marker::PhantomData;
use std::ptr;
use std::rc::Rc;
#[derive(Debug)]
pub struct FontAtlas {
raw: *mut sys::ImFontAtlas,
owned: bool,
_phantom: PhantomData<*mut sys::ImFontAtlas>,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct FontId(pub(crate) *const sys::ImFont);
pub struct FontLoader {
raw: sys::ImFontLoader,
_name: CString,
}
impl FontLoader {
pub fn new(name: &str) -> Result<Self, std::ffi::NulError> {
let name_cstring = CString::new(name)?;
let mut raw = unsafe {
let p = sys::ImFontLoader_ImFontLoader();
if p.is_null() {
panic!("ImFontLoader_ImFontLoader() returned null");
}
let v = *p;
sys::ImFontLoader_destroy(p);
v
};
raw.Name = name_cstring.as_ptr();
Ok(Self {
raw,
_name: name_cstring,
})
}
pub(crate) fn as_ptr(&self) -> *const sys::ImFontLoader {
&self.raw
}
pub fn with_loader_init<F>(self, _callback: F) -> Self
where
F: Fn(&mut FontAtlas) -> bool + 'static,
{
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FontLoaderFlags(pub u32);
impl FontLoaderFlags {
pub const NONE: Self = Self(0);
pub const NO_HINTING: Self = Self(1 << 0);
pub const NO_AUTOHINT: Self = Self(1 << 1);
pub const FORCE_AUTOHINT: Self = Self(1 << 2);
pub const LIGHT_HINTING: Self = Self(1 << 3);
pub const MONO_HINTING: Self = Self(1 << 4);
pub const BOLD: Self = Self(1 << 5);
pub const OBLIQUE: Self = Self(1 << 6);
pub const MONOCHROME: Self = Self(1 << 7);
pub const LOAD_COLOR: Self = Self(1 << 8);
pub const BITMAP: Self = Self(1 << 9);
}
impl std::ops::BitOr for FontLoaderFlags {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl std::ops::BitOrAssign for FontLoaderFlags {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
#[derive(Debug, Clone)]
pub struct SharedFontAtlas(pub(crate) Rc<*mut sys::ImFontAtlas>);
impl SharedFontAtlas {
pub fn create() -> SharedFontAtlas {
unsafe {
let raw_atlas = sys::ImFontAtlas_ImFontAtlas();
if raw_atlas.is_null() {
panic!("ImFontAtlas_ImFontAtlas() returned null");
}
SharedFontAtlas(Rc::new(raw_atlas))
}
}
pub(crate) fn as_ptr_mut(&mut self) -> *mut sys::ImFontAtlas {
*self.0
}
}
impl Drop for SharedFontAtlas {
fn drop(&mut self) {
if Rc::strong_count(&self.0) == 1 {
unsafe {
let atlas_ptr = *self.0;
if !atlas_ptr.is_null() {
sys::ImFontAtlas_destroy(atlas_ptr);
}
}
}
}
}
impl FontAtlas {
pub fn new() -> Self {
unsafe {
let raw = sys::ImFontAtlas_ImFontAtlas();
if raw.is_null() {
panic!("ImFontAtlas_ImFontAtlas() returned null");
}
Self {
raw,
owned: true,
_phantom: PhantomData,
}
}
}
pub fn with_font_loader(loader: &FontLoader) -> Self {
let mut atlas = Self::new();
atlas.set_font_loader(loader);
atlas
}
pub(crate) unsafe fn from_raw(raw: *mut sys::ImFontAtlas) -> Self {
Self {
raw,
owned: false,
_phantom: PhantomData,
}
}
pub fn raw(&self) -> *mut sys::ImFontAtlas {
self.raw
}
pub fn set_font_loader(&mut self, loader: &FontLoader) {
unsafe {
sys::ImFontAtlas_SetFontLoader(self.raw, loader.as_ptr());
}
}
pub fn set_font_loader_flags(&mut self, flags: FontLoaderFlags) {
unsafe {
(*self.raw).FontLoaderFlags = flags.0;
}
}
pub fn font_loader_flags(&self) -> FontLoaderFlags {
unsafe { FontLoaderFlags((*self.raw).FontLoaderFlags) }
}
#[doc(alias = "AddFont")]
pub fn add_font(&mut self, font_sources: &[FontSource<'_>]) -> crate::fonts::FontId {
let Some((head, tail)) = font_sources.split_first() else {
panic!("FontAtlas::add_font requires at least one FontSource");
};
let font_id = self.add_font_internal(head, false);
for font in tail {
self.add_font_internal(font, true);
}
font_id
}
fn add_font_internal(
&mut self,
font_source: &FontSource<'_>,
merge_mode: bool,
) -> crate::fonts::FontId {
match font_source {
FontSource::DefaultFontData {
size_pixels,
config,
} => {
let size = size_pixels.unwrap_or(0.0);
let mut cfg = config.clone().unwrap_or_default();
if size > 0.0 {
cfg = cfg.size_pixels(size);
}
if merge_mode {
cfg = cfg.merge_mode(true);
}
let font = self.add_font_default(Some(&cfg));
font.id()
}
FontSource::TtfData {
data,
size_pixels,
config,
} => {
let size = size_pixels.unwrap_or(0.0);
let mut cfg = config.clone().unwrap_or_default();
if size > 0.0 {
cfg = cfg.size_pixels(size);
}
if merge_mode {
cfg = cfg.merge_mode(true);
}
let font = self
.add_font_from_memory_ttf(data, size, Some(&cfg), None)
.expect("Failed to add TTF font from memory");
font.id()
}
FontSource::CompressedTtfData {
data,
size_pixels,
config,
} => {
let size = size_pixels.unwrap_or(0.0);
let mut cfg = config.clone().unwrap_or_default();
if size > 0.0 {
cfg = cfg.size_pixels(size);
}
if merge_mode {
cfg = cfg.merge_mode(true);
}
let font = self
.add_font_from_memory_compressed_ttf(data, size, Some(&cfg), None)
.expect("Failed to add compressed TTF font from memory");
font.id()
}
FontSource::CompressedTtfBase85 {
data,
size_pixels,
config,
} => {
let size = size_pixels.unwrap_or(0.0);
let mut cfg = config.clone().unwrap_or_default();
if size > 0.0 {
cfg = cfg.size_pixels(size);
}
if merge_mode {
cfg = cfg.merge_mode(true);
}
let font = self
.add_font_from_memory_compressed_base85_ttf(data, size, Some(&cfg), None)
.expect("Failed to add base85 compressed TTF font from memory");
font.id()
}
FontSource::TtfFile {
path,
size_pixels,
config,
} => {
let size = size_pixels.unwrap_or(0.0);
let mut cfg = config.clone().unwrap_or_default();
if size > 0.0 {
cfg = cfg.size_pixels(size);
}
if merge_mode {
cfg = cfg.merge_mode(true);
}
let font = self
.add_font_from_file_ttf(path, size, Some(&cfg), None)
.expect("Failed to add TTF font from file");
font.id()
}
}
}
#[doc(alias = "AddFont")]
pub fn add_font_with_config(&mut self, font_cfg: &FontConfig) -> &mut Font {
unsafe {
let font_ptr = sys::ImFontAtlas_AddFont(self.raw, font_cfg.raw());
if font_cfg.raw.MergeMode {
self.discard_bakes(0);
}
Font::from_raw_mut(font_ptr)
}
}
#[doc(alias = "AddFontDefault")]
pub fn add_font_default(&mut self, font_cfg: Option<&FontConfig>) -> &mut Font {
unsafe {
let cfg_ptr = font_cfg.map_or(ptr::null(), |cfg| cfg.raw());
let font_ptr = sys::ImFontAtlas_AddFontDefault(self.raw, cfg_ptr);
if let Some(cfg) = font_cfg {
if cfg.raw.MergeMode {
self.discard_bakes(0);
}
}
Font::from_raw_mut(font_ptr)
}
}
#[doc(alias = "AddFontFromFileTTF")]
pub fn add_font_from_file_ttf(
&mut self,
filename: &str,
size_pixels: f32,
font_cfg: Option<&FontConfig>,
glyph_ranges: Option<&[sys::ImWchar]>,
) -> Option<&mut Font> {
unsafe {
let filename_cstr = std::ffi::CString::new(filename).ok()?;
let cfg_ptr = font_cfg.map_or(ptr::null(), |cfg| cfg.raw());
let ranges_ptr = glyph_ranges.map_or(ptr::null(), |ranges| ranges.as_ptr());
let font_ptr = sys::ImFontAtlas_AddFontFromFileTTF(
self.raw,
filename_cstr.as_ptr(),
size_pixels,
cfg_ptr,
ranges_ptr,
);
if font_ptr.is_null() {
None
} else {
if let Some(cfg) = font_cfg {
if cfg.raw.MergeMode {
self.discard_bakes(0);
}
}
Some(Font::from_raw_mut(font_ptr))
}
}
}
#[doc(alias = "AddFontFromMemoryTTF")]
pub fn add_font_from_memory_ttf(
&mut self,
font_data: &[u8],
size_pixels: f32,
font_cfg: Option<&FontConfig>,
glyph_ranges: Option<&[sys::ImWchar]>,
) -> Option<&mut Font> {
if font_data.len() <= 100 {
return None;
}
let font_data_len = i32::try_from(font_data.len()).ok()?;
unsafe {
let mem = sys::igMemAlloc(font_data.len());
if mem.is_null() {
return None;
}
std::ptr::copy_nonoverlapping(font_data.as_ptr(), mem as *mut u8, font_data.len());
let cfg = font_cfg
.cloned()
.unwrap_or_default()
.font_data_owned_by_atlas(true);
let is_merge = cfg.raw.MergeMode;
let cfg_ptr = cfg.raw();
let ranges_ptr = glyph_ranges.map_or(ptr::null(), |ranges| ranges.as_ptr());
let font_ptr = sys::ImFontAtlas_AddFontFromMemoryTTF(
self.raw,
mem,
font_data_len,
size_pixels,
cfg_ptr,
ranges_ptr,
);
if font_ptr.is_null() {
sys::igMemFree(mem);
None
} else {
if is_merge {
self.discard_bakes(0);
}
Some(Font::from_raw_mut(font_ptr))
}
}
}
#[doc(alias = "AddFontFromMemoryCompressedTTF")]
pub fn add_font_from_memory_compressed_ttf(
&mut self,
compressed_font_data: &[u8],
size_pixels: f32,
font_cfg: Option<&FontConfig>,
glyph_ranges: Option<&[sys::ImWchar]>,
) -> Option<&mut Font> {
if compressed_font_data.is_empty() {
return None;
}
let compressed_len = i32::try_from(compressed_font_data.len()).ok()?;
unsafe {
let cfg = font_cfg.cloned().unwrap_or_default();
let is_merge = cfg.raw.MergeMode;
let cfg_ptr = cfg.raw();
let ranges_ptr = glyph_ranges.map_or(ptr::null(), |ranges| ranges.as_ptr());
let font_ptr = sys::ImFontAtlas_AddFontFromMemoryCompressedTTF(
self.raw,
compressed_font_data.as_ptr() as *const std::os::raw::c_void,
compressed_len,
size_pixels,
cfg_ptr,
ranges_ptr,
);
if font_ptr.is_null() {
None
} else {
if is_merge {
self.discard_bakes(0);
}
Some(Font::from_raw_mut(font_ptr))
}
}
}
#[doc(alias = "AddFontFromMemoryCompressedBase85TTF")]
pub fn add_font_from_memory_compressed_base85_ttf(
&mut self,
compressed_font_data_base85: &str,
size_pixels: f32,
font_cfg: Option<&FontConfig>,
glyph_ranges: Option<&[sys::ImWchar]>,
) -> Option<&mut Font> {
if compressed_font_data_base85.is_empty() {
return None;
}
let base85 = std::ffi::CString::new(compressed_font_data_base85).ok()?;
unsafe {
let cfg = font_cfg.cloned().unwrap_or_default();
let is_merge = cfg.raw.MergeMode;
let cfg_ptr = cfg.raw();
let ranges_ptr = glyph_ranges.map_or(ptr::null(), |ranges| ranges.as_ptr());
let font_ptr = sys::ImFontAtlas_AddFontFromMemoryCompressedBase85TTF(
self.raw,
base85.as_ptr(),
size_pixels,
cfg_ptr,
ranges_ptr,
);
if font_ptr.is_null() {
None
} else {
if is_merge {
self.discard_bakes(0);
}
Some(Font::from_raw_mut(font_ptr))
}
}
}
#[doc(alias = "RemoveFont")]
pub fn remove_font(&mut self, font: &mut Font) {
unsafe { sys::ImFontAtlas_RemoveFont(self.raw, font.raw()) }
}
#[doc(alias = "Clear")]
pub fn clear(&mut self) {
unsafe { sys::ImFontAtlas_Clear(self.raw) }
}
#[doc(alias = "ClearFonts")]
pub fn clear_fonts(&mut self) {
unsafe { sys::ImFontAtlas_ClearFonts(self.raw) }
}
#[doc(alias = "ClearTexData")]
pub fn clear_tex_data(&mut self) {
unsafe { sys::ImFontAtlas_ClearTexData(self.raw) }
}
#[doc(alias = "GetGlyphRangesDefault")]
pub fn get_glyph_ranges_default_ptr(&self) -> *const sys::ImWchar {
if self.raw.is_null() {
return std::ptr::null();
}
unsafe { sys::ImFontAtlas_GetGlyphRangesDefault(self.raw) }
}
#[doc(alias = "GetGlyphRangesDefault")]
pub fn get_glyph_ranges_default(&self) -> &[sys::ImWchar] {
unsafe {
let ptr = self.get_glyph_ranges_default_ptr();
if ptr.is_null() {
&[]
} else {
const MAX_WORDS: usize = 2048;
let mut i = 0usize;
while i < MAX_WORDS {
if *ptr.add(i) == 0 {
return std::slice::from_raw_parts(ptr, i + 1);
}
i = i.saturating_add(2);
}
debug_assert!(
false,
"ImFontAtlas_GetGlyphRangesDefault() did not terminate within MAX_WORDS"
);
&[]
}
}
}
#[doc(alias = "Build")]
pub fn build(&mut self) -> bool {
if self.raw.is_null() {
return false;
}
unsafe {
sys::igImFontAtlasBuildMain(self.raw);
(*self.raw).TexIsBuilt
}
}
#[doc(alias = "ImFontAtlasBuildDiscardBakes")]
pub fn discard_bakes(&mut self, unused_frames: i32) {
if self.raw.is_null() {
return;
}
unsafe {
if (*self.raw).Builder.is_null() {
return;
}
sys::igImFontAtlasBuildDiscardBakes(self.raw, unused_frames);
}
}
pub fn is_built(&self) -> bool {
if self.raw.is_null() {
return false;
}
unsafe { (*self.raw).TexIsBuilt }
}
pub fn get_tex_data_info(&self) -> Option<(u32, u32)> {
if self.raw.is_null() {
return None;
}
unsafe {
if (*self.raw).TexIsBuilt {
let min_width = (*self.raw).TexMinWidth as u32;
let min_height = (*self.raw).TexMinHeight as u32;
Some((min_width, min_height))
} else {
None
}
}
}
pub unsafe fn get_tex_data_ptr(&self) -> Option<(*const u8, u32, u32)> {
if self.raw.is_null() {
return None;
}
unsafe {
if (*self.raw).TexIsBuilt {
let tex_data = (*self.raw).TexData;
if !tex_data.is_null() {
let width = (*tex_data).Width as u32;
let height = (*tex_data).Height as u32;
let pixels = (*tex_data).Pixels;
if !pixels.is_null() {
Some((pixels, width, height))
} else {
None
}
} else {
None
}
} else {
None
}
}
}
pub fn get_tex_ref(&self) -> sys::ImTextureRef {
unsafe { (*self.raw).TexRef }
}
pub fn set_tex_ref(&mut self, tex_ref: sys::ImTextureRef) {
unsafe {
(*self.raw).TexRef = tex_ref;
}
}
pub fn tex_data_mut(&mut self) -> Option<&mut crate::texture::TextureData> {
let ptr = unsafe { (*self.raw).TexData };
if ptr.is_null() {
None
} else {
Some(unsafe { crate::texture::TextureData::from_raw(ptr) })
}
}
pub fn set_texture_id(&mut self, tex_id: crate::texture::TextureId) {
let tex_ref = if let Some(td) = self.tex_data_mut() {
td.set_tex_id(tex_id);
td.set_status(crate::texture::TextureStatus::OK);
td.texture_ref().raw()
} else {
sys::ImTextureRef {
_TexData: std::ptr::null_mut(),
_TexID: tex_id.id() as sys::ImTextureID,
}
};
self.set_tex_ref(tex_ref);
}
pub fn get_tex_data(&self) -> *mut sys::ImTextureData {
unsafe { (*self.raw).TexData }
}
pub fn get_tex_uv_scale(&self) -> [f32; 2] {
unsafe {
let scale = (*self.raw).TexUvScale;
[scale.x, scale.y]
}
}
pub fn get_tex_uv_white_pixel(&self) -> [f32; 2] {
unsafe {
let pixel = (*self.raw).TexUvWhitePixel;
[pixel.x, pixel.y]
}
}
}
impl Default for FontAtlas {
fn default() -> Self {
Self::new()
}
}
impl Drop for FontAtlas {
fn drop(&mut self) {
if self.owned && !self.raw.is_null() {
unsafe {
sys::ImFontAtlas_destroy(self.raw);
}
}
}
}
#[derive(Debug)]
pub struct FontConfig {
raw: sys::ImFontConfig,
glyph_exclude_ranges: Option<Vec<sys::ImWchar>>,
}
impl Clone for FontConfig {
fn clone(&self) -> Self {
let mut raw = self.raw;
let glyph_exclude_ranges = self.glyph_exclude_ranges.clone();
if let Some(ref ranges) = glyph_exclude_ranges {
raw.GlyphExcludeRanges = ranges.as_ptr();
}
Self {
raw,
glyph_exclude_ranges,
}
}
}
impl FontConfig {
pub fn new() -> Self {
unsafe {
let cfg = sys::ImFontConfig_ImFontConfig();
if cfg.is_null() {
panic!("ImFontConfig_ImFontConfig() returned null");
}
let raw = *cfg;
sys::ImFontConfig_destroy(cfg);
Self {
raw,
glyph_exclude_ranges: None,
}
}
}
pub(crate) fn raw(&self) -> *const sys::ImFontConfig {
&self.raw
}
pub fn size_pixels(mut self, size: f32) -> Self {
self.raw.SizePixels = size;
self
}
pub fn merge_mode(mut self, merge: bool) -> Self {
self.raw.MergeMode = merge;
self
}
pub fn font_data_owned_by_atlas(mut self, owned: bool) -> Self {
self.raw.FontDataOwnedByAtlas = owned;
self
}
pub fn font_loader_flags(mut self, flags: FontLoaderFlags) -> Self {
self.raw.FontLoaderFlags = flags.0;
self
}
pub fn glyph_exclude_ranges(mut self, ranges: &[u32]) -> Self {
if ranges.is_empty() {
self.raw.GlyphExcludeRanges = ptr::null();
self.glyph_exclude_ranges = None;
return self;
}
const IMWCHAR_MAX: u32 = if std::mem::size_of::<sys::ImWchar>() == 2 {
0xFFFF
} else {
0x10FFFF
};
let mut converted: Vec<sys::ImWchar> = Vec::with_capacity(ranges.len() + 1);
for &v in ranges {
assert!(
v <= IMWCHAR_MAX,
"glyph_exclude_ranges value out of range for ImWchar (max {IMWCHAR_MAX:#x}): {v:#x}"
);
let v = sys::ImWchar::try_from(v).unwrap_or_else(|_| {
panic!("glyph_exclude_ranges value {v:#x} was not representable as ImWchar")
});
converted.push(v);
}
if converted.last().copied() != Some(0) {
converted.push(0);
}
self.raw.GlyphExcludeRanges = converted.as_ptr();
self.glyph_exclude_ranges = Some(converted);
self
}
pub fn font_loader(mut self, loader: &FontLoader) -> Self {
self.raw.FontLoader = loader.as_ptr();
self
}
pub fn name(mut self, name: &str) -> Self {
let name_bytes = name.as_bytes();
let copy_len = std::cmp::min(name_bytes.len(), self.raw.Name.len() - 1);
for i in 0..self.raw.Name.len() {
self.raw.Name[i] = 0;
}
for (i, &byte) in name_bytes.iter().take(copy_len).enumerate() {
self.raw.Name[i] = byte as c_char;
}
self
}
pub fn glyph_offset(mut self, offset: [f32; 2]) -> Self {
self.raw.GlyphOffset.x = offset[0];
self.raw.GlyphOffset.y = offset[1];
self
}
pub fn glyph_min_advance_x(mut self, advance: f32) -> Self {
self.raw.GlyphMinAdvanceX = advance;
self
}
pub fn glyph_max_advance_x(mut self, advance: f32) -> Self {
self.raw.GlyphMaxAdvanceX = advance;
self
}
pub fn glyph_extra_advance_x(mut self, advance: f32) -> Self {
self.raw.GlyphExtraAdvanceX = advance;
self
}
pub fn rasterizer_multiply(mut self, multiply: f32) -> Self {
self.raw.RasterizerMultiply = multiply;
self
}
pub fn rasterizer_density(mut self, density: f32) -> Self {
self.raw.RasterizerDensity = density;
self
}
pub fn pixel_snap_h(mut self, snap: bool) -> Self {
self.raw.PixelSnapH = snap;
self
}
pub fn oversample_h(mut self, oversample: i8) -> Self {
self.raw.OversampleH = oversample;
self
}
pub fn oversample_v(mut self, oversample: i8) -> Self {
self.raw.OversampleV = oversample;
self
}
}
impl Default for FontConfig {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn font_config_glyph_exclude_ranges_converts_and_terminates() {
let cfg = FontConfig::new().glyph_exclude_ranges(&[0x41]);
assert!(!cfg.raw.GlyphExcludeRanges.is_null());
unsafe {
assert_eq!(*cfg.raw.GlyphExcludeRanges.add(0), 0x41 as sys::ImWchar);
assert_eq!(*cfg.raw.GlyphExcludeRanges.add(1), 0);
}
}
#[test]
fn font_config_glyph_exclude_ranges_accepts_non_bmp_when_wchar32() {
if std::mem::size_of::<sys::ImWchar>() != 4 {
return;
}
let cfg = FontConfig::new().glyph_exclude_ranges(&[0x1_0000]);
assert!(!cfg.raw.GlyphExcludeRanges.is_null());
unsafe {
assert_eq!(*cfg.raw.GlyphExcludeRanges.add(0), 0x1_0000 as sys::ImWchar);
assert_eq!(*cfg.raw.GlyphExcludeRanges.add(1), 0);
}
}
#[test]
fn font_config_glyph_exclude_ranges_rejects_out_of_range() {
let out_of_range = if std::mem::size_of::<sys::ImWchar>() == 2 {
0x1_0000
} else {
0x11_0000
};
let res = std::panic::catch_unwind(|| {
let _ = FontConfig::new().glyph_exclude_ranges(&[out_of_range]);
});
assert!(res.is_err());
}
#[test]
fn add_font_from_memory_ttf_rejects_too_small_buffers() {
let mut ctx = crate::Context::create();
let mut fonts = ctx.font_atlas_mut();
assert!(
fonts
.add_font_from_memory_ttf(&[0u8; 10], 13.0, None, None)
.is_none()
);
}
#[test]
fn set_texture_id_preserves_managed_tex_data_reference() {
let mut ctx = crate::Context::create();
let mut fonts = ctx.font_atlas_mut();
let _ = fonts.build();
let raw_tex_data = fonts.get_tex_data();
assert!(!raw_tex_data.is_null());
let texture_id = crate::texture::TextureId::new(0x1234);
fonts.set_texture_id(texture_id);
let mut tex_ref = fonts.get_tex_ref();
assert_eq!(tex_ref._TexData, raw_tex_data);
let resolved = unsafe { sys::ImTextureRef_GetTexID(&mut tex_ref) };
assert_eq!(resolved, texture_id.id() as sys::ImTextureID);
}
}
#[derive(Clone, Debug)]
pub enum FontSource<'a> {
DefaultFontData {
size_pixels: Option<f32>,
config: Option<FontConfig>,
},
TtfData {
data: &'a [u8],
size_pixels: Option<f32>,
config: Option<FontConfig>,
},
CompressedTtfData {
data: &'a [u8],
size_pixels: Option<f32>,
config: Option<FontConfig>,
},
CompressedTtfBase85 {
data: &'a str,
size_pixels: Option<f32>,
config: Option<FontConfig>,
},
TtfFile {
path: &'a str,
size_pixels: Option<f32>,
config: Option<FontConfig>,
},
}
impl<'a> FontSource<'a> {
pub fn default_font() -> Self {
Self::DefaultFontData {
size_pixels: None,
config: None,
}
}
pub fn default_font_with_size(size: f32) -> Self {
Self::DefaultFontData {
size_pixels: Some(size),
config: None,
}
}
pub fn ttf_data(data: &'a [u8]) -> Self {
Self::TtfData {
data,
size_pixels: None,
config: None,
}
}
pub fn ttf_data_with_size(data: &'a [u8], size: f32) -> Self {
Self::TtfData {
data,
size_pixels: Some(size),
config: None,
}
}
pub fn compressed_ttf_data(data: &'a [u8]) -> Self {
Self::CompressedTtfData {
data,
size_pixels: None,
config: None,
}
}
pub fn compressed_ttf_data_with_size(data: &'a [u8], size: f32) -> Self {
Self::CompressedTtfData {
data,
size_pixels: Some(size),
config: None,
}
}
pub fn compressed_ttf_base85(data: &'a str) -> Self {
Self::CompressedTtfBase85 {
data,
size_pixels: None,
config: None,
}
}
pub fn compressed_ttf_base85_with_size(data: &'a str, size: f32) -> Self {
Self::CompressedTtfBase85 {
data,
size_pixels: Some(size),
config: None,
}
}
pub fn ttf_file(path: &'a str) -> Self {
Self::TtfFile {
path,
size_pixels: None,
config: None,
}
}
pub fn ttf_file_with_size(path: &'a str, size: f32) -> Self {
Self::TtfFile {
path,
size_pixels: Some(size),
config: None,
}
}
pub fn with_config(mut self, config: FontConfig) -> Self {
match &mut self {
Self::DefaultFontData { config: cfg, .. } => *cfg = Some(config),
Self::TtfData { config: cfg, .. } => *cfg = Some(config),
Self::CompressedTtfData { config: cfg, .. } => *cfg = Some(config),
Self::CompressedTtfBase85 { config: cfg, .. } => *cfg = Some(config),
Self::TtfFile { config: cfg, .. } => *cfg = Some(config),
}
self
}
}
#[derive(Clone, Debug)]
pub struct FontAtlasTexture<'a> {
pub width: u32,
pub height: u32,
pub data: &'a [u8],
}