use {
super::{
Canonicalize, Writer,
bitmap::{BitmapAsset, BitmapSwizzle},
file_key, re_run_if_changed,
},
crate::{
BitmapFontId, BlobId,
bitmap::{Bitmap, BitmapColor, BitmapFormat},
bitmap_font::BitmapFont,
},
bmfont::{BMFont, OrdinateOrientation},
log::info,
parking_lot::Mutex,
serde::Deserialize,
std::{
fs::File,
fs::read_to_string,
io::{Cursor, Read},
path::{Path, PathBuf},
sync::Arc,
},
};
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
pub struct BlobAsset {
src: Option<PathBuf>,
}
impl BlobAsset {
pub fn new(src: impl AsRef<Path>) -> Self {
let src = src.as_ref().to_path_buf();
Self { src: Some(src) }
}
pub fn bake(
&self,
writer: &Arc<Mutex<Writer>>,
project_dir: impl AsRef<Path>,
) -> anyhow::Result<BlobId> {
let Some(src) = self.src() else {
return Err(anyhow::Error::msg("unspecified bitmap source"));
};
let asset = self.clone().into();
if let Some(id) = writer.lock().ctx.get(&asset) {
return Ok(id.as_blob().unwrap());
}
let key = file_key(&project_dir, src);
info!("Baking blob: {}", key);
re_run_if_changed(src);
let mut file = File::open(src).unwrap();
let mut value = vec![];
file.read_to_end(&mut value).unwrap();
let mut writer = writer.lock();
if let Some(id) = writer.ctx.get(&asset) {
return Ok(id.as_blob().unwrap());
}
let id = writer.push_blob(value, Some(key));
writer.ctx.insert(asset, id.into());
Ok(id)
}
pub(super) fn bake_bitmap_font(
&self,
writer: &Arc<Mutex<Writer>>,
project_dir: impl AsRef<Path>,
path: impl AsRef<Path>,
) -> anyhow::Result<BitmapFontId> {
let Some(src) = self.src() else {
return Err(anyhow::Error::msg("unspecified bitmap source"));
};
let asset = self.clone().into();
if let Some(id) = writer.lock().ctx.get(&asset) {
return Ok(id.as_bitmap_font().unwrap());
}
let key = file_key(&project_dir, &path);
info!("Baking bitmap font: {}", key);
re_run_if_changed(src);
let def_parent = src.parent().unwrap();
let def_file = read_to_string(src).unwrap();
let def = BMFont::new(Cursor::new(&def_file), OrdinateOrientation::TopToBottom).unwrap();
let pages = def
.pages()
.flat_map(|page| {
let path = def_parent.join(page);
BitmapAsset::read_pixels(path, Some(BitmapSwizzle::RGBA), None)
})
.map(|(_, width, pixels)| {
let mut better_pixels = Vec::with_capacity(pixels.len());
for y in 0..pixels.len() / 4 / width as usize {
for x in 0..width as usize {
let g = pixels[y * width as usize * 4 + x * 4 + 1];
let r = pixels[y * width as usize * 4 + x * 4 + 3];
if 0xff == r {
better_pixels.push(0xff);
better_pixels.push(0x00);
} else if 0xff == g {
better_pixels.push(0x00);
better_pixels.push(0xff);
} else {
better_pixels.push(0x00);
better_pixels.push(0x00);
}
better_pixels.push(0x00);
}
}
(width, better_pixels)
})
.collect::<Vec<_>>();
let mut page_size = None;
for (page_width, page_pixels) in &pages {
let page_height = page_pixels.len() as u32 / 3 / page_width;
if page_size.is_none() {
page_size = Some((*page_width, page_height));
} else if let Some((width, height)) = page_size
&& (*page_width != width || page_height != height)
{
panic!("Unexpected page size");
}
}
let (width, _) = page_size.unwrap();
let page_bufs = pages
.into_iter()
.map(|(_, pixels)| {
Bitmap::new(BitmapColor::Linear, BitmapFormat::Rgb, width, 1, pixels)
})
.collect();
let mut writer = writer.lock();
if let Some(id) = writer.ctx.get(&asset) {
return Ok(id.as_bitmap_font().unwrap());
}
let id = writer.push_bitmap_font(BitmapFont::new(def_file, page_bufs), Some(key));
writer.ctx.insert(asset, id.into());
Ok(id)
}
pub fn set_src(&mut self, src: impl AsRef<Path>) {
self.src = Some(src.as_ref().to_path_buf());
}
pub fn src(&self) -> Option<&Path> {
self.src.as_deref()
}
}
impl Canonicalize for BlobAsset {
fn canonicalize(&mut self, project_dir: impl AsRef<Path>, src_dir: impl AsRef<Path>) {
if let Some(src) = self.src() {
self.src = Some(Self::canonicalize_project_path(project_dir, src_dir, src));
}
}
}