mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 06:10:06 -04:00
Texture loading in egui (#1110)
* Move texture allocation into epaint/egui proper * Add TextureHandle * egui_glow: cast using bytemuck instead of unsafe code * Optimize glium painter * Optimize WebGL * Add example of loading an image from file
This commit is contained in:
@@ -9,10 +9,7 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
use egui::{
|
||||
emath::vec2,
|
||||
epaint::{Color32, FontImage},
|
||||
};
|
||||
use egui::{emath::vec2, epaint::Color32};
|
||||
|
||||
type Gl = WebGlRenderingContext;
|
||||
|
||||
@@ -28,13 +25,8 @@ pub struct WebGlPainter {
|
||||
texture_format: u32,
|
||||
post_process: Option<PostProcess>,
|
||||
|
||||
egui_texture: WebGlTexture,
|
||||
egui_texture_version: Option<u64>,
|
||||
|
||||
/// Index is the same as in [`egui::TextureId::User`].
|
||||
user_textures: HashMap<u64, WebGlTexture>,
|
||||
|
||||
next_native_tex_id: u64, // TODO: 128-bit texture space?
|
||||
textures: HashMap<egui::TextureId, WebGlTexture>,
|
||||
next_native_tex_id: u64,
|
||||
}
|
||||
|
||||
impl WebGlPainter {
|
||||
@@ -101,18 +93,13 @@ impl WebGlPainter {
|
||||
color_buffer,
|
||||
texture_format,
|
||||
post_process,
|
||||
egui_texture,
|
||||
egui_texture_version: None,
|
||||
user_textures: Default::default(),
|
||||
textures: Default::default(),
|
||||
next_native_tex_id: 1 << 32,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_texture(&self, texture_id: egui::TextureId) -> Option<&WebGlTexture> {
|
||||
match texture_id {
|
||||
egui::TextureId::Egui => Some(&self.egui_texture),
|
||||
egui::TextureId::User(id) => self.user_textures.get(&id),
|
||||
}
|
||||
self.textures.get(&texture_id)
|
||||
}
|
||||
|
||||
fn paint_mesh(&self, mesh: &egui::epaint::Mesh16) -> Result<(), JsValue> {
|
||||
@@ -234,44 +221,8 @@ impl WebGlPainter {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl epi::NativeTexture for WebGlPainter {
|
||||
type Texture = WebGlTexture;
|
||||
|
||||
fn register_native_texture(&mut self, native: Self::Texture) -> egui::TextureId {
|
||||
let id = self.next_native_tex_id;
|
||||
self.next_native_tex_id += 1;
|
||||
self.user_textures.insert(id, native);
|
||||
egui::TextureId::User(id as u64)
|
||||
}
|
||||
|
||||
fn replace_native_texture(&mut self, id: egui::TextureId, replacing: Self::Texture) {
|
||||
if let egui::TextureId::User(id) = id {
|
||||
if let Some(user_texture) = self.user_textures.get_mut(&id) {
|
||||
*user_texture = replacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Painter for WebGlPainter {
|
||||
fn set_texture(&mut self, tex_id: u64, image: epi::Image) {
|
||||
assert_eq!(
|
||||
image.size[0] * image.size[1],
|
||||
image.pixels.len(),
|
||||
"Mismatch between texture size and texel count"
|
||||
);
|
||||
|
||||
// TODO: optimize
|
||||
let mut pixels: Vec<u8> = Vec::with_capacity(image.pixels.len() * 4);
|
||||
for srgba in image.pixels {
|
||||
pixels.push(srgba.r());
|
||||
pixels.push(srgba.g());
|
||||
pixels.push(srgba.b());
|
||||
pixels.push(srgba.a());
|
||||
}
|
||||
|
||||
fn set_texture_rgba(&mut self, tex_id: egui::TextureId, size: [usize; 2], pixels: &[u8]) {
|
||||
let gl = &self.gl;
|
||||
let gl_texture = gl.create_texture().unwrap();
|
||||
gl.bind_texture(Gl::TEXTURE_2D, Some(&gl_texture));
|
||||
@@ -291,20 +242,64 @@ impl crate::Painter for WebGlPainter {
|
||||
Gl::TEXTURE_2D,
|
||||
level,
|
||||
internal_format as _,
|
||||
image.size[0] as _,
|
||||
image.size[1] as _,
|
||||
size[0] as _,
|
||||
size[1] as _,
|
||||
border,
|
||||
src_format,
|
||||
src_type,
|
||||
Some(&pixels),
|
||||
Some(pixels),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
self.user_textures.insert(tex_id, gl_texture);
|
||||
self.textures.insert(tex_id, gl_texture);
|
||||
}
|
||||
}
|
||||
|
||||
impl epi::NativeTexture for WebGlPainter {
|
||||
type Texture = WebGlTexture;
|
||||
|
||||
fn register_native_texture(&mut self, texture: Self::Texture) -> egui::TextureId {
|
||||
let id = egui::TextureId::User(self.next_native_tex_id);
|
||||
self.next_native_tex_id += 1;
|
||||
self.textures.insert(id, texture);
|
||||
id
|
||||
}
|
||||
|
||||
fn free_texture(&mut self, tex_id: u64) {
|
||||
self.user_textures.remove(&tex_id);
|
||||
fn replace_native_texture(&mut self, id: egui::TextureId, texture: Self::Texture) {
|
||||
self.textures.insert(id, texture);
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Painter for WebGlPainter {
|
||||
fn set_texture(&mut self, tex_id: egui::TextureId, image: egui::ImageData) {
|
||||
match image {
|
||||
egui::ImageData::Color(image) => {
|
||||
assert_eq!(
|
||||
image.width() * image.height(),
|
||||
image.pixels.len(),
|
||||
"Mismatch between texture size and texel count"
|
||||
);
|
||||
|
||||
let data: &[u8] = bytemuck::cast_slice(image.pixels.as_ref());
|
||||
self.set_texture_rgba(tex_id, image.size, data);
|
||||
}
|
||||
egui::ImageData::Alpha(image) => {
|
||||
let gamma = if self.post_process.is_none() {
|
||||
1.0 / 2.2 // HACK due to non-linear framebuffer blending.
|
||||
} else {
|
||||
1.0 // post process enables linear blending
|
||||
};
|
||||
let data: Vec<u8> = image
|
||||
.srgba_pixels(gamma)
|
||||
.flat_map(|a| a.to_array())
|
||||
.collect();
|
||||
self.set_texture_rgba(tex_id, image.size, &data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn free_texture(&mut self, tex_id: egui::TextureId) {
|
||||
self.textures.remove(&tex_id);
|
||||
}
|
||||
|
||||
fn debug_info(&self) -> String {
|
||||
@@ -323,48 +318,6 @@ impl crate::Painter for WebGlPainter {
|
||||
&self.canvas_id
|
||||
}
|
||||
|
||||
fn upload_egui_texture(&mut self, font_image: &FontImage) {
|
||||
if self.egui_texture_version == Some(font_image.version) {
|
||||
return; // No change
|
||||
}
|
||||
|
||||
let gamma = if self.post_process.is_none() {
|
||||
1.0 / 2.2 // HACK due to non-linear framebuffer blending.
|
||||
} else {
|
||||
1.0 // post process enables linear blending
|
||||
};
|
||||
let mut pixels: Vec<u8> = Vec::with_capacity(font_image.pixels.len() * 4);
|
||||
for srgba in font_image.srgba_pixels(gamma) {
|
||||
pixels.push(srgba.r());
|
||||
pixels.push(srgba.g());
|
||||
pixels.push(srgba.b());
|
||||
pixels.push(srgba.a());
|
||||
}
|
||||
|
||||
let gl = &self.gl;
|
||||
gl.bind_texture(Gl::TEXTURE_2D, Some(&self.egui_texture));
|
||||
|
||||
let level = 0;
|
||||
let internal_format = self.texture_format;
|
||||
let border = 0;
|
||||
let src_format = self.texture_format;
|
||||
let src_type = Gl::UNSIGNED_BYTE;
|
||||
gl.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
|
||||
Gl::TEXTURE_2D,
|
||||
level,
|
||||
internal_format as i32,
|
||||
font_image.width as i32,
|
||||
font_image.height as i32,
|
||||
border,
|
||||
src_format,
|
||||
src_type,
|
||||
Some(&pixels),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
self.egui_texture_version = Some(font_image.version);
|
||||
}
|
||||
|
||||
fn clear(&mut self, clear_color: egui::Rgba) {
|
||||
let gl = &self.gl;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user