use std::{
fs::File,
io::Read,
path::{Path, PathBuf},
sync::{Arc, LazyLock},
};
use image::load_from_memory;
use parley::{GenericFamily, fontique::FontInfoOverride};
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use takumi::{
GlobalContext,
layout::{Viewport, node::NodeKind},
rendering::{
AnimationFrame, ImageOutputFormat, RenderOptionsBuilder, encode_animated_png,
encode_animated_webp, render, write_image,
},
resources::image::{ImageSource, parse_svg_str},
};
fn assets_path(path: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../assets/")
.join(path)
.to_path_buf()
}
const TEST_FONTS: &[(&str, &str, GenericFamily)] = &[
(
"fonts/geist/Geist[wght].woff2",
"Geist",
GenericFamily::SansSerif,
),
(
"fonts/geist/GeistMono[wght].woff2",
"Geist Mono",
GenericFamily::Monospace,
),
(
"fonts/twemoji/TwemojiMozilla-colr.woff2",
"Twemoji Mozilla",
GenericFamily::Emoji,
),
(
"fonts/archivo/Archivo-VariableFont_wdth,wght.ttf",
"Archivo",
GenericFamily::SansSerif,
),
(
"fonts/sil/scheherazade-new-v17-arabic-regular.woff2",
"Scheherazade New Test",
GenericFamily::Serif,
),
];
fn create_test_context() -> GlobalContext {
let mut context = GlobalContext::default();
let mut yeecord_image_data = Vec::new();
File::open(assets_path("images/yeecord.png"))
.unwrap()
.read_to_end(&mut yeecord_image_data)
.unwrap();
let mut luma_image_data = String::new();
File::open(assets_path("images/luma.svg"))
.unwrap()
.read_to_string(&mut luma_image_data)
.unwrap();
context.persistent_image_store.insert(
"assets/images/yeecord.png".to_string(),
Arc::new(ImageSource::Bitmap(
load_from_memory(&yeecord_image_data).unwrap().into_rgba8(),
)),
);
let mut luma_cover_image_data = Vec::new();
File::open(assets_path(
"images/luma-cover-0dfbf65d-0f58-4941-947c-d84a5b131dc0.jpeg",
))
.unwrap()
.read_to_end(&mut luma_cover_image_data)
.unwrap();
context.persistent_image_store.insert(
"assets/images/luma.svg".to_string(),
parse_svg_str(&luma_image_data).unwrap(),
);
context.persistent_image_store.insert(
"assets/images/luma-cover-0dfbf65d-0f58-4941-947c-d84a5b131dc0.jpeg".to_string(),
Arc::new(ImageSource::Bitmap(
load_from_memory(&luma_cover_image_data)
.unwrap()
.into_rgba8(),
)),
);
for (font, name, generic) in TEST_FONTS {
let mut font_data = Vec::new();
File::open(assets_path(font))
.unwrap()
.read_to_end(&mut font_data)
.unwrap();
context
.font_context
.load_and_store(
font_data.into(),
Some(FontInfoOverride {
family_name: Some(name),
..Default::default()
}),
Some(*generic),
)
.unwrap();
}
context
}
pub fn create_test_viewport() -> Viewport {
(1200, 630).into()
}
pub static CONTEXT: LazyLock<GlobalContext> = LazyLock::new(create_test_context);
#[allow(dead_code)]
pub fn run_fixture_test(node: NodeKind, fixture_name: &str) {
let viewport = create_test_viewport();
let image = render(
RenderOptionsBuilder::default()
.viewport(viewport)
.node(node)
.global(&CONTEXT)
.build()
.unwrap(),
)
.unwrap();
let fixture_path = format!("tests/fixtures-generated/{}.webp", fixture_name);
let path = Path::new(&fixture_path);
let mut file = File::create(path).unwrap();
write_image(&image, &mut file, ImageOutputFormat::WebP, None).unwrap();
}
#[allow(dead_code)]
pub fn run_webp_animation_test(
nodes: Vec<(NodeKind, u32)>,
fixture_name: &str,
blend: bool,
dispose: bool,
loop_count: Option<u16>,
) {
assert_ne!(nodes.len(), 0);
let viewport = create_test_viewport();
let frames: Vec<_> = nodes
.into_par_iter()
.map(|(node, duration_ms)| {
AnimationFrame::new(
render(
RenderOptionsBuilder::default()
.viewport(viewport)
.node(node)
.global(&CONTEXT)
.build()
.unwrap(),
)
.unwrap(),
duration_ms,
)
})
.collect();
let fixture_path = format!("tests/fixtures-generated/{}", fixture_name);
let mut out = File::create(fixture_path).unwrap();
encode_animated_webp(&frames, &mut out, blend, dispose, loop_count).unwrap();
}
#[allow(dead_code)]
pub fn run_png_animation_test(
nodes: Vec<(NodeKind, u32)>,
fixture_name: &str,
loop_count: Option<u16>,
) {
assert_ne!(nodes.len(), 0);
let viewport = create_test_viewport();
let frames: Vec<_> = nodes
.into_par_iter()
.map(|(node, duration_ms)| {
AnimationFrame::new(
render(
RenderOptionsBuilder::default()
.viewport(viewport)
.node(node)
.global(&CONTEXT)
.build()
.unwrap(),
)
.unwrap(),
duration_ms,
)
})
.collect();
let fixture_path = format!("tests/fixtures-generated/{}", fixture_name);
let mut out = File::create(fixture_path).unwrap();
encode_animated_png(&frames, &mut out, loop_count).unwrap();
}