cotis_raylib/raylib_images.rs
1//! Image loading traits and texture cache integration for [`crate::renderer::RaylibRender`].
2//!
3//! Path-based images from `cotis-defaults` implement [`RaylibLoadableImage`] and
4//! [`GenericCompatibleImage`]. Custom [`Image`](cotis_defaults::render_commands::Image) element data
5//! can integrate by implementing these traits and using [`RaylibRender::translate_generic_image`] to
6//! populate the renderer cache.
7//!
8//! # Custom image extension
9//!
10//! ```rust,ignore
11//! use std::sync::Arc;
12//! use cotis_raylib::drawables::RaylibDrawContext;
13//! use cotis_raylib::raylib_images::{GenericCompatibleImage, RaylibLoadableImage};
14//! use cotis_raylib::renderer::RaylibRender;
15//! use raylib::prelude::Texture2D;
16//!
17//! struct MyImage { path: String }
18//!
19//! impl RaylibLoadableImage for MyImage {
20//! fn get_filename(&self) -> &str { &self.path }
21//! }
22//!
23//! impl GenericCompatibleImage for MyImage {
24//! fn load_texture(&self, _ctx: &mut RaylibDrawContext<'_, '_>) {}
25//! fn get_texture(&self, ctx: &RaylibDrawContext<'_, '_>) -> Arc<Texture2D> {
26//! ctx.image_cache.expect("cache required").get(&self.path).unwrap().clone()
27//! }
28//! }
29//!
30//! // Preload before drawing:
31//! // render.translate_generic_image(&my_image);
32//! ```
33
34use crate::drawables::RaylibDrawContext;
35use crate::renderer::RaylibRender;
36use raylib::consts::TextureFilter;
37use raylib::prelude::{RaylibTexture2D, Texture2D};
38use std::fmt::{Debug, Formatter};
39use std::ops::Deref;
40use std::sync::Arc;
41
42/// Path-based image data that can be loaded into the renderer texture cache.
43pub trait RaylibLoadableImage {
44 /// Filesystem path used as the cache key and passed to raylib's texture loader.
45 fn get_filename(&self) -> &str;
46}
47
48impl RaylibRender {
49 /// Loads a texture from a path-based image, caching the result by file path.
50 ///
51 /// On cache hit, returns the existing texture. On miss, loads via raylib with
52 /// [`TextureFilter::TEXTURE_FILTER_POINT`] filtering.
53 ///
54 /// # Panics
55 ///
56 /// Panics if raylib cannot load the file at the given path.
57 pub fn translate_generic_image(
58 &mut self,
59 image: &dyn RaylibLoadableImage,
60 ) -> Box<dyn Texture2DHolder> {
61 let path = image.get_filename();
62 if self.image_cache().contains_key(path) {
63 return Box::new(self.image_cache().get(path).unwrap().clone());
64 }
65 let texture: Arc<Texture2D> = {
66 let mut rl = self.lock_handle();
67 let res = rl.load_texture(self.thread(), path);
68 match res {
69 Ok(tex) => {
70 tex.set_texture_filter(self.thread(), TextureFilter::TEXTURE_FILTER_POINT);
71 tex.into()
72 }
73 Err(_) => panic!("[Raylib Error]: Failed to load path-based image from: {path}"),
74 }
75 };
76 self.image_cache_mut().insert(path.to_owned(), texture);
77 Box::new(self.image_cache().get(path).unwrap().clone())
78 }
79}
80
81/// Abstraction over owned and borrowed raylib textures.
82pub trait Texture2DHolder {
83 /// Returns the underlying raylib texture.
84 fn texture(&self) -> &Texture2D;
85}
86
87impl Texture2DHolder for Texture2D {
88 fn texture(&self) -> &Texture2D {
89 self
90 }
91}
92impl<T: Texture2DHolder> Texture2DHolder for &T {
93 fn texture(&self) -> &Texture2D {
94 (*self).texture()
95 }
96}
97
98impl Deref for dyn Texture2DHolder {
99 type Target = Texture2D;
100
101 fn deref(&self) -> &Self::Target {
102 self.texture()
103 }
104}
105
106impl Debug for dyn Texture2DHolder {
107 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
108 f.write_str("dyn Texture2DHolder")
109 }
110}
111
112/// Two-phase image draw: optional per-frame setup, then texture lookup from cache.
113pub trait GenericCompatibleImage: RaylibLoadableImage {
114 /// Called before [`Self::get_texture`] during drawing (no-op for path-based types).
115 fn load_texture(&self, ctx: &mut RaylibDrawContext<'_, '_>);
116
117 /// Returns the texture to draw. Path-based types read from [`RaylibDrawContext::image_cache`].
118 ///
119 /// # Panics
120 ///
121 /// Panics if `image_cache` is `None` or the path was not preloaded.
122 fn get_texture(&self, ctx: &RaylibDrawContext<'_, '_>) -> Arc<Texture2D>;
123}