1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-27 15:13:12 -04:00

eframe: gate web clipboard image copy behind web_clipboard_image feature

The wasm path of `set_clipboard_image` is the only consumer of the
`image` crate inside eframe; `image`'s decoders / color-management code
add ~600 KB compressed to a wasm bundle even when only `png` is
requested (image-webp / moxcms / pxfm get pulled in regardless).

Make the dep optional behind a new `web_clipboard_image` feature
(default-on, so this is source-compatible for everyone except those who
explicitly want the smaller bundle). Apps that opt out get a
`log::warn!` if a CopyImage command ever fires.
This commit is contained in:
lucasmerlin
2026-05-08 05:37:40 +02:00
parent 34f25e8155
commit 90b83c780e
3 changed files with 23 additions and 1 deletions

View File

@@ -29,12 +29,18 @@ default = [
"accesskit",
"default_fonts",
"wayland", # Required for Linux support (including CI!)
"web_clipboard_image",
"web_screen_reader",
"wgpu",
"winit/default",
"x11",
]
## Enable PNG-encoding `egui::ColorImage`s for the web clipboard via the
## [`image`](https://docs.rs/image) crate. Disable on the web to drop the
## image (and image-webp / moxcms / ...) crates from the wasm bundle.
web_clipboard_image = ["dep:image"]
## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/).
accesskit = ["egui-winit/accesskit"]
@@ -195,7 +201,10 @@ windows-sys = { workspace = true, features = [
# web:
[target.'cfg(target_arch = "wasm32")'.dependencies]
bytemuck.workspace = true
image = { workspace = true, features = ["png"] } # For copying images
# `image` is only needed for the "copy image to clipboard" path on the web.
# Behind `web_clipboard_image` (default-on) so apps that don't care can drop
# the image crate (and ~600 KB of png/webp/moxcms compressed code with it).
image = { workspace = true, features = ["png"], optional = true }
js-sys.workspace = true
percent-encoding.workspace = true
wasm-bindgen.workspace = true

View File

@@ -385,7 +385,16 @@ impl AppRunner {
super::set_clipboard_text(&text);
}
egui::OutputCommand::CopyImage(image) => {
#[cfg(feature = "web_clipboard_image")]
super::set_clipboard_image(&image);
#[cfg(not(feature = "web_clipboard_image"))]
{
let _ = image;
log::warn!(
"CopyImage requested but eframe was built without the \
`web_clipboard_image` feature; ignoring."
);
}
}
egui::OutputCommand::OpenUrl(open_url) => {
super::open_url(&open_url.url, open_url.new_tab);

View File

@@ -207,6 +207,7 @@ fn set_clipboard_text(s: &str) {
}
/// Set the clipboard image.
#[cfg(feature = "web_clipboard_image")]
fn set_clipboard_image(image: &egui::ColorImage) {
if let Some(window) = web_sys::window() {
if !window.is_secure_context() {
@@ -250,6 +251,7 @@ fn set_clipboard_image(image: &egui::ColorImage) {
}
}
#[cfg(feature = "web_clipboard_image")]
fn to_image(image: &egui::ColorImage) -> Result<image::RgbaImage, String> {
profiling::function_scope!();
image::RgbaImage::from_raw(
@@ -260,6 +262,7 @@ fn to_image(image: &egui::ColorImage) -> Result<image::RgbaImage, String> {
.ok_or_else(|| "Invalid IconData".to_owned())
}
#[cfg(feature = "web_clipboard_image")]
fn to_png_bytes(image: &image::RgbaImage) -> Result<Vec<u8>, String> {
profiling::function_scope!();
let mut png_bytes: Vec<u8> = Vec::new();
@@ -272,6 +275,7 @@ fn to_png_bytes(image: &image::RgbaImage) -> Result<Vec<u8>, String> {
Ok(png_bytes)
}
#[cfg(feature = "web_clipboard_image")]
fn create_clipboard_item(mime: &str, bytes: &[u8]) -> Result<web_sys::ClipboardItem, JsValue> {
let array = js_sys::Uint8Array::from(bytes);
let blob_parts = js_sys::Array::new();