1#![doc(html_root_url = "https://docs.rs/egui-resources/0.4.0")]
2use std::error::Error;
8use std::{fs, path::PathBuf};
9use std::io::Read;
10use image::{load_from_memory, DynamicImage, RgbaImage};
11use image::imageops::FilterType;
12use eframe::{self, egui::*};
13
14pub fn dynamic_image_from(src: &ColorImage) -> DynamicImage {
18 let (sw, sh) = (src.width(), src.height());
19unsafe {
20 let p = &src.pixels[0] as *const Color32 as *const u8; let s = std::slice::from_raw_parts(p, sw * sh * 4); DynamicImage::from(
23 match RgbaImage::from_raw(sw as u32, sh as u32, s.to_vec()) {
25 None => RgbaImage::new(sw as u32, sh as u32),
26 Some(b) => b
27 }
28 )
29}
30}
31
32pub fn resized_copy_from(wh: [usize; 2], src: &ColorImage,
38 filter: FilterType) -> ColorImage {
39 let img = dynamic_image_from(src)
40 .resize_to_fill(wh[0] as u32, wh[1] as u32, filter);
41 color_image_from_dynamic_image(img)
43 }
45
46#[macro_export]
50macro_rules! im_flat {
51 ($img: expr) => {{
52 let im = $img.into_rgba8();
53 let (width, height) = im.dimensions();
54 let rgba = im.into_raw();
55 (rgba, width, height)
56 }}
57}
58pub fn color_image_from_dynamic_image(src: DynamicImage) -> ColorImage {
64 let (rgba, width, height) = im_flat!(src);
65 ColorImage::from_rgba_unmultiplied(
66 [width as usize, height as usize], &rgba)
67}
68
69pub struct ResourcesBase {
71 pub basepath: PathBuf
73}
74
75impl ResourcesBase {
77 pub fn new(basepath: PathBuf) -> Self {
80 ResourcesBase{basepath}
81 }
82
83 pub fn resource_img(&self, f: &str, p: bool) -> ColorImage {
88 let Ok(b) = self.read_bytes(f, p) else { return ColorImage::example(); };
89 if let Ok(img) = load_from_memory(&b) {
90 color_image_from_dynamic_image(img)
91 }else{
92 ColorImage::example()
93 }
94 }
95
96 pub fn resource_icon(&self, ico: &str, p: bool) -> Option<eframe::IconData> {
101 let Ok(b) = self.read_bytes(ico, p) else { return None; };
102 if let Ok(img) = load_from_memory(&b) {
103 let (rgba, width, height) = im_flat!(img);
104 Some(eframe::IconData{rgba, width, height})
105 }else{
106 None
107 }
108 }
109
110 pub fn resource_font(&self, fonts: &mut FontDefinitions,
118 n: &str, f: &str, t: FontFamily, p: bool) {
119 let Ok(b) = self.read_bytes(f, p) else { return; };
120 let n = n.to_string();
121 let m = n.clone();
122 fonts.font_data.insert(n, FontData::from_owned(b));
123 fonts.families.entry(t).or_default().insert(0, m);
126 }
127
128 pub fn reg_fonts(&self, ffs: Vec<(&str, &str, FontFamily)>) ->
132 FontDefinitions {
133 let mut fonts = FontDefinitions::default();
134 for (n, f, t) in ffs.into_iter() {
135 self.resource_font(&mut fonts, n, f, t, !f.contains("/"));
136 }
137 fonts
138 }
139
140 pub fn read_bytes(&self, f: &str, p: bool) ->
145 Result<Vec<u8>, Box<dyn Error>> {
146 let p = if !p { PathBuf::from(f) } else { self.basepath.join(f) };
147 let mut fi = fs::File::open(&p)?;
148 let metadata = fs::metadata(&p)?;
149 let mut buf = vec![0u8; metadata.len() as usize];
150 fi.read(&mut buf)?;
151 Ok(buf)
152 }
153}
154
155#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
162 fn test_resources() {
163 let bp = ResourcesBase::new(PathBuf::from("./resources"));
164 let flat = vec![
165[255, 0, 0, 255], [255, 0, 0, 255], [0, 255, 0, 255], [0, 255, 0, 255],
166[255, 0, 0, 255], [255, 0, 0, 255], [0, 255, 0, 255], [0, 255, 0, 255],
167[0, 0, 255, 255], [0, 0, 255, 255], [255, 255, 0, 255], [255, 255, 0, 255],
168[0, 0, 255, 255], [0, 0, 255, 255], [255, 255, 0, 255], [255, 255, 0, 255]
169 ].into_iter().map(|c|
170 Color32::from_rgba_premultiplied(c[0], c[1], c[2], c[3]) ).collect::<Vec<_>>();
172 let im = bp.resource_img("_4c_4x4.png", true);
173 assert_eq!(im.size, [4, 4]);
174 assert_eq!(im.pixels.len(), 16);
175 assert_eq!(im.pixels, flat);
176
177 let resized = vec![
178 [255, 0, 0, 255], [0, 255, 0, 255],
179 [0, 0, 255, 255], [255, 255, 0, 255]
180 ].into_iter().map(|c|
181 Color32::from_rgba_premultiplied(c[0], c[1], c[2], c[3]) ).collect::<Vec<_>>();
183 let img = resized_copy_from([2, 2], &im, FilterType::Nearest);
184 assert_eq!(img.size, [2, 2]);
185 assert_eq!(img.pixels.len(), 4);
186 assert_eq!(img.pixels, resized);
187 }
188}