mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 05:40:03 -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:
@@ -1,5 +1,6 @@
|
||||
use crate::*;
|
||||
|
||||
use egui::TexturesDelta;
|
||||
pub use egui::{pos2, Color32};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -92,7 +93,7 @@ pub struct AppRunner {
|
||||
screen_reader: crate::screen_reader::ScreenReader,
|
||||
pub(crate) text_cursor_pos: Option<egui::Pos2>,
|
||||
pub(crate) mutable_text_under_cursor: bool,
|
||||
pending_texture_destructions: Vec<u64>,
|
||||
textures_delta: TexturesDelta,
|
||||
}
|
||||
|
||||
impl AppRunner {
|
||||
@@ -139,7 +140,7 @@ impl AppRunner {
|
||||
screen_reader: Default::default(),
|
||||
text_cursor_pos: None,
|
||||
mutable_text_under_cursor: false,
|
||||
pending_texture_destructions: Default::default(),
|
||||
textures_delta: Default::default(),
|
||||
};
|
||||
|
||||
{
|
||||
@@ -183,7 +184,10 @@ impl AppRunner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn logic(&mut self) -> Result<(egui::Output, Vec<egui::ClippedMesh>), JsValue> {
|
||||
/// Returns `true` if egui requests a repaint.
|
||||
///
|
||||
/// Call [`Self::paint`] later to paint
|
||||
pub fn logic(&mut self) -> Result<(bool, Vec<egui::ClippedMesh>), JsValue> {
|
||||
let frame_start = now_sec();
|
||||
|
||||
resize_canvas_to_screen_size(self.canvas_id(), self.app.max_size_points());
|
||||
@@ -195,7 +199,9 @@ impl AppRunner {
|
||||
});
|
||||
let clipped_meshes = self.egui_ctx.tessellate(shapes);
|
||||
|
||||
self.handle_egui_output(&egui_output);
|
||||
let needs_repaint = egui_output.needs_repaint;
|
||||
let textures_delta = self.handle_egui_output(egui_output);
|
||||
self.textures_delta.append(textures_delta);
|
||||
|
||||
{
|
||||
let app_output = self.frame.take_app_output();
|
||||
@@ -205,32 +211,32 @@ impl AppRunner {
|
||||
window_title: _, // TODO: change title of window
|
||||
decorated: _, // Can't toggle decorations
|
||||
drag_window: _, // Can't be dragged
|
||||
tex_allocation_data,
|
||||
} = app_output;
|
||||
|
||||
for (id, image) in tex_allocation_data.creations {
|
||||
self.painter.set_texture(id, image);
|
||||
}
|
||||
self.pending_texture_destructions = tex_allocation_data.destructions;
|
||||
}
|
||||
|
||||
self.frame.lock().info.cpu_usage = Some((now_sec() - frame_start) as f32);
|
||||
Ok((egui_output, clipped_meshes))
|
||||
Ok((needs_repaint, clipped_meshes))
|
||||
}
|
||||
|
||||
/// Paint the results of the last call to [`Self::logic`].
|
||||
pub fn paint(&mut self, clipped_meshes: Vec<egui::ClippedMesh>) -> Result<(), JsValue> {
|
||||
self.painter
|
||||
.upload_egui_texture(&self.egui_ctx.font_image());
|
||||
let textures_delta = std::mem::take(&mut self.textures_delta);
|
||||
for (id, image) in textures_delta.set {
|
||||
self.painter.set_texture(id, image);
|
||||
}
|
||||
|
||||
self.painter.clear(self.app.clear_color());
|
||||
self.painter
|
||||
.paint_meshes(clipped_meshes, self.egui_ctx.pixels_per_point())?;
|
||||
for id in self.pending_texture_destructions.drain(..) {
|
||||
|
||||
for id in textures_delta.free {
|
||||
self.painter.free_texture(id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_egui_output(&mut self, output: &egui::Output) {
|
||||
fn handle_egui_output(&mut self, output: egui::Output) -> egui::TexturesDelta {
|
||||
if self.egui_ctx.memory().options.screen_reader {
|
||||
self.screen_reader.speak(&output.events_description());
|
||||
}
|
||||
@@ -243,27 +249,30 @@ impl AppRunner {
|
||||
events: _, // already handled
|
||||
mutable_text_under_cursor,
|
||||
text_cursor_pos,
|
||||
textures_delta,
|
||||
} = output;
|
||||
|
||||
set_cursor_icon(*cursor_icon);
|
||||
set_cursor_icon(cursor_icon);
|
||||
if let Some(open) = open_url {
|
||||
crate::open_url(&open.url, open.new_tab);
|
||||
}
|
||||
|
||||
#[cfg(web_sys_unstable_apis)]
|
||||
if !copied_text.is_empty() {
|
||||
set_clipboard_text(copied_text);
|
||||
set_clipboard_text(&copied_text);
|
||||
}
|
||||
|
||||
#[cfg(not(web_sys_unstable_apis))]
|
||||
let _ = copied_text;
|
||||
|
||||
self.mutable_text_under_cursor = *mutable_text_under_cursor;
|
||||
self.mutable_text_under_cursor = mutable_text_under_cursor;
|
||||
|
||||
if &self.text_cursor_pos != text_cursor_pos {
|
||||
if self.text_cursor_pos != text_cursor_pos {
|
||||
move_text_cursor(text_cursor_pos, self.canvas_id());
|
||||
self.text_cursor_pos = *text_cursor_pos;
|
||||
self.text_cursor_pos = text_cursor_pos;
|
||||
}
|
||||
|
||||
textures_delta
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{canvas_element_or_die, console_error};
|
||||
use egui::{ClippedMesh, FontImage, Rgba};
|
||||
use egui::{ClippedMesh, Rgba};
|
||||
use egui_glow::glow;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::JsValue;
|
||||
@@ -40,12 +40,12 @@ impl WrappedGlowPainter {
|
||||
}
|
||||
|
||||
impl crate::Painter for WrappedGlowPainter {
|
||||
fn set_texture(&mut self, tex_id: u64, image: epi::Image) {
|
||||
fn set_texture(&mut self, tex_id: egui::TextureId, image: egui::ImageData) {
|
||||
self.painter.set_texture(&self.glow_ctx, tex_id, &image);
|
||||
}
|
||||
|
||||
fn free_texture(&mut self, tex_id: u64) {
|
||||
self.painter.free_texture(tex_id);
|
||||
fn free_texture(&mut self, tex_id: egui::TextureId) {
|
||||
self.painter.free_texture(&self.glow_ctx, tex_id);
|
||||
}
|
||||
|
||||
fn debug_info(&self) -> String {
|
||||
@@ -60,10 +60,6 @@ impl crate::Painter for WrappedGlowPainter {
|
||||
&self.canvas_id
|
||||
}
|
||||
|
||||
fn upload_egui_texture(&mut self, font_image: &FontImage) {
|
||||
self.painter.upload_egui_texture(&self.glow_ctx, font_image)
|
||||
}
|
||||
|
||||
fn clear(&mut self, clear_color: Rgba) {
|
||||
let canvas_dimension = [self.canvas.width(), self.canvas.height()];
|
||||
egui_glow::painter::clear(&self.glow_ctx, canvas_dimension, clear_color)
|
||||
|
||||
@@ -482,9 +482,9 @@ fn paint_and_schedule(runner_ref: AppRunnerRef) -> Result<(), JsValue> {
|
||||
fn paint_if_needed(runner_ref: &AppRunnerRef) -> Result<(), JsValue> {
|
||||
let mut runner_lock = runner_ref.0.lock();
|
||||
if runner_lock.needs_repaint.fetch_and_clear() {
|
||||
let (output, clipped_meshes) = runner_lock.logic()?;
|
||||
let (needs_repaint, clipped_meshes) = runner_lock.logic()?;
|
||||
runner_lock.paint(clipped_meshes)?;
|
||||
if output.needs_repaint {
|
||||
if needs_repaint {
|
||||
runner_lock.needs_repaint.set_true();
|
||||
}
|
||||
runner_lock.auto_save();
|
||||
@@ -1214,7 +1214,7 @@ fn is_mobile() -> Option<bool> {
|
||||
// candidate window moves following text element (agent),
|
||||
// so it appears that the IME candidate window moves with text cursor.
|
||||
// On mobile devices, there is no need to do that.
|
||||
fn move_text_cursor(cursor: &Option<egui::Pos2>, canvas_id: &str) -> Option<()> {
|
||||
fn move_text_cursor(cursor: Option<egui::Pos2>, canvas_id: &str) -> Option<()> {
|
||||
let style = text_agent().style();
|
||||
// Note: movint agent on mobile devices will lead to unpredictable scroll.
|
||||
if is_mobile() == Some(false) {
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
use wasm_bindgen::prelude::JsValue;
|
||||
|
||||
pub trait Painter {
|
||||
fn set_texture(&mut self, tex_id: u64, image: epi::Image);
|
||||
fn set_texture(&mut self, tex_id: egui::TextureId, image: egui::ImageData);
|
||||
|
||||
fn free_texture(&mut self, tex_id: u64);
|
||||
fn free_texture(&mut self, tex_id: egui::TextureId);
|
||||
|
||||
fn debug_info(&self) -> String;
|
||||
|
||||
/// id of the canvas html element containing the rendering
|
||||
fn canvas_id(&self) -> &str;
|
||||
|
||||
fn upload_egui_texture(&mut self, font_image: &egui::FontImage);
|
||||
|
||||
fn clear(&mut self, clear_color: egui::Rgba);
|
||||
|
||||
fn paint_meshes(
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -10,10 +10,7 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
use egui::{
|
||||
emath::vec2,
|
||||
epaint::{Color32, FontImage},
|
||||
};
|
||||
use egui::{emath::vec2, epaint::Color32};
|
||||
|
||||
type Gl = WebGl2RenderingContext;
|
||||
|
||||
@@ -28,13 +25,8 @@ pub struct WebGl2Painter {
|
||||
color_buffer: WebGlBuffer,
|
||||
post_process: 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 WebGl2Painter {
|
||||
@@ -85,18 +77,13 @@ impl WebGl2Painter {
|
||||
tc_buffer,
|
||||
color_buffer,
|
||||
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> {
|
||||
@@ -218,44 +205,8 @@ impl WebGl2Painter {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl epi::NativeTexture for WebGl2Painter {
|
||||
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 WebGl2Painter {
|
||||
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));
|
||||
@@ -271,24 +222,65 @@ impl crate::Painter for WebGl2Painter {
|
||||
let border = 0;
|
||||
let src_format = Gl::RGBA;
|
||||
let src_type = Gl::UNSIGNED_BYTE;
|
||||
gl.pixel_storei(Gl::UNPACK_ALIGNMENT, 1);
|
||||
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 _,
|
||||
image.size[0] as _,
|
||||
image.size[1] as _,
|
||||
internal_format as i32,
|
||||
size[0] as i32,
|
||||
size[1] as i32,
|
||||
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 WebGl2Painter {
|
||||
type Texture = WebGlTexture;
|
||||
|
||||
fn register_native_texture(&mut self, native: Self::Texture) -> egui::TextureId {
|
||||
let id = egui::TextureId::User(self.next_native_tex_id);
|
||||
self.next_native_tex_id += 1;
|
||||
self.textures.insert(id, native);
|
||||
id
|
||||
}
|
||||
|
||||
fn free_texture(&mut self, tex_id: u64) {
|
||||
self.user_textures.remove(&tex_id);
|
||||
fn replace_native_texture(&mut self, id: egui::TextureId, native: Self::Texture) {
|
||||
self.textures.insert(id, native);
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Painter for WebGl2Painter {
|
||||
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 = 1.0;
|
||||
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 {
|
||||
@@ -307,44 +299,6 @@ impl crate::Painter for WebGl2Painter {
|
||||
&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 mut pixels: Vec<u8> = Vec::with_capacity(font_image.pixels.len() * 4);
|
||||
for srgba in font_image.srgba_pixels(1.0) {
|
||||
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 = Gl::SRGB8_ALPHA8;
|
||||
let border = 0;
|
||||
let src_format = Gl::RGBA;
|
||||
let src_type = Gl::UNSIGNED_BYTE;
|
||||
gl.pixel_storei(Gl::UNPACK_ALIGNMENT, 1);
|
||||
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