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:
Emil Ernerfeldt 2022-11-02 17:54:06 +01:00 committed by GitHub
parent 8e79a5a8ae
commit 34e6e12f00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 147 additions and 97 deletions

View file

@ -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)).

View file

@ -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()
}) })
} }

View file

@ -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)
} }

View file

@ -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,
}; };

View file

@ -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()
/// ) /// )
/// }); /// });
/// ///

View file

@ -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()
/// ) /// )
/// }); /// });
/// ///

View file

@ -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,
) )
}) })
} }

View file

@ -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,

View file

@ -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"));

View file

@ -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()
} }

View file

@ -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);

View file

@ -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,
} }
} }
} }

View file

@ -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(

View file

@ -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

View file

@ -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),
} }
} }

View file

@ -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))
} }
} }

View file

@ -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

View file

@ -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.

View file

@ -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(),
)); ));
} }