Skip to main content

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}