Skip to content

Commit 2bc6814

Browse files
jprochazkemilk
andauthored
Improved texture loading (#3315)
* rework loading around `Arc<Loaders>` * use `Bytes` instead of splitting api * remove unwraps in `texture_handle` * make `FileLoader` optional under `file` feature * hide http load error stack trace from UI * implement image fit * support more image sources * center spinner if we know size ahead of time * allocate final size for spinner * improve image format guessing * remove `ui.image`, `Image`, add `RawImage` * deprecate `RetainedImage` * `image2` -> `image` * add viewer example * update `examples/image` + remove `svg` and `download_image` exapmles * fix lints and tests * fix doc link * add image controls to `images` example * add more `From` str-like types * add api to forget all images * fix max size * do not scale original size unless necessary * fix doc link * add more docs for `Image` and `RawImage` * make paint_at `pub` * update `ImageButton` to use new `Image` API * fix double rendering * `SizeHint::Original` -> `Scale` + remove `Option` wrapper * Update crates/egui/src/load.rs Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * remove special `None` value for `forget` * Update crates/egui/src/load.rs Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * add more examples to `ui.image` + add `include_image` macro * Update crates/egui/src/ui.rs Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * update `menu_image_button` to use `ImageSource` * `OrderedFloat::get` -> `into_inner` * derive `Eq` on `SizedTexture` * add `id` to loaders + `is_installed` check * move `images` to demo + simplify `images` example * log trace when installing loaders * fix lint * fix doc link * add more documentation * more `egui_extras::loaders` docs * Update examples/images/src/main.rs Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * update `images` example screenshots + readme * remove unused `rfd` from `images` example * Update crates/egui_extras/src/loaders/ehttp_loader.rs Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * add `must_use` on `Image` and `RawImage` * document `loaders::install` multiple call safety * Update crates/egui_extras/Cargo.toml Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * reshuffle `is_loader_installed` * make `include_image` produce `ImageSource` + update docs * update `include_image` docs * remove `None` mentions from loader `forget` * inline `From` texture id + size for `SizedTexture` * add warning about statically known path * change image load error + use in image button * add `.size()` to `Image` * Update crates/egui_demo_app/Cargo.toml Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * add explanations to image viewer ui --------- Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
1 parent dbcf15b commit 2bc6814

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1556
-763
lines changed

Cargo.lock

+37-29
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/egui/assets/ferris.png

45.2 KB
Loading

crates/egui/src/context.rs

+84-43
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
use std::sync::Arc;
44

5+
use crate::load::Bytes;
6+
use crate::load::SizedTexture;
57
use crate::{
68
animation_manager::AnimationManager, data::output::PlatformOutput, frame_state::FrameState,
7-
input_state::*, layers::GraphicLayers, memory::Options, os::OperatingSystem,
9+
input_state::*, layers::GraphicLayers, load::Loaders, memory::Options, os::OperatingSystem,
810
output::FullOutput, util::IdTypeMap, TextureHandle, *,
911
};
1012
use epaint::{mutex::*, stats::*, text::Fonts, TessellationOptions, *};
@@ -167,7 +169,7 @@ struct ContextImpl {
167169
#[cfg(feature = "accesskit")]
168170
accesskit_node_classes: accesskit::NodeClassSet,
169171

170-
loaders: load::Loaders,
172+
loaders: Arc<Loaders>,
171173
}
172174

173175
impl ContextImpl {
@@ -1143,7 +1145,7 @@ impl Context {
11431145
/// });
11441146
///
11451147
/// // Show the image:
1146-
/// ui.image(texture, texture.size_vec2());
1148+
/// ui.raw_image((texture.id(), texture.size_vec2()));
11471149
/// }
11481150
/// }
11491151
/// ```
@@ -1689,14 +1691,15 @@ impl Context {
16891691
let mut size = vec2(w as f32, h as f32);
16901692
size *= (max_preview_size.x / size.x).min(1.0);
16911693
size *= (max_preview_size.y / size.y).min(1.0);
1692-
ui.image(texture_id, size).on_hover_ui(|ui| {
1693-
// show larger on hover
1694-
let max_size = 0.5 * ui.ctx().screen_rect().size();
1695-
let mut size = vec2(w as f32, h as f32);
1696-
size *= max_size.x / size.x.max(max_size.x);
1697-
size *= max_size.y / size.y.max(max_size.y);
1698-
ui.image(texture_id, size);
1699-
});
1694+
ui.raw_image(SizedTexture::new(texture_id, size))
1695+
.on_hover_ui(|ui| {
1696+
// show larger on hover
1697+
let max_size = 0.5 * ui.ctx().screen_rect().size();
1698+
let mut size = vec2(w as f32, h as f32);
1699+
size *= max_size.x / size.x.max(max_size.x);
1700+
size *= max_size.y / size.y.max(max_size.y);
1701+
ui.raw_image(SizedTexture::new(texture_id, size));
1702+
});
17001703

17011704
ui.label(format!("{w} x {h}"));
17021705
ui.label(format!("{:.3} MB", meta.bytes_used() as f64 * 1e-6));
@@ -1907,60 +1910,90 @@ impl Context {
19071910
impl Context {
19081911
/// Associate some static bytes with a `uri`.
19091912
///
1910-
/// The same `uri` may be passed to [`Ui::image2`] later to load the bytes as an image.
1911-
pub fn include_static_bytes(&self, uri: &'static str, bytes: &'static [u8]) {
1912-
self.read(|ctx| ctx.loaders.include.insert_static(uri, bytes));
1913+
/// The same `uri` may be passed to [`Ui::image`] later to load the bytes as an image.
1914+
pub fn include_bytes(&self, uri: &'static str, bytes: impl Into<Bytes>) {
1915+
self.loaders().include.insert(uri, bytes.into());
19131916
}
19141917

1915-
/// Associate some bytes with a `uri`.
1916-
///
1917-
/// The same `uri` may be passed to [`Ui::image2`] later to load the bytes as an image.
1918-
pub fn include_bytes(&self, uri: &'static str, bytes: impl Into<Arc<[u8]>>) {
1919-
self.read(|ctx| ctx.loaders.include.insert_shared(uri, bytes));
1918+
/// Returns `true` if the chain of bytes, image, or texture loaders
1919+
/// contains a loader with the given `id`.
1920+
pub fn is_loader_installed(&self, id: &str) -> bool {
1921+
let loaders = self.loaders();
1922+
1923+
let in_bytes = loaders.bytes.lock().iter().any(|loader| loader.id() == id);
1924+
let in_image = loaders.image.lock().iter().any(|loader| loader.id() == id);
1925+
let in_texture = loaders
1926+
.texture
1927+
.lock()
1928+
.iter()
1929+
.any(|loader| loader.id() == id);
1930+
1931+
in_bytes || in_image || in_texture
19201932
}
19211933

19221934
/// Append an entry onto the chain of bytes loaders.
19231935
///
19241936
/// See [`load`] for more information.
19251937
pub fn add_bytes_loader(&self, loader: Arc<dyn load::BytesLoader + Send + Sync + 'static>) {
1926-
self.write(|ctx| ctx.loaders.bytes.push(loader));
1938+
self.loaders().bytes.lock().push(loader);
19271939
}
19281940

19291941
/// Append an entry onto the chain of image loaders.
19301942
///
19311943
/// See [`load`] for more information.
19321944
pub fn add_image_loader(&self, loader: Arc<dyn load::ImageLoader + Send + Sync + 'static>) {
1933-
self.write(|ctx| ctx.loaders.image.push(loader));
1945+
self.loaders().image.lock().push(loader);
19341946
}
19351947

19361948
/// Append an entry onto the chain of texture loaders.
19371949
///
19381950
/// See [`load`] for more information.
19391951
pub fn add_texture_loader(&self, loader: Arc<dyn load::TextureLoader + Send + Sync + 'static>) {
1940-
self.write(|ctx| ctx.loaders.texture.push(loader));
1952+
self.loaders().texture.lock().push(loader);
19411953
}
19421954

19431955
/// Release all memory and textures related to the given image URI.
19441956
///
19451957
/// If you attempt to load the image again, it will be reloaded from scratch.
19461958
pub fn forget_image(&self, uri: &str) {
1947-
self.write(|ctx| {
1948-
use crate::load::BytesLoader as _;
1959+
use load::BytesLoader as _;
19491960

1950-
ctx.loaders.include.forget(uri);
1961+
crate::profile_function!();
19511962

1952-
for loader in &ctx.loaders.bytes {
1953-
loader.forget(uri);
1954-
}
1963+
let loaders = self.loaders();
19551964

1956-
for loader in &ctx.loaders.image {
1957-
loader.forget(uri);
1958-
}
1965+
loaders.include.forget(uri);
1966+
for loader in loaders.bytes.lock().iter() {
1967+
loader.forget(uri);
1968+
}
1969+
for loader in loaders.image.lock().iter() {
1970+
loader.forget(uri);
1971+
}
1972+
for loader in loaders.texture.lock().iter() {
1973+
loader.forget(uri);
1974+
}
1975+
}
19591976

1960-
for loader in &ctx.loaders.texture {
1961-
loader.forget(uri);
1962-
}
1963-
});
1977+
/// Release all memory and textures related to images used in [`Ui::image`] or [`Image`].
1978+
///
1979+
/// If you attempt to load any images again, they will be reloaded from scratch.
1980+
pub fn forget_all_images(&self) {
1981+
use load::BytesLoader as _;
1982+
1983+
crate::profile_function!();
1984+
1985+
let loaders = self.loaders();
1986+
1987+
loaders.include.forget_all();
1988+
for loader in loaders.bytes.lock().iter() {
1989+
loader.forget_all();
1990+
}
1991+
for loader in loaders.image.lock().iter() {
1992+
loader.forget_all();
1993+
}
1994+
for loader in loaders.texture.lock().iter() {
1995+
loader.forget_all();
1996+
}
19641997
}
19651998

19661999
/// Try loading the bytes from the given uri using any available bytes loaders.
@@ -1977,11 +2010,14 @@ impl Context {
19772010
/// - [`LoadError::NotSupported`][not_supported] if none of the registered loaders support loading the given `uri`.
19782011
/// - [`LoadError::Custom`][custom] if one of the loaders _does_ support loading the `uri`, but the loading process failed.
19792012
///
2013+
/// ⚠ May deadlock if called from within a `BytesLoader`!
2014+
///
19802015
/// [not_supported]: crate::load::LoadError::NotSupported
19812016
/// [custom]: crate::load::LoadError::Custom
19822017
pub fn try_load_bytes(&self, uri: &str) -> load::BytesLoadResult {
1983-
let loaders = self.loaders();
1984-
for loader in &loaders.bytes {
2018+
crate::profile_function!();
2019+
2020+
for loader in self.loaders().bytes.lock().iter() {
19852021
match loader.load(self, uri) {
19862022
Err(load::LoadError::NotSupported) => continue,
19872023
result => return result,
@@ -2005,11 +2041,14 @@ impl Context {
20052041
/// - [`LoadError::NotSupported`][not_supported] if none of the registered loaders support loading the given `uri`.
20062042
/// - [`LoadError::Custom`][custom] if one of the loaders _does_ support loading the `uri`, but the loading process failed.
20072043
///
2044+
/// ⚠ May deadlock if called from within an `ImageLoader`!
2045+
///
20082046
/// [not_supported]: crate::load::LoadError::NotSupported
20092047
/// [custom]: crate::load::LoadError::Custom
20102048
pub fn try_load_image(&self, uri: &str, size_hint: load::SizeHint) -> load::ImageLoadResult {
2011-
let loaders = self.loaders();
2012-
for loader in &loaders.image {
2049+
crate::profile_function!();
2050+
2051+
for loader in self.loaders().image.lock().iter() {
20132052
match loader.load(self, uri, size_hint) {
20142053
Err(load::LoadError::NotSupported) => continue,
20152054
result => return result,
@@ -2033,6 +2072,8 @@ impl Context {
20332072
/// - [`LoadError::NotSupported`][not_supported] if none of the registered loaders support loading the given `uri`.
20342073
/// - [`LoadError::Custom`][custom] if one of the loaders _does_ support loading the `uri`, but the loading process failed.
20352074
///
2075+
/// ⚠ May deadlock if called from within a `TextureLoader`!
2076+
///
20362077
/// [not_supported]: crate::load::LoadError::NotSupported
20372078
/// [custom]: crate::load::LoadError::Custom
20382079
pub fn try_load_texture(
@@ -2041,9 +2082,9 @@ impl Context {
20412082
texture_options: TextureOptions,
20422083
size_hint: load::SizeHint,
20432084
) -> load::TextureLoadResult {
2044-
let loaders = self.loaders();
2085+
crate::profile_function!();
20452086

2046-
for loader in &loaders.texture {
2087+
for loader in self.loaders().texture.lock().iter() {
20472088
match loader.load(self, uri, texture_options, size_hint) {
20482089
Err(load::LoadError::NotSupported) => continue,
20492090
result => return result,
@@ -2053,9 +2094,9 @@ impl Context {
20532094
Err(load::LoadError::NotSupported)
20542095
}
20552096

2056-
fn loaders(&self) -> load::Loaders {
2097+
fn loaders(&self) -> Arc<Loaders> {
20572098
crate::profile_function!();
2058-
self.read(|this| this.loaders.clone()) // TODO(emilk): something less slow
2099+
self.read(|this| this.loaders.clone())
20592100
}
20602101
}
20612102

crates/egui/src/lib.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
//! ui.separator();
8585
//!
8686
//! # let my_image = egui::TextureId::default();
87-
//! ui.image(my_image, [640.0, 480.0]);
87+
//! ui.raw_image((my_image, egui::Vec2::new(640.0, 480.0)));
8888
//!
8989
//! ui.collapsing("Click to see what is hidden!", |ui| {
9090
//! ui.label("Not much, as it turns out");
@@ -424,6 +424,28 @@ pub fn warn_if_debug_build(ui: &mut crate::Ui) {
424424

425425
// ----------------------------------------------------------------------------
426426

427+
/// Include an image in the binary.
428+
///
429+
/// This is a wrapper over `include_bytes!`, and behaves in the same way.
430+
///
431+
/// It produces an [`ImageSource`] which can be used directly in [`Ui::image`] or [`Image::new`]:
432+
///
433+
/// ```
434+
/// # egui::__run_test_ui(|ui| {
435+
/// ui.image(egui::include_image!("../assets/ferris.png"));
436+
/// ui.add(
437+
/// egui::Image::new(egui::include_image!("../assets/ferris.png"))
438+
/// .rounding(egui::Rounding::same(6.0))
439+
/// );
440+
/// # });
441+
/// ```
442+
#[macro_export]
443+
macro_rules! include_image {
444+
($path: literal) => {
445+
$crate::ImageSource::Bytes($path, $crate::load::Bytes::Static(include_bytes!($path)))
446+
};
447+
}
448+
427449
/// Create a [`Hyperlink`](crate::Hyperlink) to the current [`file!()`] (and line) on Github
428450
///
429451
/// ```

0 commit comments

Comments
 (0)