1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-31 13:50:04 -04:00

Add user texture support to egui_glium and egui_web backends

This commit is contained in:
Emil Ernerfeldt
2020-09-11 08:56:47 +02:00
parent 02ef0cd9d5
commit d49aec4079
5 changed files with 321 additions and 114 deletions

View File

@@ -5,7 +5,10 @@ use crate::{
*,
};
pub use egui::app::{App, Backend, RunMode, Storage};
pub use egui::{
app::{App, Backend, RunMode, Storage},
Srgba,
};
const EGUI_MEMORY_KEY: &str = "egui";
const WINDOW_KEY: &str = "window";
@@ -14,14 +17,16 @@ pub struct GliumBackend {
frame_times: egui::MovementTracker<f32>,
quit: bool,
run_mode: RunMode,
painter: Painter,
}
impl GliumBackend {
pub fn new(run_mode: RunMode) -> Self {
pub fn new(run_mode: RunMode, painter: Painter) -> Self {
Self {
frame_times: egui::MovementTracker::new(1000, 1.0),
quit: false,
run_mode,
painter,
}
}
}
@@ -46,6 +51,14 @@ impl Backend for GliumBackend {
fn quit(&mut self) {
self.quit = true;
}
fn new_texture_srgba_premultiplied(
&mut self,
size: (usize, usize),
pixels: &[Srgba],
) -> egui::TextureId {
self.painter.new_user_texture(size, pixels)
}
}
/// Run an egui app
@@ -81,12 +94,11 @@ pub fn run(
let mut ctx = egui::Context::new();
*ctx.memory() = egui::app::get_value(&storage, EGUI_MEMORY_KEY).unwrap_or_default();
let mut painter = Painter::new(&display);
let mut raw_input = make_raw_input(&display);
// used to keep track of time for animations
let start_time = Instant::now();
let mut runner = GliumBackend::new(run_mode);
let mut runner = GliumBackend::new(run_mode, Painter::new(&display));
let mut clipboard = init_clipboard();
event_loop.run(move |event, _, control_flow| {
@@ -105,7 +117,9 @@ pub fn run(
let frame_time = (Instant::now() - egui_start).as_secs_f64() as f32;
runner.frame_times.add(raw_input.time, frame_time);
painter.paint_jobs(&display, paint_jobs, &ctx.texture());
runner
.painter
.paint_jobs(&display, paint_jobs, &ctx.texture());
if runner.quit {
*control_flow = glutin::event_loop::ControlFlow::Exit

View File

@@ -4,18 +4,35 @@ use {
egui::{
math::clamp,
paint::{PaintJobs, Triangles},
Rect,
Rect, Srgba,
},
glium::{
implement_vertex, index::PrimitiveType, program, texture, uniform,
uniforms::SamplerWrapFunction, Frame, Surface,
implement_vertex,
index::PrimitiveType,
program,
texture::{self, srgb_texture2d::SrgbTexture2d},
uniform,
uniforms::SamplerWrapFunction,
Frame, Surface,
},
};
pub struct Painter {
program: glium::Program,
texture: texture::texture2d::Texture2d,
current_texture_version: Option<u64>,
egui_texture: SrgbTexture2d,
egui_texture_version: Option<u64>,
user_textures: Vec<UserTexture>,
}
#[derive(Default)]
struct UserTexture {
/// Pending upload (will be emptied later).
/// This is the format glium likes.
pixels: Vec<Vec<(u8, u8, u8, u8)>>,
/// Lazily uploaded
texture: Option<SrgbTexture2d>,
}
impl Painter {
@@ -63,7 +80,7 @@ impl Painter {
void main() {
// glium expects linear rgba
f_color = v_rgba * texture(u_sampler, v_tc).r;
f_color = v_rgba * texture(u_sampler, v_tc);
}
"
},
@@ -109,7 +126,7 @@ impl Painter {
void main() {
// glium expects linear rgba
gl_FragColor = v_rgba * texture2D(u_sampler, v_tc).r;
gl_FragColor = v_rgba * texture2D(u_sampler, v_tc);
}
",
},
@@ -155,7 +172,7 @@ impl Painter {
void main() {
// glium expects linear rgba
gl_FragColor = v_rgba * texture2D(u_sampler, v_tc).r;
gl_FragColor = v_rgba * texture2D(u_sampler, v_tc);
}
",
},
@@ -163,34 +180,69 @@ impl Painter {
.unwrap();
let pixels = vec![vec![255u8, 0u8], vec![0u8, 255u8]];
let format = texture::UncompressedFloatFormat::U8;
let format = texture::SrgbFormat::U8U8U8U8;
let mipmaps = texture::MipmapsOption::NoMipmap;
let texture =
texture::texture2d::Texture2d::with_format(facade, pixels, format, mipmaps).unwrap();
let egui_texture = SrgbTexture2d::with_format(facade, pixels, format, mipmaps).unwrap();
Painter {
program,
texture,
current_texture_version: None,
egui_texture,
egui_texture_version: None,
user_textures: Default::default(),
}
}
fn upload_texture(&mut self, facade: &dyn glium::backend::Facade, texture: &egui::Texture) {
if self.current_texture_version == Some(texture.version) {
pub fn new_user_texture(&mut self, size: (usize, usize), pixels: &[Srgba]) -> egui::TextureId {
assert_eq!(size.0 * size.1, pixels.len());
let pixels: Vec<Vec<(u8, u8, u8, u8)>> = pixels
.chunks(size.0 as usize)
.map(|row| row.iter().map(|srgba| srgba.to_tuple()).collect())
.collect();
let id = egui::TextureId::User(self.user_textures.len() as u64);
self.user_textures.push(UserTexture {
pixels,
texture: None,
});
id
}
fn upload_egui_texture(
&mut self,
facade: &dyn glium::backend::Facade,
texture: &egui::Texture,
) {
if self.egui_texture_version == Some(texture.version) {
return; // No change
}
let pixels: Vec<Vec<u8>> = texture
let pixels: Vec<Vec<(u8, u8, u8, u8)>> = texture
.pixels
.chunks(texture.width as usize)
.map(|row| row.to_vec())
.map(|row| {
row.iter()
.map(|&a| Srgba::white_alpha(a).to_tuple())
.collect()
})
.collect();
let format = texture::UncompressedFloatFormat::U8;
let format = texture::SrgbFormat::U8U8U8U8;
let mipmaps = texture::MipmapsOption::NoMipmap;
self.texture =
texture::texture2d::Texture2d::with_format(facade, pixels, format, mipmaps).unwrap();
self.current_texture_version = Some(texture.version);
self.egui_texture = SrgbTexture2d::with_format(facade, pixels, format, mipmaps).unwrap();
self.egui_texture_version = Some(texture.version);
}
fn upload_user_textures(&mut self, facade: &dyn glium::backend::Facade) {
for user_texture in &mut self.user_textures {
if user_texture.texture.is_none() {
let pixels = std::mem::take(&mut user_texture.pixels);
let format = texture::SrgbFormat::U8U8U8U8;
let mipmaps = texture::MipmapsOption::NoMipmap;
user_texture.texture =
Some(SrgbTexture2d::with_format(facade, pixels, format, mipmaps).unwrap());
}
}
}
pub fn paint_jobs(
@@ -199,7 +251,8 @@ impl Painter {
jobs: PaintJobs,
texture: &egui::Texture,
) {
self.upload_texture(display, texture);
self.upload_egui_texture(display, texture);
self.upload_user_textures(display);
let mut target = display.draw();
target.clear_color(0.0, 0.0, 0.0, 0.0);
@@ -209,6 +262,18 @@ impl Painter {
target.finish().unwrap();
}
fn get_texture(&self, texture_id: egui::TextureId) -> &SrgbTexture2d {
match texture_id {
egui::TextureId::Egui => &self.egui_texture,
egui::TextureId::User(id) => {
let id = id as usize;
assert!(id < self.user_textures.len());
let texture = self.user_textures[id].texture.as_ref();
texture.expect("Should have been uploaded")
}
}
}
#[inline(never)] // Easier profiling
fn paint_job(
&mut self,
@@ -234,7 +299,7 @@ impl Painter {
.map(|v| Vertex {
a_pos: [v.pos.x, v.pos.y],
a_tc: [v.uv.x, v.uv.y],
a_srgba: v.color.0,
a_srgba: v.color.to_array(),
})
.collect();
@@ -251,9 +316,11 @@ impl Painter {
let width_points = width_pixels as f32 / pixels_per_point;
let height_points = height_pixels as f32 / pixels_per_point;
let texture = self.get_texture(triangles.texture_id);
let uniforms = uniform! {
u_screen_size: [width_points, height_points],
u_sampler: self.texture.sampled().wrap_function(SamplerWrapFunction::Clamp),
u_sampler: texture.sampled().wrap_function(SamplerWrapFunction::Clamp),
};
// Egui outputs colors with premultiplied alpha: