egui_extras/
image.rs
1#![allow(deprecated)]
2
3use egui::{mutex::Mutex, TextureOptions};
4
5#[cfg(feature = "svg")]
6use egui::SizeHint;
7
8#[deprecated = "consider using `egui::Image` instead"]
16pub struct RetainedImage {
17 debug_name: String,
18
19 size: [usize; 2],
20
21 image: Mutex<egui::ColorImage>,
23
24 texture: Mutex<Option<egui::TextureHandle>>,
26
27 options: TextureOptions,
28}
29
30impl RetainedImage {
31 pub fn from_color_image(debug_name: impl Into<String>, image: egui::ColorImage) -> Self {
32 Self {
33 debug_name: debug_name.into(),
34 size: image.size,
35 image: Mutex::new(image),
36 texture: Default::default(),
37 options: Default::default(),
38 }
39 }
40
41 #[cfg(feature = "image")]
51 pub fn from_image_bytes(
52 debug_name: impl Into<String>,
53 image_bytes: &[u8],
54 ) -> Result<Self, String> {
55 Ok(Self::from_color_image(
56 debug_name,
57 load_image_bytes(image_bytes).map_err(|err| err.to_string())?,
58 ))
59 }
60
61 #[cfg(feature = "svg")]
66 pub fn from_svg_bytes(debug_name: impl Into<String>, svg_bytes: &[u8]) -> Result<Self, String> {
67 Self::from_svg_bytes_with_size(debug_name, svg_bytes, None)
68 }
69
70 #[cfg(feature = "svg")]
75 pub fn from_svg_str(debug_name: impl Into<String>, svg_str: &str) -> Result<Self, String> {
76 Self::from_svg_bytes(debug_name, svg_str.as_bytes())
77 }
78
79 #[cfg(feature = "svg")]
85 pub fn from_svg_bytes_with_size(
86 debug_name: impl Into<String>,
87 svg_bytes: &[u8],
88 size_hint: Option<SizeHint>,
89 ) -> Result<Self, String> {
90 Ok(Self::from_color_image(
91 debug_name,
92 load_svg_bytes_with_size(svg_bytes, size_hint)?,
93 ))
94 }
95
96 #[inline]
116 pub fn with_options(mut self, options: TextureOptions) -> Self {
117 self.options = options;
118
119 *self.texture.lock() = None;
122
123 self
124 }
125
126 pub fn size(&self) -> [usize; 2] {
128 self.size
129 }
130
131 pub fn width(&self) -> usize {
133 self.size[0]
134 }
135
136 pub fn height(&self) -> usize {
138 self.size[1]
139 }
140
141 pub fn size_vec2(&self) -> egui::Vec2 {
143 let [w, h] = self.size();
144 egui::vec2(w as f32, h as f32)
145 }
146
147 pub fn debug_name(&self) -> &str {
149 &self.debug_name
150 }
151
152 pub fn texture_id(&self, ctx: &egui::Context) -> egui::TextureId {
154 self.texture
155 .lock()
156 .get_or_insert_with(|| {
157 let image: &mut egui::ColorImage = &mut self.image.lock();
158 let image = std::mem::take(image);
159 ctx.load_texture(&self.debug_name, image, self.options)
160 })
161 .id()
162 }
163
164 pub fn show_max_size(&self, ui: &mut egui::Ui, max_size: egui::Vec2) -> egui::Response {
166 let mut desired_size = self.size_vec2();
167 desired_size *= (max_size.x / desired_size.x).min(1.0);
168 desired_size *= (max_size.y / desired_size.y).min(1.0);
169 self.show_size(ui, desired_size)
170 }
171
172 pub fn show(&self, ui: &mut egui::Ui) -> egui::Response {
174 self.show_size(ui, self.size_vec2())
175 }
176
177 pub fn show_scaled(&self, ui: &mut egui::Ui, scale: f32) -> egui::Response {
179 self.show_size(ui, self.size_vec2() * scale)
180 }
181
182 pub fn show_size(&self, ui: &mut egui::Ui, desired_size: egui::Vec2) -> egui::Response {
184 ui.image((self.texture_id(ui.ctx()), desired_size))
188 }
189}
190
191#[cfg(feature = "image")]
201pub fn load_image_bytes(image_bytes: &[u8]) -> Result<egui::ColorImage, egui::load::LoadError> {
202 profiling::function_scope!();
203 let image = image::load_from_memory(image_bytes).map_err(|err| match err {
204 image::ImageError::Unsupported(err) => match err.kind() {
205 image::error::UnsupportedErrorKind::Format(format) => {
206 egui::load::LoadError::FormatNotSupported {
207 detected_format: Some(format.to_string()),
208 }
209 }
210 _ => egui::load::LoadError::Loading(err.to_string()),
211 },
212 err => egui::load::LoadError::Loading(err.to_string()),
213 })?;
214 let size = [image.width() as _, image.height() as _];
215 let image_buffer = image.to_rgba8();
216 let pixels = image_buffer.as_flat_samples();
217 Ok(egui::ColorImage::from_rgba_unmultiplied(
218 size,
219 pixels.as_slice(),
220 ))
221}
222
223#[cfg(feature = "svg")]
230pub fn load_svg_bytes(svg_bytes: &[u8]) -> Result<egui::ColorImage, String> {
231 load_svg_bytes_with_size(svg_bytes, None)
232}
233
234#[cfg(feature = "svg")]
241pub fn load_svg_bytes_with_size(
242 svg_bytes: &[u8],
243 size_hint: Option<SizeHint>,
244) -> Result<egui::ColorImage, String> {
245 use resvg::tiny_skia::{IntSize, Pixmap};
246 use resvg::usvg::{Options, Tree, TreeParsing};
247
248 profiling::function_scope!();
249
250 let opt = Options::default();
251
252 let mut rtree = Tree::from_data(svg_bytes, &opt).map_err(|err| err.to_string())?;
253
254 let mut size = rtree.size.to_int_size();
255 match size_hint {
256 None => (),
257 Some(SizeHint::Size(w, h)) => {
258 size = size.scale_to(
259 IntSize::from_wh(w, h).ok_or_else(|| format!("Failed to scale SVG to {w}x{h}"))?,
260 );
261 }
262 Some(SizeHint::Height(h)) => {
263 size = size
264 .scale_to_height(h)
265 .ok_or_else(|| format!("Failed to scale SVG to height {h}"))?;
266 }
267 Some(SizeHint::Width(w)) => {
268 size = size
269 .scale_to_width(w)
270 .ok_or_else(|| format!("Failed to scale SVG to width {w}"))?;
271 }
272 Some(SizeHint::Scale(z)) => {
273 let z_inner = z.into_inner();
274 size = size
275 .scale_by(z_inner)
276 .ok_or_else(|| format!("Failed to scale SVG by {z_inner}"))?;
277 }
278 };
279 let (w, h) = (size.width(), size.height());
280
281 let mut pixmap =
282 Pixmap::new(w, h).ok_or_else(|| format!("Failed to create SVG Pixmap of size {w}x{h}"))?;
283
284 rtree.size = size.to_size();
285 resvg::Tree::from_usvg(&rtree).render(Default::default(), &mut pixmap.as_mut());
286
287 let image = egui::ColorImage::from_rgba_unmultiplied([w as _, h as _], pixmap.data());
288
289 Ok(image)
290}