swamp_font/
lib.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/swamp
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use bmf_parser::BMFont;
6use int_math::{URect, UVec2, Vec2};
7use limnus_app::prelude::{App, Plugin};
8use limnus_asset_registry::AssetRegistry;
9use limnus_assets::prelude::{Asset, AssetName, Id, RawWeakId, WeakId};
10use limnus_assets::Assets;
11use limnus_assets_loader::{
12    AssetLoader, ConversionError, ResourceStorage, WrappedAssetLoaderRegistry,
13};
14use limnus_local_resource::LocalResourceStorage;
15use tracing::debug;
16
17pub type FontRef = Id<Font>;
18pub type WeakFontRef = WeakId<Font>;
19
20#[derive(Debug, Asset)]
21pub struct Font {
22    font: BMFont,
23}
24
25pub struct FontPlugin;
26
27impl Plugin for FontPlugin {
28    fn build(&self, app: &mut App) {
29        {
30            let registry = app.resource_mut::<WrappedAssetLoaderRegistry>();
31            let loader = FontConverter::new();
32
33            registry.value.lock().unwrap().register_loader(loader);
34        }
35
36        app.insert_resource(Assets::<Font>::default());
37    }
38}
39
40#[derive(Default)]
41pub struct FontConverter;
42
43impl FontConverter {
44    #[must_use]
45    pub const fn new() -> Self {
46        Self {}
47    }
48}
49
50impl AssetLoader for FontConverter {
51    type AssetType = Font;
52
53    fn convert_and_insert(
54        &self,
55        id: RawWeakId,
56        octets: &[u8],
57        resources: &mut ResourceStorage,
58        _local_resources: &mut LocalResourceStorage,
59    ) -> Result<(), ConversionError> {
60        let name: AssetName;
61        {
62            let asset_container = resources.fetch::<AssetRegistry>();
63            name = asset_container
64                .name_raw(id)
65                .expect("should know about this Id");
66        }
67
68        debug!("convert from fnt {name}");
69        let font = BMFont::from_octets(octets)?;
70
71        debug!("font complete {name}");
72        let font_assets = resources.fetch_mut::<Assets<Font>>();
73
74        font_assets.set_raw(id, Font { font });
75
76        Ok(())
77    }
78}
79
80#[derive(Debug)]
81pub struct Glyph {
82    pub relative_position: Vec2,
83    pub texture_rectangle: URect,
84}
85
86impl Font {
87    /// # Panics
88    ///
89    #[must_use]
90    pub fn from_octets(bm_contents: &[u8]) -> Self {
91        let font = BMFont::from_octets(bm_contents).unwrap();
92        Self { font }
93    }
94
95    /// # Panics
96    ///
97    #[must_use]
98    pub fn draw(&self, text: &str) -> Vec<Glyph> {
99        let mut x = 0;
100        let y = 0;
101        let common = self.font.common.as_ref().unwrap();
102        let height = self.font.info.as_ref().unwrap().font_size;
103        let mut glyphs = Vec::new();
104        let factor = 1u16;
105        let y_offset = -(common.line_height as i16 - common.base as i16);
106        for ch in text.chars() {
107            if let Some(bm_char) = self.font.chars.get(&(ch as u32)) {
108                let cx = x + bm_char.x_offset * factor as i16;
109                let cy = y + y_offset + height - (bm_char.height as i16) - bm_char.y_offset;
110
111                let glyph = Glyph {
112                    relative_position: Vec2 { x: cx, y: cy },
113                    texture_rectangle: URect {
114                        position: UVec2 {
115                            x: bm_char.x,
116                            y: bm_char.y,
117                        },
118                        size: UVec2 {
119                            x: bm_char.width,
120                            y: bm_char.height,
121                        },
122                    },
123                };
124                x += bm_char.x_advance * factor as i16;
125
126                glyphs.push(glyph);
127            }
128        }
129
130        glyphs
131    }
132}