Specify deifferent minification and magnification filters (#2224)
* Specify deifferent minification and magnification filters * Fixes * Update changelogs * Doctest fixes * Add deprecation notice for RetainedImage::with_texture_filter
This commit is contained in:
parent
8e79a5a8ae
commit
34e6e12f00
19 changed files with 147 additions and 97 deletions
|
@ -12,6 +12,7 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG
|
||||||
* Added `Context::os/Context::set_os` to query/set what operating system egui believes it is running on ([#2202](https://github.com/emilk/egui/pull/2202)).
|
* Added `Context::os/Context::set_os` to query/set what operating system egui believes it is running on ([#2202](https://github.com/emilk/egui/pull/2202)).
|
||||||
* Added `Button::shortcut_text` for showing keyboard shortcuts in menu buttons ([#2202](https://github.com/emilk/egui/pull/2202)).
|
* Added `Button::shortcut_text` for showing keyboard shortcuts in menu buttons ([#2202](https://github.com/emilk/egui/pull/2202)).
|
||||||
* Added `egui::KeyboardShortcut` for showing keyboard shortcuts in menu buttons ([#2202](https://github.com/emilk/egui/pull/2202)).
|
* Added `egui::KeyboardShortcut` for showing keyboard shortcuts in menu buttons ([#2202](https://github.com/emilk/egui/pull/2202)).
|
||||||
|
* Texture loading now takes a `TexureOptions` with minification and magnification filters ([#2224](https://github.com/emilk/egui/pull/2224)).
|
||||||
|
|
||||||
### Fixed 🐛
|
### Fixed 🐛
|
||||||
* ⚠️ BREAKING: Fix text being too small ([#2069](https://github.com/emilk/egui/pull/2069)).
|
* ⚠️ BREAKING: Fix text being too small ([#2069](https://github.com/emilk/egui/pull/2069)).
|
||||||
|
|
|
@ -136,7 +136,7 @@ pub struct Renderer {
|
||||||
/// sampler.
|
/// sampler.
|
||||||
textures: HashMap<egui::TextureId, (Option<wgpu::Texture>, wgpu::BindGroup)>,
|
textures: HashMap<egui::TextureId, (Option<wgpu::Texture>, wgpu::BindGroup)>,
|
||||||
next_user_texture_id: u64,
|
next_user_texture_id: u64,
|
||||||
samplers: HashMap<egui::TextureFilter, wgpu::Sampler>,
|
samplers: HashMap<egui::TextureOptions, wgpu::Sampler>,
|
||||||
|
|
||||||
/// Storage for use by [`egui::PaintCallback`]'s that need to store resources such as render
|
/// Storage for use by [`egui::PaintCallback`]'s that need to store resources such as render
|
||||||
/// pipelines that must have the lifetime of the renderpass.
|
/// pipelines that must have the lifetime of the renderpass.
|
||||||
|
@ -530,8 +530,8 @@ impl Renderer {
|
||||||
});
|
});
|
||||||
let sampler = self
|
let sampler = self
|
||||||
.samplers
|
.samplers
|
||||||
.entry(image_delta.filter)
|
.entry(image_delta.options)
|
||||||
.or_insert_with(|| create_sampler(image_delta.filter, device));
|
.or_insert_with(|| create_sampler(image_delta.options, device));
|
||||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
label,
|
label,
|
||||||
layout: &self.texture_bind_group_layout,
|
layout: &self.texture_bind_group_layout,
|
||||||
|
@ -790,15 +790,22 @@ impl Renderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_sampler(filter: egui::TextureFilter, device: &wgpu::Device) -> wgpu::Sampler {
|
fn create_sampler(options: egui::TextureOptions, device: &wgpu::Device) -> wgpu::Sampler {
|
||||||
let wgpu_filter = match filter {
|
let mag_filter = match options.magnification {
|
||||||
|
egui::TextureFilter::Nearest => wgpu::FilterMode::Nearest,
|
||||||
|
egui::TextureFilter::Linear => wgpu::FilterMode::Linear,
|
||||||
|
};
|
||||||
|
let min_filter = match options.minification {
|
||||||
egui::TextureFilter::Nearest => wgpu::FilterMode::Nearest,
|
egui::TextureFilter::Nearest => wgpu::FilterMode::Nearest,
|
||||||
egui::TextureFilter::Linear => wgpu::FilterMode::Linear,
|
egui::TextureFilter::Linear => wgpu::FilterMode::Linear,
|
||||||
};
|
};
|
||||||
device.create_sampler(&wgpu::SamplerDescriptor {
|
device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
label: Some(&format!("egui sampler ({:?})", filter)),
|
label: Some(&format!(
|
||||||
mag_filter: wgpu_filter,
|
"egui sampler (mag: {:?}, min {:?})",
|
||||||
min_filter: wgpu_filter,
|
mag_filter, min_filter
|
||||||
|
)),
|
||||||
|
mag_filter,
|
||||||
|
min_filter,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
input_state::*, layers::GraphicLayers, memory::Options, os::OperatingSystem,
|
input_state::*, layers::GraphicLayers, memory::Options, os::OperatingSystem,
|
||||||
output::FullOutput, TextureHandle, *,
|
output::FullOutput, TextureHandle, *,
|
||||||
};
|
};
|
||||||
use epaint::{mutex::*, stats::*, text::Fonts, textures::TextureFilter, TessellationOptions, *};
|
use epaint::{mutex::*, stats::*, text::Fonts, TessellationOptions, *};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -800,7 +800,7 @@ impl Context {
|
||||||
/// ui.ctx().load_texture(
|
/// ui.ctx().load_texture(
|
||||||
/// "my-image",
|
/// "my-image",
|
||||||
/// egui::ColorImage::example(),
|
/// egui::ColorImage::example(),
|
||||||
/// egui::TextureFilter::Linear
|
/// Default::default()
|
||||||
/// )
|
/// )
|
||||||
/// });
|
/// });
|
||||||
///
|
///
|
||||||
|
@ -815,7 +815,7 @@ impl Context {
|
||||||
&self,
|
&self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
image: impl Into<ImageData>,
|
image: impl Into<ImageData>,
|
||||||
filter: TextureFilter,
|
options: TextureOptions,
|
||||||
) -> TextureHandle {
|
) -> TextureHandle {
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
let image = image.into();
|
let image = image.into();
|
||||||
|
@ -829,7 +829,7 @@ impl Context {
|
||||||
max_texture_side
|
max_texture_side
|
||||||
);
|
);
|
||||||
let tex_mngr = self.tex_manager();
|
let tex_mngr = self.tex_manager();
|
||||||
let tex_id = tex_mngr.write().alloc(name, image, filter);
|
let tex_id = tex_mngr.write().alloc(name, image, options);
|
||||||
TextureHandle::new(tex_mngr, tex_id)
|
TextureHandle::new(tex_mngr, tex_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -332,7 +332,7 @@ pub use epaint::hex_color;
|
||||||
pub use epaint::{
|
pub use epaint::{
|
||||||
color, mutex,
|
color, mutex,
|
||||||
text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak},
|
text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak},
|
||||||
textures::{TextureFilter, TexturesDelta},
|
textures::{TextureFilter, TextureOptions, TexturesDelta},
|
||||||
ClippedPrimitive, Color32, ColorImage, FontImage, ImageData, Mesh, PaintCallback,
|
ClippedPrimitive, Color32, ColorImage, FontImage, ImageData, Mesh, PaintCallback,
|
||||||
PaintCallbackInfo, Rgba, Rounding, Shape, Stroke, TextureHandle, TextureId,
|
PaintCallbackInfo, Rgba, Rounding, Shape, Stroke, TextureHandle, TextureId,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1553,7 +1553,7 @@ impl Ui {
|
||||||
/// ui.ctx().load_texture(
|
/// ui.ctx().load_texture(
|
||||||
/// "my-image",
|
/// "my-image",
|
||||||
/// egui::ColorImage::example(),
|
/// egui::ColorImage::example(),
|
||||||
/// egui::TextureFilter::Linear
|
/// Default::default()
|
||||||
/// )
|
/// )
|
||||||
/// });
|
/// });
|
||||||
///
|
///
|
||||||
|
|
|
@ -18,7 +18,7 @@ use emath::Rot2;
|
||||||
/// ui.ctx().load_texture(
|
/// ui.ctx().load_texture(
|
||||||
/// "my-image",
|
/// "my-image",
|
||||||
/// egui::ColorImage::example(),
|
/// egui::ColorImage::example(),
|
||||||
/// egui::TextureFilter::Linear
|
/// Default::default()
|
||||||
/// )
|
/// )
|
||||||
/// });
|
/// });
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use egui::{color::*, widgets::color_picker::show_color, TextureFilter, *};
|
use egui::{color::*, widgets::color_picker::show_color, TextureOptions, *};
|
||||||
|
|
||||||
const GRADIENT_SIZE: Vec2 = vec2(256.0, 18.0);
|
const GRADIENT_SIZE: Vec2 = vec2(256.0, 18.0);
|
||||||
|
|
||||||
|
@ -372,7 +372,7 @@ impl TextureManager {
|
||||||
size: [width, height],
|
size: [width, height],
|
||||||
pixels,
|
pixels,
|
||||||
},
|
},
|
||||||
TextureFilter::Linear,
|
TextureOptions::LINEAR,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -596,11 +596,8 @@ impl ItemsDemo {
|
||||||
};
|
};
|
||||||
|
|
||||||
let texture: &egui::TextureHandle = self.texture.get_or_insert_with(|| {
|
let texture: &egui::TextureHandle = self.texture.get_or_insert_with(|| {
|
||||||
ui.ctx().load_texture(
|
ui.ctx()
|
||||||
"plot_demo",
|
.load_texture("plot_demo", egui::ColorImage::example(), Default::default())
|
||||||
egui::ColorImage::example(),
|
|
||||||
egui::TextureFilter::Linear,
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
let image = PlotImage::new(
|
let image = PlotImage::new(
|
||||||
texture,
|
texture,
|
||||||
|
|
|
@ -115,11 +115,8 @@ impl WidgetGallery {
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let texture: &egui::TextureHandle = texture.get_or_insert_with(|| {
|
let texture: &egui::TextureHandle = texture.get_or_insert_with(|| {
|
||||||
ui.ctx().load_texture(
|
ui.ctx()
|
||||||
"example",
|
.load_texture("example", egui::ColorImage::example(), Default::default())
|
||||||
egui::ColorImage::example(),
|
|
||||||
egui::TextureFilter::Linear,
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.add(doc_link_label("Label", "label,heading"));
|
ui.add(doc_link_label("Label", "label,heading"));
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use egui::mutex::Mutex;
|
use egui::{mutex::Mutex, TextureFilter, TextureOptions};
|
||||||
use egui::TextureFilter;
|
|
||||||
|
|
||||||
/// An image to be shown in egui.
|
/// An image to be shown in egui.
|
||||||
///
|
///
|
||||||
|
@ -13,7 +12,7 @@ pub struct RetainedImage {
|
||||||
image: Mutex<egui::ColorImage>,
|
image: Mutex<egui::ColorImage>,
|
||||||
/// Lazily loaded when we have an egui context.
|
/// Lazily loaded when we have an egui context.
|
||||||
texture: Mutex<Option<egui::TextureHandle>>,
|
texture: Mutex<Option<egui::TextureHandle>>,
|
||||||
filter: TextureFilter,
|
options: TextureOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RetainedImage {
|
impl RetainedImage {
|
||||||
|
@ -23,7 +22,7 @@ impl RetainedImage {
|
||||||
size: image.size,
|
size: image.size,
|
||||||
image: Mutex::new(image),
|
image: Mutex::new(image),
|
||||||
texture: Default::default(),
|
texture: Default::default(),
|
||||||
filter: Default::default(),
|
options: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +67,7 @@ impl RetainedImage {
|
||||||
Self::from_svg_bytes(debug_name, svg_str.as_bytes())
|
Self::from_svg_bytes(debug_name, svg_str.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the texture filter to use for the image.
|
/// Set the texture filters to use for the image.
|
||||||
///
|
///
|
||||||
/// **Note:** If the texture has already been uploaded to the GPU, this will require
|
/// **Note:** If the texture has already been uploaded to the GPU, this will require
|
||||||
/// re-uploading the texture with the updated filter.
|
/// re-uploading the texture with the updated filter.
|
||||||
|
@ -76,7 +75,7 @@ impl RetainedImage {
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use egui_extras::RetainedImage;
|
/// # use egui_extras::RetainedImage;
|
||||||
/// # use egui::{Color32, epaint::{ColorImage, textures::TextureFilter}};
|
/// # use egui::{Color32, epaint::{ColorImage, textures::TextureOptions}};
|
||||||
/// # let pixels = vec![Color32::BLACK];
|
/// # let pixels = vec![Color32::BLACK];
|
||||||
/// # let color_image = ColorImage {
|
/// # let color_image = ColorImage {
|
||||||
/// # size: [1, 1],
|
/// # size: [1, 1],
|
||||||
|
@ -85,10 +84,10 @@ impl RetainedImage {
|
||||||
/// #
|
/// #
|
||||||
/// // Upload a pixel art image without it getting blurry when resized
|
/// // Upload a pixel art image without it getting blurry when resized
|
||||||
/// let image = RetainedImage::from_color_image("my_image", color_image)
|
/// let image = RetainedImage::from_color_image("my_image", color_image)
|
||||||
/// .with_texture_filter(TextureFilter::Nearest);
|
/// .with_options(TextureOptions::NEAREST);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn with_texture_filter(mut self, filter: TextureFilter) -> Self {
|
pub fn with_options(mut self, options: TextureOptions) -> Self {
|
||||||
self.filter = filter;
|
self.options = options;
|
||||||
|
|
||||||
// If the texture has already been uploaded, this will force it to be re-uploaded with the
|
// If the texture has already been uploaded, this will force it to be re-uploaded with the
|
||||||
// updated filter.
|
// updated filter.
|
||||||
|
@ -97,6 +96,14 @@ impl RetainedImage {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated = "Use with_options instead"]
|
||||||
|
pub fn with_texture_filter(self, filter: TextureFilter) -> Self {
|
||||||
|
self.with_options(TextureOptions {
|
||||||
|
magnification: filter,
|
||||||
|
minification: filter,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// The size of the image data (number of pixels wide/high).
|
/// The size of the image data (number of pixels wide/high).
|
||||||
pub fn size(&self) -> [usize; 2] {
|
pub fn size(&self) -> [usize; 2] {
|
||||||
self.size
|
self.size
|
||||||
|
@ -130,7 +137,7 @@ impl RetainedImage {
|
||||||
.get_or_insert_with(|| {
|
.get_or_insert_with(|| {
|
||||||
let image: &mut ColorImage = &mut self.image.lock();
|
let image: &mut ColorImage = &mut self.image.lock();
|
||||||
let image = std::mem::take(image);
|
let image = std::mem::take(image);
|
||||||
ctx.load_texture(&self.debug_name, image, self.filter)
|
ctx.load_texture(&self.debug_name, image, self.options)
|
||||||
})
|
})
|
||||||
.id()
|
.id()
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ fn main() {
|
||||||
// Allocate egui's texture id for GL texture
|
// Allocate egui's texture id for GL texture
|
||||||
let texture_id = egui_glium
|
let texture_id = egui_glium
|
||||||
.painter
|
.painter
|
||||||
.register_native_texture(glium_texture, egui::TextureFilter::Linear);
|
.register_native_texture(glium_texture, Default::default());
|
||||||
// Setup button image size for reasonable image size for button container.
|
// Setup button image size for reasonable image size for button container.
|
||||||
let button_image_size = egui::vec2(32_f32, 32_f32);
|
let button_image_size = egui::vec2(32_f32, 32_f32);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
#![allow(deprecated)] // legacy implement_vertex macro
|
#![allow(deprecated)] // legacy implement_vertex macro
|
||||||
#![allow(semicolon_in_expressions_from_macros)] // glium::program! macro
|
#![allow(semicolon_in_expressions_from_macros)] // glium::program! macro
|
||||||
|
|
||||||
use egui::{epaint::Primitive, TextureFilter};
|
use egui::{
|
||||||
|
epaint::{textures::TextureFilter, Primitive},
|
||||||
|
TextureOptions,
|
||||||
|
};
|
||||||
|
|
||||||
use {
|
use {
|
||||||
egui::{emath::Rect, epaint::Mesh},
|
egui::{emath::Rect, epaint::Mesh},
|
||||||
|
@ -10,7 +13,7 @@ use {
|
||||||
index::PrimitiveType,
|
index::PrimitiveType,
|
||||||
texture::{self, srgb_texture2d::SrgbTexture2d},
|
texture::{self, srgb_texture2d::SrgbTexture2d},
|
||||||
uniform,
|
uniform,
|
||||||
uniforms::{MagnifySamplerFilter, SamplerWrapFunction},
|
uniforms::{MagnifySamplerFilter, MinifySamplerFilter, SamplerWrapFunction},
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
};
|
};
|
||||||
|
@ -191,14 +194,25 @@ impl Painter {
|
||||||
|
|
||||||
if let Some(texture) = self.texture(mesh.texture_id) {
|
if let Some(texture) = self.texture(mesh.texture_id) {
|
||||||
// The texture coordinates for text are so that both nearest and linear should work with the egui font texture.
|
// The texture coordinates for text are so that both nearest and linear should work with the egui font texture.
|
||||||
let filter = match texture.filter {
|
let mag_filter = match texture.options.magnification {
|
||||||
TextureFilter::Nearest => MagnifySamplerFilter::Nearest,
|
TextureFilter::Nearest => MagnifySamplerFilter::Nearest,
|
||||||
TextureFilter::Linear => MagnifySamplerFilter::Linear,
|
TextureFilter::Linear => MagnifySamplerFilter::Linear,
|
||||||
};
|
};
|
||||||
|
let min_filter = match texture.options.minification {
|
||||||
|
TextureFilter::Nearest => MinifySamplerFilter::Nearest,
|
||||||
|
TextureFilter::Linear => MinifySamplerFilter::Linear,
|
||||||
|
};
|
||||||
|
|
||||||
|
let sampler = texture
|
||||||
|
.glium_texture
|
||||||
|
.sampled()
|
||||||
|
.magnify_filter(mag_filter)
|
||||||
|
.minify_filter(min_filter)
|
||||||
|
.wrap_function(SamplerWrapFunction::Clamp);
|
||||||
|
|
||||||
let uniforms = uniform! {
|
let uniforms = uniform! {
|
||||||
u_screen_size: [width_in_points, height_in_points],
|
u_screen_size: [width_in_points, height_in_points],
|
||||||
u_sampler: texture.glium_texture.sampled().magnify_filter(filter).wrap_function(SamplerWrapFunction::Clamp),
|
u_sampler: sampler,
|
||||||
};
|
};
|
||||||
|
|
||||||
// egui outputs colors with premultiplied alpha:
|
// egui outputs colors with premultiplied alpha:
|
||||||
|
@ -309,13 +323,13 @@ impl Painter {
|
||||||
.main_level()
|
.main_level()
|
||||||
.write(rect, glium_image);
|
.write(rect, glium_image);
|
||||||
|
|
||||||
user_texture.filter = delta.filter;
|
user_texture.options = delta.options;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let gl_texture =
|
let gl_texture =
|
||||||
SrgbTexture2d::with_format(facade, glium_image, format, mipmaps).unwrap();
|
SrgbTexture2d::with_format(facade, glium_image, format, mipmaps).unwrap();
|
||||||
|
|
||||||
let user_texture = EguiTexture::new(gl_texture.into(), delta.filter);
|
let user_texture = EguiTexture::new(gl_texture.into(), delta.options);
|
||||||
self.textures.insert(tex_id, user_texture);
|
self.textures.insert(tex_id, user_texture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -331,12 +345,12 @@ impl Painter {
|
||||||
pub fn register_native_texture(
|
pub fn register_native_texture(
|
||||||
&mut self,
|
&mut self,
|
||||||
native: Rc<SrgbTexture2d>,
|
native: Rc<SrgbTexture2d>,
|
||||||
filter: TextureFilter,
|
options: TextureOptions,
|
||||||
) -> egui::TextureId {
|
) -> egui::TextureId {
|
||||||
let id = egui::TextureId::User(self.next_native_tex_id);
|
let id = egui::TextureId::User(self.next_native_tex_id);
|
||||||
self.next_native_tex_id += 1;
|
self.next_native_tex_id += 1;
|
||||||
|
|
||||||
let texture = EguiTexture::new(native, filter);
|
let texture = EguiTexture::new(native, options);
|
||||||
self.textures.insert(id, texture);
|
self.textures.insert(id, texture);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
@ -345,23 +359,23 @@ impl Painter {
|
||||||
&mut self,
|
&mut self,
|
||||||
id: egui::TextureId,
|
id: egui::TextureId,
|
||||||
replacing: Rc<SrgbTexture2d>,
|
replacing: Rc<SrgbTexture2d>,
|
||||||
filter: TextureFilter,
|
options: TextureOptions,
|
||||||
) {
|
) {
|
||||||
let texture = EguiTexture::new(replacing, filter);
|
let texture = EguiTexture::new(replacing, options);
|
||||||
self.textures.insert(id, texture);
|
self.textures.insert(id, texture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EguiTexture {
|
struct EguiTexture {
|
||||||
glium_texture: Rc<SrgbTexture2d>,
|
glium_texture: Rc<SrgbTexture2d>,
|
||||||
filter: TextureFilter,
|
options: TextureOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EguiTexture {
|
impl EguiTexture {
|
||||||
fn new(glium_texture: Rc<SrgbTexture2d>, filter: TextureFilter) -> Self {
|
fn new(glium_texture: Rc<SrgbTexture2d>, options: TextureOptions) -> Self {
|
||||||
Self {
|
Self {
|
||||||
glium_texture,
|
glium_texture,
|
||||||
filter,
|
options,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,17 +20,15 @@ pub use glow::Context;
|
||||||
const VERT_SRC: &str = include_str!("shader/vertex.glsl");
|
const VERT_SRC: &str = include_str!("shader/vertex.glsl");
|
||||||
const FRAG_SRC: &str = include_str!("shader/fragment.glsl");
|
const FRAG_SRC: &str = include_str!("shader/fragment.glsl");
|
||||||
|
|
||||||
pub type TextureFilter = egui::TextureFilter;
|
|
||||||
|
|
||||||
trait TextureFilterExt {
|
trait TextureFilterExt {
|
||||||
fn glow_code(&self) -> u32;
|
fn glow_code(&self) -> u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureFilterExt for TextureFilter {
|
impl TextureFilterExt for egui::TextureFilter {
|
||||||
fn glow_code(&self) -> u32 {
|
fn glow_code(&self) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
TextureFilter::Linear => glow::LINEAR,
|
egui::TextureFilter::Linear => glow::LINEAR,
|
||||||
TextureFilter::Nearest => glow::NEAREST,
|
egui::TextureFilter::Nearest => glow::NEAREST,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -469,7 +467,7 @@ impl Painter {
|
||||||
|
|
||||||
let data: &[u8] = bytemuck::cast_slice(image.pixels.as_ref());
|
let data: &[u8] = bytemuck::cast_slice(image.pixels.as_ref());
|
||||||
|
|
||||||
self.upload_texture_srgb(delta.pos, image.size, delta.filter, data);
|
self.upload_texture_srgb(delta.pos, image.size, delta.options, data);
|
||||||
}
|
}
|
||||||
egui::ImageData::Font(image) => {
|
egui::ImageData::Font(image) => {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -483,7 +481,7 @@ impl Painter {
|
||||||
.flat_map(|a| a.to_array())
|
.flat_map(|a| a.to_array())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
self.upload_texture_srgb(delta.pos, image.size, delta.filter, &data);
|
self.upload_texture_srgb(delta.pos, image.size, delta.options, &data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -492,7 +490,7 @@ impl Painter {
|
||||||
&mut self,
|
&mut self,
|
||||||
pos: Option<[usize; 2]>,
|
pos: Option<[usize; 2]>,
|
||||||
[w, h]: [usize; 2],
|
[w, h]: [usize; 2],
|
||||||
texture_filter: TextureFilter,
|
options: egui::TextureOptions,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
) {
|
) {
|
||||||
assert_eq!(data.len(), w * h * 4);
|
assert_eq!(data.len(), w * h * 4);
|
||||||
|
@ -508,12 +506,12 @@ impl Painter {
|
||||||
self.gl.tex_parameter_i32(
|
self.gl.tex_parameter_i32(
|
||||||
glow::TEXTURE_2D,
|
glow::TEXTURE_2D,
|
||||||
glow::TEXTURE_MAG_FILTER,
|
glow::TEXTURE_MAG_FILTER,
|
||||||
texture_filter.glow_code() as i32,
|
options.magnification.glow_code() as i32,
|
||||||
);
|
);
|
||||||
self.gl.tex_parameter_i32(
|
self.gl.tex_parameter_i32(
|
||||||
glow::TEXTURE_2D,
|
glow::TEXTURE_2D,
|
||||||
glow::TEXTURE_MIN_FILTER,
|
glow::TEXTURE_MIN_FILTER,
|
||||||
texture_filter.glow_code() as i32,
|
options.minification.glow_code() as i32,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.gl.tex_parameter_i32(
|
self.gl.tex_parameter_i32(
|
||||||
|
|
|
@ -13,6 +13,7 @@ All notable changes to the epaint crate will be documented in this file.
|
||||||
* Added `epaint::hex_color!` to create `Color32`'s from hex strings under the `color-hex` feature ([#1596](https://github.com/emilk/egui/pull/1596)).
|
* Added `epaint::hex_color!` to create `Color32`'s from hex strings under the `color-hex` feature ([#1596](https://github.com/emilk/egui/pull/1596)).
|
||||||
* Optimize tessellation of filled circles by 10x or more ([#1616](https://github.com/emilk/egui/pull/1616)).
|
* Optimize tessellation of filled circles by 10x or more ([#1616](https://github.com/emilk/egui/pull/1616)).
|
||||||
* Added opt-in feature `deadlock_detection` to detect double-lock of mutexes on the same thread ([#1619](https://github.com/emilk/egui/pull/1619)).
|
* Added opt-in feature `deadlock_detection` to detect double-lock of mutexes on the same thread ([#1619](https://github.com/emilk/egui/pull/1619)).
|
||||||
|
* Texture loading now takes a `TexureOptions` with minification and magnification filters ([#2224](https://github.com/emilk/egui/pull/2224)).
|
||||||
|
|
||||||
|
|
||||||
## 0.18.1 - 2022-05-01
|
## 0.18.1 - 2022-05-01
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{textures::TextureFilter, Color32};
|
use crate::{textures::TextureOptions, Color32};
|
||||||
|
|
||||||
/// An image stored in RAM.
|
/// An image stored in RAM.
|
||||||
///
|
///
|
||||||
|
@ -291,7 +291,7 @@ pub struct ImageDelta {
|
||||||
/// If [`Self::pos`] is `Some`, this describes a patch of the whole image starting at [`Self::pos`].
|
/// If [`Self::pos`] is `Some`, this describes a patch of the whole image starting at [`Self::pos`].
|
||||||
pub image: ImageData,
|
pub image: ImageData,
|
||||||
|
|
||||||
pub filter: TextureFilter,
|
pub options: TextureOptions,
|
||||||
|
|
||||||
/// If `None`, set the whole texture to [`Self::image`].
|
/// If `None`, set the whole texture to [`Self::image`].
|
||||||
///
|
///
|
||||||
|
@ -301,19 +301,19 @@ pub struct ImageDelta {
|
||||||
|
|
||||||
impl ImageDelta {
|
impl ImageDelta {
|
||||||
/// Update the whole texture.
|
/// Update the whole texture.
|
||||||
pub fn full(image: impl Into<ImageData>, filter: TextureFilter) -> Self {
|
pub fn full(image: impl Into<ImageData>, options: TextureOptions) -> Self {
|
||||||
Self {
|
Self {
|
||||||
image: image.into(),
|
image: image.into(),
|
||||||
filter,
|
options,
|
||||||
pos: None,
|
pos: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update a sub-region of an existing texture.
|
/// Update a sub-region of an existing texture.
|
||||||
pub fn partial(pos: [usize; 2], image: impl Into<ImageData>, filter: TextureFilter) -> Self {
|
pub fn partial(pos: [usize; 2], image: impl Into<ImageData>, options: TextureOptions) -> Self {
|
||||||
Self {
|
Self {
|
||||||
image: image.into(),
|
image: image.into(),
|
||||||
filter,
|
options,
|
||||||
pos: Some(pos),
|
pos: Some(pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use emath::{remap_clamp, Rect};
|
use emath::{remap_clamp, Rect};
|
||||||
|
|
||||||
use crate::{textures::TextureFilter, FontImage, ImageDelta};
|
use crate::{FontImage, ImageDelta};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
struct Rectu {
|
struct Rectu {
|
||||||
|
@ -174,16 +174,18 @@ impl TextureAtlas {
|
||||||
|
|
||||||
/// Call to get the change to the image since last call.
|
/// Call to get the change to the image since last call.
|
||||||
pub fn take_delta(&mut self) -> Option<ImageDelta> {
|
pub fn take_delta(&mut self) -> Option<ImageDelta> {
|
||||||
|
let texture_options = crate::textures::TextureOptions::LINEAR;
|
||||||
|
|
||||||
let dirty = std::mem::replace(&mut self.dirty, Rectu::NOTHING);
|
let dirty = std::mem::replace(&mut self.dirty, Rectu::NOTHING);
|
||||||
if dirty == Rectu::NOTHING {
|
if dirty == Rectu::NOTHING {
|
||||||
None
|
None
|
||||||
} else if dirty == Rectu::EVERYTHING {
|
} else if dirty == Rectu::EVERYTHING {
|
||||||
Some(ImageDelta::full(self.image.clone(), TextureFilter::Linear))
|
Some(ImageDelta::full(self.image.clone(), texture_options))
|
||||||
} else {
|
} else {
|
||||||
let pos = [dirty.min_x, dirty.min_y];
|
let pos = [dirty.min_x, dirty.min_y];
|
||||||
let size = [dirty.max_x - dirty.min_x, dirty.max_y - dirty.min_y];
|
let size = [dirty.max_x - dirty.min_x, dirty.max_y - dirty.min_y];
|
||||||
let region = self.image.region(pos, size);
|
let region = self.image.region(pos, size);
|
||||||
Some(ImageDelta::partial(pos, region, TextureFilter::Linear))
|
Some(ImageDelta::partial(pos, region, texture_options))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
emath::NumExt, mutex::RwLock, textures::TextureFilter, ImageData, ImageDelta, TextureId,
|
emath::NumExt, mutex::RwLock, textures::TextureOptions, ImageData, ImageDelta, TextureId,
|
||||||
TextureManager,
|
TextureManager,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,10 +66,10 @@ impl TextureHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign a new image to an existing texture.
|
/// Assign a new image to an existing texture.
|
||||||
pub fn set(&mut self, image: impl Into<ImageData>, filter: TextureFilter) {
|
pub fn set(&mut self, image: impl Into<ImageData>, options: TextureOptions) {
|
||||||
self.tex_mngr
|
self.tex_mngr
|
||||||
.write()
|
.write()
|
||||||
.set(self.id, ImageDelta::full(image.into(), filter));
|
.set(self.id, ImageDelta::full(image.into(), options));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign a new image to a subregion of the whole texture.
|
/// Assign a new image to a subregion of the whole texture.
|
||||||
|
@ -77,11 +77,11 @@ impl TextureHandle {
|
||||||
&mut self,
|
&mut self,
|
||||||
pos: [usize; 2],
|
pos: [usize; 2],
|
||||||
image: impl Into<ImageData>,
|
image: impl Into<ImageData>,
|
||||||
filter: TextureFilter,
|
options: TextureOptions,
|
||||||
) {
|
) {
|
||||||
self.tex_mngr
|
self.tex_mngr
|
||||||
.write()
|
.write()
|
||||||
.set(self.id, ImageDelta::partial(pos, image.into(), filter));
|
.set(self.id, ImageDelta::partial(pos, image.into(), options));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// width x height
|
/// width x height
|
||||||
|
|
|
@ -26,7 +26,7 @@ impl TextureManager {
|
||||||
/// MUST have a white pixel at (0,0) ([`crate::WHITE_UV`]).
|
/// MUST have a white pixel at (0,0) ([`crate::WHITE_UV`]).
|
||||||
///
|
///
|
||||||
/// The texture is given a retain-count of `1`, requiring one call to [`Self::free`] to free it.
|
/// The texture is given a retain-count of `1`, requiring one call to [`Self::free`] to free it.
|
||||||
pub fn alloc(&mut self, name: String, image: ImageData, filter: TextureFilter) -> TextureId {
|
pub fn alloc(&mut self, name: String, image: ImageData, options: TextureOptions) -> TextureId {
|
||||||
let id = TextureId::Managed(self.next_id);
|
let id = TextureId::Managed(self.next_id);
|
||||||
self.next_id += 1;
|
self.next_id += 1;
|
||||||
|
|
||||||
|
@ -35,10 +35,10 @@ impl TextureManager {
|
||||||
size: image.size(),
|
size: image.size(),
|
||||||
bytes_per_pixel: image.bytes_per_pixel(),
|
bytes_per_pixel: image.bytes_per_pixel(),
|
||||||
retain_count: 1,
|
retain_count: 1,
|
||||||
filter,
|
options,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.delta.set.push((id, ImageDelta::full(image, filter)));
|
self.delta.set.push((id, ImageDelta::full(image, options)));
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,8 +131,50 @@ pub struct TextureMeta {
|
||||||
/// Free when this reaches zero.
|
/// Free when this reaches zero.
|
||||||
pub retain_count: usize,
|
pub retain_count: usize,
|
||||||
|
|
||||||
/// The texture filtering mode to use when rendering
|
/// The texture filtering mode to use when rendering.
|
||||||
pub filter: TextureFilter,
|
pub options: TextureOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextureMeta {
|
||||||
|
/// Size in bytes.
|
||||||
|
/// width x height x [`Self::bytes_per_pixel`].
|
||||||
|
pub fn bytes_used(&self) -> usize {
|
||||||
|
self.size[0] * self.size[1] * self.bytes_per_pixel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// How the texture texels are filtered.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub struct TextureOptions {
|
||||||
|
/// How to filter when magnifying (when texels are larger than pixels).
|
||||||
|
pub magnification: TextureFilter,
|
||||||
|
|
||||||
|
/// How to filter when minifying (when texels are smaller than pixels).
|
||||||
|
pub minification: TextureFilter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextureOptions {
|
||||||
|
/// Linear magnification and minification.
|
||||||
|
pub const LINEAR: Self = Self {
|
||||||
|
magnification: TextureFilter::Linear,
|
||||||
|
minification: TextureFilter::Linear,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Nearest magnification and minification.
|
||||||
|
pub const NEAREST: Self = Self {
|
||||||
|
magnification: TextureFilter::Nearest,
|
||||||
|
minification: TextureFilter::Nearest,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TextureOptions {
|
||||||
|
/// The default is linear for both magnification and minification.
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::LINEAR
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How the texture texels are filtered.
|
/// How the texture texels are filtered.
|
||||||
|
@ -146,25 +188,9 @@ pub enum TextureFilter {
|
||||||
Nearest,
|
Nearest,
|
||||||
|
|
||||||
/// Linearly interpolate the nearest neighbors, creating a smoother look when zooming in and out.
|
/// Linearly interpolate the nearest neighbors, creating a smoother look when zooming in and out.
|
||||||
///
|
|
||||||
/// This is the default.
|
|
||||||
Linear,
|
Linear,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TextureFilter {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Linear
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TextureMeta {
|
|
||||||
/// Size in bytes.
|
|
||||||
/// width x height x [`Self::bytes_per_pixel`].
|
|
||||||
pub fn bytes_used(&self) -> usize {
|
|
||||||
self.size[0] * self.size[1] * self.bytes_per_pixel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// What has been allocated and freed during the last period.
|
/// What has been allocated and freed during the last period.
|
||||||
|
|
|
@ -30,7 +30,7 @@ impl eframe::App for MyApp {
|
||||||
self.texture = Some(ui.ctx().load_texture(
|
self.texture = Some(ui.ctx().load_texture(
|
||||||
"screenshot",
|
"screenshot",
|
||||||
screenshot,
|
screenshot,
|
||||||
egui::TextureFilter::Linear,
|
Default::default(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue