mirror of
https://github.com/emilk/egui.git
synced 2026-06-26 22:53:14 -04:00
Managed texture loading (#3297)
* add types from proposal * add load methods on `egui::Context` * implement loaders from proposal in `egui_extras` * impl `From<Vec2>` for `SizeHint` * re-export `SizeHint` from `egui` root * rework `svg` example to use new managed `Image` * split loaders into separate files + add logging * add `log_trace` * clean up `RetainedImage` from `svg` example * refactor ehttp loader response to bytes mapping * remove spammy trace * load images even without extension * fix lints * remove unused imports * use `Image2` in `download_image` * use `visuals.error_fg_color` in `Image2` error state * update lockfile * use `Arc<ColorImage>` in `ImageData` + add `forget` API * add `ui.image2` * add byte size query api * use iterators to sum loader byte sizes * add static image loading * use static image in `svg` example * small refactor of `Image2::ui` texture loading code * add `ImageFit` to size images properly * remove println calls * add bad image load to `download_image` example * add loader file extension support tests * fix lint errors in `loaders` * remove unused `poll-promise` dependency * add some docs to `Image2` * add some docs to `egui_extras::loaders::install` * explain `loaders::install` in examples * fix lint * upgrade `ehttp` to `0.3` for some crates * Remove some unused dependencies * Remove unnecessary context clone * Turn on the `log` create feature of egui_extras in all examples * rename `forget` and document it * derive `Debug` on `SizeHint` Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * round when converting SizeHint from vec2 Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * add `load` module docs * docstring `add_loader` methods * expose + document `load_include_bytes` * cache texture handles in `DefaultTextureLoader` * add `image2` doctest + further document `Image2` * use `Default` for default `Image2` options * update `image2` doc comment * mention immediate-mode safety * more fit calculation into inherent impl * add hover text on spinner * add `all-loaders` feature * clarify `egui_extras::loaders::install` behavior * explain how to enable image formats * properly format `uri` * use `thread::Builder` instead of `spawn` * use eq op instead of `matches` * inline `From<Arc<ColorImage>>` for `ImageData` * allow non-`'static` bytes + `forget` in `DefaultTextureLoader` * sort features * change `ehttp` feature to `http` * update `Image2` docs * refactor loader cache type --------- Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
@@ -12,8 +12,10 @@ publish = false
|
||||
eframe = { path = "../../crates/eframe", features = [
|
||||
"__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO
|
||||
] }
|
||||
egui_extras = { path = "../../crates/egui_extras", features = ["image"] }
|
||||
ehttp = "0.2"
|
||||
egui_extras = { path = "../../crates/egui_extras", features = [
|
||||
"http",
|
||||
"image",
|
||||
"log",
|
||||
] }
|
||||
env_logger = "0.10"
|
||||
image = { version = "0.24", default-features = false, features = ["jpeg"] }
|
||||
poll-promise = "0.2"
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||
|
||||
use eframe::egui;
|
||||
use egui_extras::RetainedImage;
|
||||
use poll_promise::Promise;
|
||||
|
||||
fn main() -> Result<(), eframe::Error> {
|
||||
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
||||
@@ -10,55 +8,34 @@ fn main() -> Result<(), eframe::Error> {
|
||||
eframe::run_native(
|
||||
"Download and show an image with eframe/egui",
|
||||
options,
|
||||
Box::new(|_cc| Box::<MyApp>::default()),
|
||||
Box::new(|cc| {
|
||||
// Without the following call, the `Image2` created below
|
||||
// will simply output `not supported` error messages.
|
||||
egui_extras::loaders::install(&cc.egui_ctx);
|
||||
Box::new(MyApp)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct MyApp {
|
||||
/// `None` when download hasn't started yet.
|
||||
promise: Option<Promise<ehttp::Result<RetainedImage>>>,
|
||||
}
|
||||
struct MyApp;
|
||||
|
||||
impl eframe::App for MyApp {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
let promise = self.promise.get_or_insert_with(|| {
|
||||
// Begin download.
|
||||
// We download the image using `ehttp`, a library that works both in WASM and on native.
|
||||
// We use the `poll-promise` library to communicate with the UI thread.
|
||||
let ctx = ctx.clone();
|
||||
let (sender, promise) = Promise::new();
|
||||
let request = ehttp::Request::get("https://picsum.photos/seed/1.759706314/1024");
|
||||
ehttp::fetch(request, move |response| {
|
||||
let image = response.and_then(parse_response);
|
||||
sender.send(image); // send the results back to the UI thread.
|
||||
ctx.request_repaint(); // wake up UI thread
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
let width = ui.available_width();
|
||||
let half_height = ui.available_height() / 2.0;
|
||||
|
||||
ui.allocate_ui(egui::Vec2::new(width, half_height), |ui| {
|
||||
ui.add(egui::Image2::from_uri(
|
||||
"https://picsum.photos/seed/1.759706314/1024",
|
||||
))
|
||||
});
|
||||
ui.allocate_ui(egui::Vec2::new(width, half_height), |ui| {
|
||||
ui.add(egui::Image2::from_uri(
|
||||
"https://this-is-hopefully-not-a-real-website.rs/image.png",
|
||||
))
|
||||
});
|
||||
promise
|
||||
});
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| match promise.ready() {
|
||||
None => {
|
||||
ui.spinner(); // still loading
|
||||
}
|
||||
Some(Err(err)) => {
|
||||
ui.colored_label(ui.visuals().error_fg_color, err); // something went wrong
|
||||
}
|
||||
Some(Ok(image)) => {
|
||||
image.show_max_size(ui, ui.available_size());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
fn parse_response(response: ehttp::Response) -> Result<RetainedImage, String> {
|
||||
let content_type = response.content_type().unwrap_or_default();
|
||||
if content_type.starts_with("image/") {
|
||||
RetainedImage::from_image_bytes(&response.url, &response.bytes)
|
||||
} else {
|
||||
Err(format!(
|
||||
"Expected image, found content-type {content_type:?}"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,6 @@ publish = false
|
||||
eframe = { path = "../../crates/eframe", features = [
|
||||
"__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO
|
||||
] }
|
||||
egui_extras = { path = "../../crates/egui_extras", features = ["image"] }
|
||||
egui_extras = { path = "../../crates/egui_extras", features = ["image", "log"] }
|
||||
env_logger = "0.10"
|
||||
image = { version = "0.24", default-features = false, features = ["png"] }
|
||||
|
||||
@@ -17,5 +17,4 @@ eframe = { path = "../../crates/eframe", features = [
|
||||
"wgpu",
|
||||
] }
|
||||
env_logger = "0.10"
|
||||
itertools = "0.10.3"
|
||||
image = { version = "0.24", default-features = false, features = ["png"] }
|
||||
|
||||
@@ -12,5 +12,5 @@ publish = false
|
||||
eframe = { path = "../../crates/eframe", features = [
|
||||
"__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO
|
||||
] }
|
||||
egui_extras = { path = "../../crates/egui_extras", features = ["svg"] }
|
||||
egui_extras = { path = "../../crates/egui_extras", features = ["log", "svg"] }
|
||||
env_logger = "0.10"
|
||||
|
||||
@@ -15,26 +15,16 @@ fn main() -> Result<(), eframe::Error> {
|
||||
eframe::run_native(
|
||||
"svg example",
|
||||
options,
|
||||
Box::new(|_cc| Box::<MyApp>::default()),
|
||||
Box::new(|cc| {
|
||||
// Without the following call, the `Image2` created below
|
||||
// will simply output a `not supported` error message.
|
||||
egui_extras::loaders::install(&cc.egui_ctx);
|
||||
Box::new(MyApp)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
struct MyApp {
|
||||
svg_image: egui_extras::RetainedImage,
|
||||
}
|
||||
|
||||
impl Default for MyApp {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
svg_image: egui_extras::RetainedImage::from_svg_bytes_with_size(
|
||||
"rustacean-flat-happy.svg",
|
||||
include_bytes!("rustacean-flat-happy.svg"),
|
||||
egui_extras::image::FitTo::Original,
|
||||
)
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
struct MyApp;
|
||||
|
||||
impl eframe::App for MyApp {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
@@ -45,7 +35,13 @@ impl eframe::App for MyApp {
|
||||
ui.separator();
|
||||
|
||||
let max_size = ui.available_size();
|
||||
self.svg_image.show_size(ui, max_size);
|
||||
ui.add(
|
||||
egui::Image2::from_static_bytes(
|
||||
"ferris.svg",
|
||||
include_bytes!("rustacean-flat-happy.svg"),
|
||||
)
|
||||
.size_hint(max_size),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user