pub mod loader;
pub mod prelude {
pub use super::{
BevyBackend, BorrowedGlyph, FontSampler,
loader::{RasterFontAssetLoaderPlugin, RasterFontLoaderSettings},
};
pub use crate::tree::InputResolver;
pub type RasterFont = crate::backend::RasterFont<BevyBackend>;
}
use std::error::Error;
use bevy_asset::{LoadContext, VisitAssetDependencies, prelude::*};
use bevy_ecs::{component::Component, resource::Resource};
use bevy_image::{Image, ImageSampler, TextureAtlasLayout};
use bevy_reflect::{Reflect, TypePath};
use serde::{Deserialize, Serialize};
use crate::{
backend::{bevy_backend::loader::RasterFontLoaderSettings, prelude::*},
core::UGlyphRegion,
};
#[derive(Clone, Copy, Debug, Default, Reflect, Serialize, Deserialize)]
pub struct BevyBackend;
impl Backend for BevyBackend {
type Resources = BevyAtlasResources;
}
impl<B> Asset for RasterFont<B>
where
B: Backend + Clone + std::fmt::Debug + Send + Sync + TypePath,
<B as Backend>::Resources: Clone + std::fmt::Debug + Send + Sync + VisitAssetDependencies,
{
}
impl<B: Backend<Resources: VisitAssetDependencies>> VisitAssetDependencies for RasterFont<B> {
fn visit_dependencies(&self, visit: &mut impl FnMut(bevy_asset::UntypedAssetId)) {
self.resources.visit_dependencies(visit);
}
}
#[derive(Clone, Debug, Asset, TypePath)] pub struct BevyAtlasResources {
pub sampler: FontSampler,
pub image: Handle<Image>,
pub offsets: Vec<IGlyphOffset>,
pub regions: Handle<TextureAtlasLayout>,
}
pub struct BuildBevyFont<'a, 'b> {
pub load_context: &'a mut LoadContext<'b>,
pub settings: &'a RasterFontLoaderSettings,
}
impl BackendBuilder for BuildBevyFont<'_, '_> {
type Backend = BevyBackend;
type Error = std::convert::Infallible;
type Sheet = Handle<Image>;
fn build_resources(
self,
raw_font: RawFont<Handle<Image>>,
) -> Result<<Self::Backend as Backend>::Resources, Self::Error> {
let size = raw_font.sheet.size;
let image = raw_font.sheet.image;
let (textures, offsets) = raw_font
.glyphs
.into_iter()
.map(|UTokenProps { region, offset }| (region, offset))
.unzip();
let layout = TextureAtlasLayout { textures, size };
let regions = self
.load_context
.labeled_asset_scope::<TextureAtlasLayout, Self::Error>(
"raster_font::texture_atlas_layout".to_string(),
|_scope| Ok(layout),
)?;
Ok(BevyAtlasResources {
image,
offsets,
regions,
sampler: FontSampler(self.settings.sampler.clone()),
})
}
}
pub struct BevyAtlas<'f, 'r> {
regions: &'r TextureAtlasLayout,
offsets: &'f [IGlyphOffset],
}
#[derive(Clone, Copy, Debug)]
pub struct BorrowedGlyph<'r, 'o> {
pub region: &'r UGlyphRegion,
pub offset: &'o IGlyphOffset,
}
impl<'f, 'r> SpriteSheet for BevyAtlas<'f, 'r> {
type Props = Option<BorrowedGlyph<'r, 'f>>;
fn props(&self, index: &AtlasIndex) -> Self::Props {
let region = self.regions.textures.get(index.0)?;
let offset = self.offsets.get(index.0)?;
Some(BorrowedGlyph { region, offset })
}
}
#[derive(Debug, Clone)]
pub struct TextureAtlasLayoutNotFoundError(pub Handle<TextureAtlasLayout>);
impl Error for TextureAtlasLayoutNotFoundError {}
impl std::fmt::Display for TextureAtlasLayoutNotFoundError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
concat![
"Failed to upgrade a Bevy raster font because its texture atlas layout was not found.",
" This likely means the font asset was not loaded correctly, or",
" there is a mismatch between the font and the atlas resources being borrowed."
]
)
}
}
impl FontResourceProvider for Assets<TextureAtlasLayout> {
type Backend = BevyBackend;
type Error = TextureAtlasLayoutNotFoundError;
type Output<'f, 'r> = BevyAtlas<'f, 'r>;
fn upgrade_font<'f, 'r>(
&'r self,
res_in: &'f <Self::Backend as Backend>::Resources,
) -> Result<Self::Output<'f, 'r>, Self::Error> {
let regions = self
.get(&res_in.regions)
.ok_or_else(|| TextureAtlasLayoutNotFoundError(res_in.regions.clone()))?;
let offsets = &res_in.offsets;
Ok(BevyAtlas { regions, offsets })
}
}
#[derive(TypePath, Component, Resource, Clone, Debug, Deserialize, Serialize)]
pub struct FontSampler(pub ImageSampler);
impl Default for FontSampler {
#[inline]
fn default() -> Self {
FontSampler(ImageSampler::nearest())
}
}