2024-08-16 23:24:11 +02:00
|
|
|
use std::{
|
|
|
|
cell::RefCell,
|
|
|
|
env::var,
|
|
|
|
path::PathBuf,
|
|
|
|
str::FromStr,
|
|
|
|
sync::{LazyLock, OnceLock},
|
|
|
|
thread::LocalKey,
|
|
|
|
};
|
2024-07-19 00:02:17 +02:00
|
|
|
|
|
|
|
use freetype::{Face, Library};
|
2024-08-16 23:24:11 +02:00
|
|
|
use image::{DynamicImage, RgbaImage};
|
2024-07-20 04:52:24 +02:00
|
|
|
|
2024-08-12 03:13:41 +02:00
|
|
|
use crate::{arcaea::chart::Difficulty, timed};
|
2024-07-19 00:02:17 +02:00
|
|
|
|
2024-08-16 23:24:11 +02:00
|
|
|
// {{{ Path helpers
|
|
|
|
#[inline]
|
|
|
|
pub fn get_var(name: &str) -> String {
|
|
|
|
var(name).unwrap_or_else(|_| panic!("Missing `{name}` environment variable"))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn get_path(name: &str) -> PathBuf {
|
|
|
|
PathBuf::from_str(&get_var(name))
|
|
|
|
.unwrap_or_else(|_| panic!("`{name}` environment variable is not a valid path"))
|
|
|
|
}
|
|
|
|
|
2024-07-19 00:02:17 +02:00
|
|
|
#[inline]
|
2024-07-20 04:52:24 +02:00
|
|
|
pub fn get_data_dir() -> PathBuf {
|
2024-08-16 23:24:11 +02:00
|
|
|
get_path("SHIMMERING_DATA_DIR")
|
2024-07-19 00:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2024-08-16 23:24:11 +02:00
|
|
|
pub fn get_config_dir() -> PathBuf {
|
|
|
|
get_path("SHIMMERING_CONFIG_DIR")
|
2024-07-20 04:52:24 +02:00
|
|
|
}
|
|
|
|
|
2024-08-16 23:24:11 +02:00
|
|
|
#[inline]
|
|
|
|
pub fn get_asset_dir() -> PathBuf {
|
|
|
|
get_path("SHIMMERING_ASSET_DIR")
|
|
|
|
}
|
|
|
|
// }}}
|
|
|
|
// {{{ Font helpers
|
2024-07-20 04:52:24 +02:00
|
|
|
#[inline]
|
|
|
|
fn get_font(name: &str) -> RefCell<Face> {
|
2024-08-12 03:13:41 +02:00
|
|
|
let face = timed!(format!("load font \"{name}\""), {
|
|
|
|
FREETYPE_LIB.with(|lib| {
|
2024-08-16 23:24:11 +02:00
|
|
|
lib.new_face(get_asset_dir().join("fonts").join(name), 0)
|
2024-08-12 03:13:41 +02:00
|
|
|
.expect(&format!("Could not load {} font", name))
|
|
|
|
})
|
2024-07-19 00:02:17 +02:00
|
|
|
});
|
|
|
|
RefCell::new(face)
|
|
|
|
}
|
|
|
|
|
2024-08-12 03:13:41 +02:00
|
|
|
#[inline]
|
|
|
|
pub fn with_font<T>(
|
|
|
|
primary: &'static LocalKey<RefCell<Face>>,
|
|
|
|
f: impl FnOnce(&mut [&mut Face]) -> T,
|
|
|
|
) -> T {
|
|
|
|
UNI_FONT.with_borrow_mut(|uni| {
|
|
|
|
// NOTO_SANS_FONT.with_borrow_mut(|noto| {
|
|
|
|
// ARIAL_FONT.with_borrow_mut(|arial| {
|
|
|
|
primary.with_borrow_mut(|primary| f(&mut [primary, uni]))
|
|
|
|
// })
|
|
|
|
// })
|
|
|
|
})
|
2024-07-19 00:02:17 +02:00
|
|
|
}
|
2024-08-16 23:24:11 +02:00
|
|
|
// }}}
|
|
|
|
// {{{ Font loading
|
|
|
|
thread_local! {
|
|
|
|
pub static FREETYPE_LIB: Library = Library::init().unwrap();
|
|
|
|
pub static SAIRA_FONT: RefCell<Face> = get_font("saira-variable.ttf");
|
|
|
|
pub static EXO_FONT: RefCell<Face> = get_font("exo-variable.ttf");
|
|
|
|
pub static GEOSANS_FONT: RefCell<Face> = get_font("geosans-light.ttf");
|
|
|
|
pub static KAZESAWA_FONT: RefCell<Face> = get_font("kazesawa-regular.ttf");
|
|
|
|
pub static KAZESAWA_BOLD_FONT: RefCell<Face> = get_font("kazesawa-bold.ttf");
|
|
|
|
pub static NOTO_SANS_FONT: RefCell<Face> = get_font("noto-sans.ttf");
|
|
|
|
pub static ARIAL_FONT: RefCell<Face> = get_font("arial.ttf");
|
|
|
|
pub static UNI_FONT: RefCell<Face> = get_font("unifont.otf");
|
|
|
|
}
|
|
|
|
// }}}
|
|
|
|
// {{{ Asset art helpers
|
2024-07-19 00:02:17 +02:00
|
|
|
#[inline]
|
|
|
|
pub fn should_skip_jacket_art() -> bool {
|
2024-08-16 15:38:00 +02:00
|
|
|
var("SHIMMERING_NO_JACKETS").unwrap_or_default() == "1"
|
2024-07-19 00:02:17 +02:00
|
|
|
}
|
2024-07-20 04:52:24 +02:00
|
|
|
|
2024-08-12 03:13:41 +02:00
|
|
|
#[inline]
|
|
|
|
pub fn should_blur_jacket_art() -> bool {
|
2024-08-16 15:38:00 +02:00
|
|
|
var("SHIMMERING_BLUR_JACKETS").unwrap_or_default() == "1"
|
2024-08-12 03:13:41 +02:00
|
|
|
}
|
|
|
|
|
2024-08-16 23:24:11 +02:00
|
|
|
macro_rules! get_asset {
|
|
|
|
($name: ident, $path:expr) => {
|
|
|
|
get_asset!($name, $path, |d: DynamicImage| d);
|
|
|
|
};
|
|
|
|
($name: ident, $path:expr, $f:expr) => {
|
|
|
|
pub static $name: LazyLock<RgbaImage> = LazyLock::new(move || {
|
|
|
|
timed!($path, {
|
|
|
|
let image = image::open(get_asset_dir().join($path))
|
|
|
|
.unwrap_or_else(|_| panic!("Could no read asset `{}`", $path));
|
|
|
|
let f = $f;
|
|
|
|
f(image).into_rgba8()
|
|
|
|
})
|
|
|
|
});
|
|
|
|
};
|
|
|
|
}
|
|
|
|
// }}}
|
|
|
|
// {{{ Asset art loading
|
|
|
|
get_asset!(COUNT_BACKGROUND, "count_background.png");
|
|
|
|
get_asset!(SCORE_BACKGROUND, "score_background.png");
|
|
|
|
get_asset!(STATUS_BACKGROUND, "status_background.png");
|
|
|
|
get_asset!(GRADE_BACKGROUND, "grade_background.png");
|
|
|
|
get_asset!(TOP_BACKGROUND, "top_background.png");
|
|
|
|
get_asset!(NAME_BACKGROUND, "name_background.png");
|
|
|
|
get_asset!(PTT_EMBLEM, "ptt_emblem.png");
|
|
|
|
get_asset!(
|
|
|
|
B30_BACKGROUND,
|
|
|
|
"b30_background.jpg",
|
|
|
|
|image: DynamicImage| image.blur(7.0)
|
|
|
|
);
|
|
|
|
|
|
|
|
pub fn get_difficulty_background(difficulty: Difficulty) -> &'static RgbaImage {
|
|
|
|
static CELL: OnceLock<[RgbaImage; 5]> = OnceLock::new();
|
2024-07-20 04:52:24 +02:00
|
|
|
&CELL.get_or_init(|| {
|
2024-08-16 15:38:00 +02:00
|
|
|
timed!("load_difficulty_background", {
|
2024-08-16 23:24:11 +02:00
|
|
|
let assets_dir = get_asset_dir();
|
2024-08-16 15:38:00 +02:00
|
|
|
Difficulty::DIFFICULTY_SHORTHANDS.map(|shorthand| {
|
|
|
|
image::open(assets_dir.join(format!("diff_{}.png", shorthand.to_lowercase())))
|
2024-08-16 23:24:11 +02:00
|
|
|
.unwrap_or_else(|_| {
|
|
|
|
panic!("Could not get background for difficulty {shorthand:?}")
|
|
|
|
})
|
2024-08-16 15:38:00 +02:00
|
|
|
.into_rgba8()
|
|
|
|
})
|
2024-07-20 04:52:24 +02:00
|
|
|
})
|
|
|
|
})[difficulty.to_index()]
|
|
|
|
}
|
2024-08-16 23:24:11 +02:00
|
|
|
// }}}
|