Add `TextureOptions::wrap_mode` (#3954)

Exposes support in both glow and wgpu for texture wrap modes

This would be breaking for manual creations of TextureOptions but would
work with the current TextureOptions::NEAREST and LINEAR without change,
keeping those clamp to edge

I wasn't sure how best to expose the options to the user and added
consts for LINEAR_REPEAT LINEAR_MIRRORED_REPEAT NEAREST_REPEAT
NEAREST_MIRRORED_REPEAT

This does not include wrap mode clamp to border as it worked fine with
glow but with wgpu it panics due to Features
Features(ADDRESS_MODE_CLAMP_TO_BORDER) are required but not enabled on
the device, and I thought it was probably best not to try to enable that
feature, but happy to include that functionality also if that is okay to
be toggled


![image](https://github.com/emilk/egui/assets/5075747/bba71f61-a105-4e5b-b8ce-1083621eb3de)

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
Dan Lock 2024-02-05 08:37:05 +00:00 committed by GitHub
parent a41a04d635
commit a5973e5cac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 74 additions and 3 deletions

View File

@ -923,12 +923,19 @@ fn create_sampler(
epaint::textures::TextureFilter::Nearest => wgpu::FilterMode::Nearest,
epaint::textures::TextureFilter::Linear => wgpu::FilterMode::Linear,
};
let address_mode = match options.wrap_mode {
epaint::textures::TextureWrapMode::ClampToEdge => wgpu::AddressMode::ClampToEdge,
epaint::textures::TextureWrapMode::Repeat => wgpu::AddressMode::Repeat,
epaint::textures::TextureWrapMode::MirroredRepeat => wgpu::AddressMode::MirrorRepeat,
};
device.create_sampler(&wgpu::SamplerDescriptor {
label: Some(&format!(
"egui sampler (mag: {mag_filter:?}, min {min_filter:?})"
)),
mag_filter,
min_filter,
address_mode_u: address_mode,
address_mode_v: address_mode,
..Default::default()
})
}

View File

@ -395,7 +395,7 @@ pub use emath::{
pub use epaint::{
mutex,
text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak},
textures::{TextureFilter, TextureOptions, TexturesDelta},
textures::{TextureFilter, TextureOptions, TextureWrapMode, TexturesDelta},
ClippedPrimitive, ColorImage, FontImage, ImageData, Mesh, PaintCallback, PaintCallbackInfo,
Rounding, Shape, Stroke, TextureHandle, TextureId,
};

View File

@ -34,6 +34,20 @@ impl TextureFilterExt for egui::TextureFilter {
}
}
trait TextureWrapModeExt {
fn glow_code(&self) -> u32;
}
impl TextureWrapModeExt for egui::TextureWrapMode {
fn glow_code(&self) -> u32 {
match self {
Self::ClampToEdge => glow::CLAMP_TO_EDGE,
Self::Repeat => glow::REPEAT,
Self::MirroredRepeat => glow::MIRRORED_REPEAT,
}
}
}
#[derive(Debug)]
pub struct PainterError(String);
@ -555,12 +569,12 @@ impl Painter {
self.gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_WRAP_S,
glow::CLAMP_TO_EDGE as i32,
options.wrap_mode.glow_code() as i32,
);
self.gl.tex_parameter_i32(
glow::TEXTURE_2D,
glow::TEXTURE_WRAP_T,
glow::CLAMP_TO_EDGE as i32,
options.wrap_mode.glow_code() as i32,
);
check_for_gl_error!(&self.gl, "tex_parameter");

View File

@ -156,6 +156,9 @@ pub struct TextureOptions {
/// How to filter when minifying (when texels are smaller than pixels).
pub minification: TextureFilter,
/// How to wrap the texture when the texture coordinates are outside the [0, 1] range.
pub wrap_mode: TextureWrapMode,
}
impl TextureOptions {
@ -163,12 +166,42 @@ impl TextureOptions {
pub const LINEAR: Self = Self {
magnification: TextureFilter::Linear,
minification: TextureFilter::Linear,
wrap_mode: TextureWrapMode::ClampToEdge,
};
/// Nearest magnification and minification.
pub const NEAREST: Self = Self {
magnification: TextureFilter::Nearest,
minification: TextureFilter::Nearest,
wrap_mode: TextureWrapMode::ClampToEdge,
};
/// Linear magnification and minification, but with the texture repeated.
pub const LINEAR_REPEAT: Self = Self {
magnification: TextureFilter::Linear,
minification: TextureFilter::Linear,
wrap_mode: TextureWrapMode::Repeat,
};
/// Linear magnification and minification, but with the texture mirrored and repeated.
pub const LINEAR_MIRRORED_REPEAT: Self = Self {
magnification: TextureFilter::Linear,
minification: TextureFilter::Linear,
wrap_mode: TextureWrapMode::MirroredRepeat,
};
/// Nearest magnification and minification, but with the texture repeated.
pub const NEAREST_REPEAT: Self = Self {
magnification: TextureFilter::Nearest,
minification: TextureFilter::Nearest,
wrap_mode: TextureWrapMode::Repeat,
};
/// Nearest magnification and minification, but with the texture mirrored and repeated.
pub const NEAREST_MIRRORED_REPEAT: Self = Self {
magnification: TextureFilter::Nearest,
minification: TextureFilter::Nearest,
wrap_mode: TextureWrapMode::MirroredRepeat,
};
}
@ -193,6 +226,23 @@ pub enum TextureFilter {
Linear,
}
/// Defines how textures are wrapped around objects when texture coordinates fall outside the [0, 1] range.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum TextureWrapMode {
/// Stretches the edge pixels to fill beyond the texture's bounds.
///
/// This is what you want to use for a normal image in a GUI.
#[default]
ClampToEdge,
/// Tiles the texture across the surface, repeating it horizontally and vertically.
Repeat,
/// Mirrors the texture with each repetition, creating symmetrical tiling.
MirroredRepeat,
}
// ----------------------------------------------------------------------------
/// What has been allocated and freed during the last period.