use std::error::Error;
use serde::{Deserialize, Serialize};
use bevy_app::prelude::*;
use bevy_asset::{AssetLoader, AssetPath, prelude::*};
use bevy_image::{Image, ImageSampler};
use bevy_reflect::prelude::*;
use crate::{
backend::bevy_backend::{
BuildBevyFont, loader::error::RasterFontAssetLoaderError, prelude::RasterFont,
},
builder::{FontAtlasBuilder, GlyphSheet, errors::FontBuilderError},
meta::FontMeta,
};
#[derive(Default, TypePath)]
#[type_path = "raster_font::RasterFontAssetLoaderPlugin"]
pub struct RasterFontAssetLoaderPlugin;
impl Plugin for RasterFontAssetLoaderPlugin {
fn build(&self, app: &mut App) {
app.init_asset::<RasterFont>()
.init_asset_loader::<RasterFontAssetLoader>();
}
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct RasterFontLoaderSettings {
pub sampler: ImageSampler,
}
impl Default for RasterFontLoaderSettings {
fn default() -> Self {
RasterFontLoaderSettings {
sampler: ImageSampler::nearest(),
}
}
}
#[derive(Default, TypePath)]
pub struct RasterFontAssetLoader;
impl AssetLoader for RasterFontAssetLoader {
type Asset = RasterFont;
type Settings = RasterFontLoaderSettings;
type Error = RasterFontAssetLoaderError;
async fn load(
&self,
reader: &mut dyn bevy_asset::io::Reader,
settings: &Self::Settings,
load_context: &mut bevy_asset::LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
let meta = toml::de::from_slice::<FontMeta>(&bytes)?;
let glyph_sheet = {
let path = load_context
.path()
.parent()
.map(|p| AssetPath::from_path_buf(p.path().join(&meta.image)))
.unwrap_or(AssetPath::from_path_buf(meta.image));
let texture_load = {
let nested = load_context.loader().immediate().with_static_type();
nested.load::<Image>(path).await?
};
GlyphSheet {
size: texture_load.get().size(),
image: load_context.add_loaded_labeled_asset("texture", texture_load),
}
};
FontAtlasBuilder::from(meta.layout.unique())
.with_name(meta.name)
.with_image(glyph_sheet)
.populate_layout(&meta.layout)
.custom_glyphs(meta.layout.custom())?
.build(BuildBevyFont {
load_context,
settings,
})
.map_err(RasterFontAssetLoaderError::FontBuilderError)
}
fn extensions(&self) -> &[&str] {
&["font.toml", "toml"]
}
}
pub mod error {
use super::*;
#[derive(Debug)]
pub enum RasterFontAssetLoaderError {
Io(std::io::Error),
TomlError(toml::de::Error),
LoadDirectError(bevy_asset::LoadDirectError),
FontBuilderError(FontBuilderError<std::convert::Infallible>),
}
impl Error for RasterFontAssetLoaderError {}
impl std::fmt::Display for RasterFontAssetLoaderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RasterFontAssetLoaderError::Io(e) => write!(f, "Could not load asset: {e}"),
RasterFontAssetLoaderError::TomlError(e) => write!(f, "Could not parse TOML: {e}"),
RasterFontAssetLoaderError::LoadDirectError(e) => {
write!(f, "Could not immediately load nested asset: {e}")
}
RasterFontAssetLoaderError::FontBuilderError(e) => {
write!(f, "Raster Font builder error: {e}")
}
}
}
}
impl From<std::io::Error> for RasterFontAssetLoaderError {
fn from(err: std::io::Error) -> Self {
RasterFontAssetLoaderError::Io(err)
}
}
impl From<toml::de::Error> for RasterFontAssetLoaderError {
fn from(err: toml::de::Error) -> Self {
RasterFontAssetLoaderError::TomlError(err)
}
}
impl From<bevy_asset::LoadDirectError> for RasterFontAssetLoaderError {
fn from(err: bevy_asset::LoadDirectError) -> Self {
RasterFontAssetLoaderError::LoadDirectError(err)
}
}
impl<E: Into<FontBuilderError<std::convert::Infallible>>> From<E> for RasterFontAssetLoaderError {
fn from(err: E) -> Self {
RasterFontAssetLoaderError::FontBuilderError(err.into())
}
}
}