1use super::{FontBaked, PixelImage, SubPixelImage, Vector2, vec2};
2use bitflags::bitflags;
3use easy_imgui_sys::*;
4use image::GenericImage;
5
6pub trait GlyphLoader {
8 fn contains_glyph(&mut self, codepoint: char) -> bool;
10 fn load_glyph(&mut self, arg: GlyphLoaderArg<'_>);
14 fn font_baked_init(&mut self, _baked: &mut FontBaked) {}
18 fn font_baked_destroy(&mut self, _baked: &mut FontBaked) {}
22}
23
24pub type BoxGlyphLoader = Box<dyn GlyphLoader + 'static>;
25
26pub struct FontLoader(pub ImFontLoader);
28unsafe impl Sync for FontLoader {}
29
30pub static FONT_LOADER: FontLoader = FontLoader::new();
31
32impl FontLoader {
33 const fn new() -> Self {
34 let inner = ImFontLoader {
35 Name: c"easy_imgui::FontLoader".as_ptr(),
36 LoaderInit: None,
37 LoaderShutdown: None,
38 FontSrcInit: Some(font_src_init),
39 FontSrcDestroy: Some(font_src_destroy),
40 FontSrcContainsGlyph: Some(font_src_contains_glyph),
41 FontBakedInit: Some(font_baked_init),
42 FontBakedDestroy: Some(font_baked_destroy),
43 FontBakedLoadGlyph: Some(font_baked_load_glyph),
44 FontBakedSrcLoaderDataSize: 0,
45 };
46 FontLoader(inner)
47 }
48}
49
50unsafe extern "C" fn font_src_init(_atlas: *mut ImFontAtlas, src: *mut ImFontConfig) -> bool {
51 unsafe {
52 let ptr = (*src).FontLoaderData;
53 if !ptr.is_null() {
54 return false;
55 }
56 (*src).FontLoaderData = std::mem::replace(&mut (*src).FontData, std::ptr::null_mut());
57 true
58 }
59}
60
61unsafe extern "C" fn font_src_destroy(_atlas: *mut ImFontAtlas, src: *mut ImFontConfig) {
62 unsafe {
63 let ptr = (*src).FontLoaderData;
64 if ptr.is_null() {
65 return;
66 }
67 let ptr = ptr as *mut BoxGlyphLoader;
68 drop(Box::from_raw(ptr));
69 (*src).FontLoaderData = std::ptr::null_mut();
70 }
71}
72
73unsafe extern "C" fn font_src_contains_glyph(
74 _atlas: *mut ImFontAtlas,
75 src: *mut ImFontConfig,
76 codepoint: ImWchar,
77) -> bool {
78 unsafe {
79 let ptr = (*src).FontLoaderData;
80 if ptr.is_null() {
81 return false;
82 }
83 let Some(c) = char::from_u32(codepoint) else {
84 return false;
85 };
86 let ptr = ptr as *mut BoxGlyphLoader;
87 let ldr = &mut *ptr;
88 ldr.contains_glyph(c)
89 }
90}
91
92unsafe extern "C" fn font_baked_load_glyph(
93 atlas: *mut ImFontAtlas,
94 src: *mut ImFontConfig,
95 baked: *mut ImFontBaked,
96 _loader_data_for_baked_src: *mut ::std::os::raw::c_void,
97 codepoint: ImWchar,
98 out_glyph: *mut ImFontGlyph,
99 out_advance_x: *mut f32,
100) -> bool {
101 unsafe {
102 let ptr = (*src).FontLoaderData;
103 if ptr.is_null() {
104 return false;
105 }
106 let Some(codepoint) = char::from_u32(codepoint) else {
107 return false;
108 };
109 let ptr = ptr as *mut BoxGlyphLoader;
110 let ldr = &mut *ptr;
111
112 let atlas = &mut *atlas;
113 let baked = &mut *baked;
114 let src = &mut *src;
115 let mut oversample_h = 0;
116 let mut oversample_v = 0;
117 ImFontAtlasBuildGetOversampleFactors(src, baked, &mut oversample_h, &mut oversample_v);
118 let rasterizer_density = src.RasterizerDensity * baked.RasterizerDensity;
119 let mut result = false;
120
121 let output = if out_advance_x.is_null() {
122 GlyphLoaderResult::Glyph(&mut *out_glyph)
123 } else {
124 GlyphLoaderResult::AdvanceX(&mut *out_advance_x)
125 };
126 let arg = GlyphLoaderArg {
127 codepoint,
128 oversample: vec2(oversample_h as f32, oversample_v as f32),
129 rasterizer_density,
130 result: &mut result,
131 atlas,
132 src,
133 baked,
134 output,
135 };
136 ldr.load_glyph(arg);
137 result
138 }
139}
140
141unsafe extern "C" fn font_baked_init(
142 _atlas: *mut ImFontAtlas,
143 src: *mut ImFontConfig,
144 baked: *mut ImFontBaked,
145 _loader_data_for_baked_src: *mut ::std::os::raw::c_void,
146) -> bool {
147 unsafe {
148 let ptr = (*src).FontLoaderData;
149 if ptr.is_null() {
150 return false;
151 }
152 let ptr = ptr as *mut BoxGlyphLoader;
153 let ldr = &mut *ptr;
154
155 ldr.font_baked_init(FontBaked::cast_mut(&mut *baked));
156 true
157 }
158}
159
160unsafe extern "C" fn font_baked_destroy(
161 _atlas: *mut ImFontAtlas,
162 src: *mut ImFontConfig,
163 baked: *mut ImFontBaked,
164 _loader_data_for_baked_src: *mut ::std::os::raw::c_void,
165) {
166 unsafe {
167 let ptr = (*src).FontLoaderData;
168 if ptr.is_null() {
169 return;
170 }
171 let ptr = ptr as *mut BoxGlyphLoader;
172 let ldr = &mut *ptr;
173
174 ldr.font_baked_destroy(FontBaked::cast_mut(&mut *baked));
175 }
176}
177
178enum GlyphLoaderResult<'a> {
179 Glyph(&'a mut ImFontGlyph),
180 AdvanceX(&'a mut f32),
181}
182
183pub struct GlyphLoaderArg<'a> {
187 codepoint: char,
188 oversample: Vector2,
190 rasterizer_density: f32,
191 result: &'a mut bool,
192
193 atlas: &'a mut ImFontAtlas,
194 src: &'a mut ImFontConfig,
195 baked: &'a mut ImFontBaked,
196 output: GlyphLoaderResult<'a>,
197}
198
199bitflags! {
200 #[derive(Copy, Clone, Debug)]
202 pub struct GlyphBuildFlags: u32 {
203 const IGNORE_OVERSAMPLE = 1;
205 const IGNORE_DPI = 2;
207 const PRESCALED_SIZE = 4;
209 const IGNORE_SCALE = Self::IGNORE_OVERSAMPLE.bits() | Self::IGNORE_DPI.bits();
213 }
214}
215
216impl GlyphLoaderArg<'_> {
217 pub fn codepoint(&self) -> char {
219 self.codepoint
220 }
221 pub fn font_size(&self) -> f32 {
223 self.baked.Size
224 }
225 pub fn dpi_density(&self) -> f32 {
229 self.rasterizer_density
230 }
231 pub fn set_dpi_density(&mut self, scale: f32) {
237 self.rasterizer_density = scale;
238 }
239 pub fn oversample(&self) -> Vector2 {
244 self.oversample
245 }
246 pub fn set_oversample(&mut self, oversample: Vector2) {
251 self.oversample = oversample;
252 }
253 pub fn only_advance_x(&self) -> bool {
264 matches!(self.output, GlyphLoaderResult::AdvanceX(_))
265 }
266 pub fn build(
278 mut self,
279 origin: Vector2,
280 mut size: Vector2,
281 advance_x: f32,
282 flags: GlyphBuildFlags,
283 draw: impl FnOnce(&mut SubPixelImage<'_, '_>),
284 ) {
285 match self.output {
286 GlyphLoaderResult::AdvanceX(out_advance_x) => {
287 *out_advance_x = advance_x;
288 }
289 GlyphLoaderResult::Glyph(out_glyph) => {
290 if flags.contains(GlyphBuildFlags::IGNORE_DPI) {
291 self.rasterizer_density = 1.0;
292 }
293 if flags.contains(GlyphBuildFlags::IGNORE_OVERSAMPLE) {
294 self.oversample = vec2(1.0, 1.0);
295 }
296
297 let scale_for_raster = self.rasterizer_density * self.oversample;
298
299 let (bmp_size_x, bmp_size_y);
300
301 if flags.contains(GlyphBuildFlags::PRESCALED_SIZE) {
302 bmp_size_x = size.x.round() as u32;
303 bmp_size_y = size.y.round() as u32;
304 size.x /= scale_for_raster.x;
305 size.y /= scale_for_raster.y;
306 } else {
307 bmp_size_x = (size.x * scale_for_raster.x).round() as u32;
308 bmp_size_y = (size.y * scale_for_raster.y).round() as u32;
309 }
310
311 let pack_id = unsafe {
312 ImFontAtlasPackAddRect(
313 self.atlas,
314 bmp_size_x as i32,
315 bmp_size_y as i32,
316 std::ptr::null_mut(),
317 )
318 };
319 if pack_id == ImFontAtlasRectId_Invalid {
320 return;
321 }
322 let r = unsafe {
323 let r = ImFontAtlasPackGetRect(self.atlas, pack_id);
324 &mut *r
325 };
326
327 let ref_size = unsafe { (*(&(*self.baked.ContainerFont).Sources)[0]).SizePixels };
328 let offsets_scale = if ref_size != 0.0 {
329 self.baked.Size / ref_size
330 } else {
331 1.0
332 };
333 let mut font_off_x = self.src.GlyphOffset.x * offsets_scale;
334 let mut font_off_y = self.src.GlyphOffset.y * offsets_scale;
335 if self.src.PixelSnapH {
336 font_off_x = font_off_x.round();
337 }
338 if self.src.PixelSnapV {
339 font_off_y = font_off_y.round();
340 }
341
342 out_glyph.set_Codepoint(self.codepoint as u32);
343 out_glyph.AdvanceX = advance_x;
344 out_glyph.X0 = font_off_x + origin.x;
345 out_glyph.Y0 = font_off_y + origin.y;
346 out_glyph.X1 = out_glyph.X0 + size.x;
347 out_glyph.Y1 = out_glyph.Y0 + size.y;
348 out_glyph.set_Visible(1);
349 out_glyph.PackId = pack_id;
350
351 let mut pixels =
352 image::ImageBuffer::<image::Rgba<u8>, Vec<u8>>::new(bmp_size_x, bmp_size_y)
353 .into_raw();
354 let mut image = PixelImage::from_raw(bmp_size_x, bmp_size_y, &mut pixels).unwrap();
355 draw(&mut image.sub_image(0, 0, bmp_size_x, bmp_size_y));
356 unsafe {
357 ImFontAtlasBakedSetFontGlyphBitmap(
358 self.atlas,
359 self.baked,
360 self.src,
361 out_glyph,
362 r,
363 pixels.as_ptr(),
364 ImTextureFormat::ImTextureFormat_RGBA32,
365 4 * bmp_size_x as i32,
366 );
367 }
368 }
369 }
370 *self.result = true;
371 }
372}