egui_resources/
lib.rs

1#![doc(html_root_url = "https://docs.rs/egui-resources/0.4.0")]
2//! egui resources
3//!
4//! https://github.com/google/fonts/blob/main/ofl/firasans/FiraSans-Regular.ttf
5//!
6
7use 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
14/// create DynamicImage from ColorImage
15/// - src: &ColorImage
16/// - result: DynamicImage
17pub 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; // pointer
21  let s = std::slice::from_raw_parts(p, sw * sh * 4); // 4 for Color32
22  DynamicImage::from(
23    // RgbaImage is an alias of ImageBuffer
24    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
32/// create resized copy from ColorImage
33/// - wh: [usize; 2] (to be resized)
34/// - src: &ColorImage
35/// - filter: image::imageops::FilterType (Nearest, Lanczos3, etc)
36/// - result: ColorImage
37pub 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  // always should use resize_to_fill for any aspect
42  color_image_from_dynamic_image(img)
43  // ColorImage::from_rgba_unmultiplied(wh, &img.into_rgba8().into_raw())
44}
45
46/// macro im_flat
47/// - img: image::DynamicImage
48/// - result: ([u8], u32, u32)
49#[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}
58// pub use im_flat;
59
60/// create ColorImage from DynamicImage
61/// - src: DynamicImage (move)
62/// - result: ColorImage
63pub 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
69/// ResourceBase
70pub struct ResourcesBase {
71  /// base path
72  pub basepath: PathBuf
73}
74
75/// ResourcesBase
76impl ResourcesBase {
77  /// constructor
78  /// - basepath: PathBuf base path (move)
79  pub fn new(basepath: PathBuf) -> Self {
80    ResourcesBase{basepath}
81  }
82
83  /// load resource img
84  /// - f: &str filename
85  /// - p: bool (true: self.basepath false: full path)
86  /// - result: ColorImage
87  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  /// load resource icon
97  /// - ico: &str filename
98  /// - p: bool (true: self.basepath false: full path)
99  /// - result: Option eframe::IconData
100  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  /// load resource font
111  /// - fonts: &amp;mut FontDefinitions
112  /// - n: &amp;str name
113  /// - f: &amp;str filename
114  /// - t: FontFamily family (move)
115  /// - p: bool (true: self.basepath false: full path)
116  /// - result: ()
117  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.font_data.insert(n, FontData::from_static(include_bytes!(
124    //   "static str path from src extended and read at the compile time")));
125    fonts.families.entry(t).or_default().insert(0, m);
126  }
127
128  /// reg fonts
129  /// - ffs: Vec&lt; (name, filename, family) &gt; (move)
130  /// - result: FontDefinitions
131  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  /// read bytes
141  /// - f: &amp;str filename
142  /// - p: bool (true: self.basepath false: full path)
143  /// - result: Result Vec u8
144  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/// tests
156#[cfg(test)]
157mod tests {
158  use super::*;
159
160  /// [-- --nocapture] [-- --show-output]
161  #[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]) // unmultiplied
171    ).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]) // unmultiplied
182    ).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}