fennel_engine/resources/
loadable.rs1use anyhow::bail;
2use image::ImageReader;
3use sdl3::{
4 pixels::PixelFormat,
5 render::Texture
6};
7use std::{
8 cell::{Ref, RefCell},
9 path::PathBuf,
10 rc::Rc,
11};
12
13use crate::{graphics::Graphics, resources::LoadableResource};
14
15pub struct Image {
17 pub path: PathBuf,
19 pub buffer: Rc<RefCell<Vec<u8>>>,
21 pub texture: Rc<Texture<'static>>,
23 pub width: u32,
25 pub height: u32,
27}
28
29pub struct Font {
31 pub path: PathBuf,
33 pub family_name: String,
35 pub size: f32,
37 pub buffer: Rc<sdl3::ttf::Font<'static>>,
39}
40
41impl LoadableResource for Image {
42 fn load(
44 path: PathBuf,
45 graphics: &mut Graphics,
46 _size: Option<f32>
47 ) -> anyhow::Result<Box<dyn LoadableResource>> {
48 let img = ImageReader::open(&path)?.decode()?;
49 let mut buffer = img.to_rgba8().into_raw();
50 let surface = sdl3::surface::Surface::from_data(
51 &mut buffer,
52 img.width(),
53 img.height(),
54 img.width() * 4,
55 PixelFormat::RGBA32,
56 )?;
57
58 let texture = unsafe {
59 std::mem::transmute::<sdl3::render::Texture<'_>, sdl3::render::Texture<'static>>(
60 graphics.texture_creator.create_texture_from_surface(surface)?,
61 )
62 };
63
64 Ok(Box::new(Self {
65 path,
66 buffer: Rc::new(RefCell::new(buffer)),
67 texture: Rc::new(texture),
68 width: img.width(),
69 height: img.height(),
70 }))
71 }
72
73 fn name(&self) -> String {
74 self.path.to_string_lossy().to_string()
75 }
76
77 fn as_mut_slice(&self) -> Option<&mut [u8]> {
78 let mut mut_ref = self.buffer.borrow_mut();
79 unsafe { Some(&mut *(mut_ref.as_mut_slice() as *mut [u8])) }
83 }
84
85 fn as_slice(&self) -> Option<Ref<'_, [u8]>> {
86 Some(Ref::map(self.buffer.borrow(), |v| v.as_slice()))
87 }
88}
89
90impl LoadableResource for Font {
91 fn load(
92 path: PathBuf,
93 graphics: &mut Graphics,
94 size: Option<f32>
95 ) -> anyhow::Result<Box<dyn LoadableResource>>
96 where
97 Self: Sized {
98 if size.is_none() {
99 bail!("no font size was provided");
100 }
101
102 let font = graphics.ttf_context.load_font(&path, size.unwrap())?;
103 Ok(Box::new(Self {
104 path,
105 family_name: font.face_family_name().expect("failed to get font family name"),
106 size: size.unwrap(),
107 buffer: Rc::new(font)
108 }))
109 }
110
111 fn name(&self) -> String {
112 format!("{} {}", self.family_name, self.size)
113 }
114}