mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 04:40:03 -04:00
Round the corners of the color picker (#8439)
The color picker's gradients and background checkers now follow the style's corner radius, giving it a softer look. This required changing the painting from being Mesh-based to being texture-based it's subtle with the default settings: <img width="286" height="397" alt="Screenshot 2026-08-21 at 15 08 18" src="https://github.com/user-attachments/assets/3a9d5289-71fb-4f34-8a20-1fe96891ba36" /> But you can [increase it](https://github.com/emilk/egui/pull/8445): <img width="285" height="391" alt="Screenshot 2026-08-21 at 15 08 41" src="https://github.com/user-attachments/assets/b0dfa7da-5b96-40e0-a80d-1da385d57a1a" /> ### Before for reference <img width="286" height="392" alt="Screenshot 2026-08-21 at 19 08 27" src="https://github.com/user-attachments/assets/7c754f36-795c-4381-9f9f-1fc8dd9a0264" /> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -98,9 +98,38 @@ fn vs_main(
|
||||
@group(1) @binding(0) var r_tex_color: texture_2d<f32>;
|
||||
@group(1) @binding(1) var r_tex_sampler: sampler;
|
||||
|
||||
/// 1 if the texture sampler uses nearest filtering, 0 if linear.
|
||||
/// Only read when `predictable_texture_filtering` is on.
|
||||
@group(1) @binding(2) var<uniform> r_tex_nearest_filtering: u32;
|
||||
/// Set in bit 0 of `r_tex_flags` if the sampler uses nearest filtering.
|
||||
///
|
||||
/// Must match `TEX_FLAG_NEAREST` in `renderer.rs`.
|
||||
const TEX_FLAG_NEAREST: u32 = 1u;
|
||||
|
||||
/// Wrap modes, stored in bits 1+ of `r_tex_flags`.
|
||||
///
|
||||
/// Must match the `WRAP_MODE_*` constants in `renderer.rs`.
|
||||
const WRAP_MODE_CLAMP_TO_EDGE: u32 = 0u;
|
||||
const WRAP_MODE_REPEAT: u32 = 1u;
|
||||
const WRAP_MODE_MIRRORED_REPEAT: u32 = 2u;
|
||||
|
||||
/// Texture flags, only read when `predictable_texture_filtering` is on.
|
||||
///
|
||||
/// Bit 0: `TEX_FLAG_NEAREST`.
|
||||
/// Bits 1+: one of the `WRAP_MODE_*` constants.
|
||||
@group(1) @binding(2) var<uniform> r_tex_flags: u32;
|
||||
|
||||
/// Map a texel coordinate to a valid texel according to the texture's wrap mode.
|
||||
fn wrap_texel_coord(coord: vec2<i32>, texture_size: vec2<i32>) -> vec2<i32> {
|
||||
let wrap_mode = r_tex_flags >> 1u;
|
||||
if wrap_mode == WRAP_MODE_REPEAT {
|
||||
return ((coord % texture_size) + texture_size) % texture_size;
|
||||
} else if wrap_mode == WRAP_MODE_MIRRORED_REPEAT {
|
||||
let period = 2 * texture_size;
|
||||
let phase = ((coord % period) + period) % period;
|
||||
return min(phase, period - vec2<i32>(1, 1) - phase);
|
||||
} else {
|
||||
// WRAP_MODE_CLAMP_TO_EDGE
|
||||
return clamp(coord, vec2<i32>(0, 0), texture_size - vec2<i32>(1, 1));
|
||||
}
|
||||
}
|
||||
|
||||
fn sample_texture(in: VertexOutput) -> vec4<f32> {
|
||||
if r_locals.predictable_texture_filtering == 0 {
|
||||
@@ -109,11 +138,10 @@ fn sample_texture(in: VertexOutput) -> vec4<f32> {
|
||||
} else {
|
||||
let texture_size = vec2<i32>(textureDimensions(r_tex_color, 0));
|
||||
let texture_size_f = vec2<f32>(texture_size);
|
||||
let max_coord = texture_size - vec2<i32>(1, 1);
|
||||
|
||||
if r_tex_nearest_filtering == 1 {
|
||||
if (r_tex_flags & TEX_FLAG_NEAREST) != 0u {
|
||||
// Nearest filtering: load the texel under the sample position.
|
||||
let texel = clamp(vec2<i32>(in.tex_coord * texture_size_f), vec2<i32>(0, 0), max_coord);
|
||||
let texel = wrap_texel_coord(vec2<i32>(floor(in.tex_coord * texture_size_f)), texture_size);
|
||||
return textureLoad(r_tex_color, texel, 0);
|
||||
}
|
||||
|
||||
@@ -122,11 +150,10 @@ fn sample_texture(in: VertexOutput) -> vec4<f32> {
|
||||
let pixel_fract = fract(pixel_coord);
|
||||
let pixel_floor = vec2<i32>(floor(pixel_coord));
|
||||
|
||||
// Manual texture clamping
|
||||
let p00 = clamp(pixel_floor + vec2<i32>(0, 0), vec2<i32>(0, 0), max_coord);
|
||||
let p10 = clamp(pixel_floor + vec2<i32>(1, 0), vec2<i32>(0, 0), max_coord);
|
||||
let p01 = clamp(pixel_floor + vec2<i32>(0, 1), vec2<i32>(0, 0), max_coord);
|
||||
let p11 = clamp(pixel_floor + vec2<i32>(1, 1), vec2<i32>(0, 0), max_coord);
|
||||
let p00 = wrap_texel_coord(pixel_floor + vec2<i32>(0, 0), texture_size);
|
||||
let p10 = wrap_texel_coord(pixel_floor + vec2<i32>(1, 0), texture_size);
|
||||
let p01 = wrap_texel_coord(pixel_floor + vec2<i32>(0, 1), texture_size);
|
||||
let p11 = wrap_texel_coord(pixel_floor + vec2<i32>(1, 1), texture_size);
|
||||
|
||||
// Load at pixel centers
|
||||
let tl = textureLoad(r_tex_color, p00, 0);
|
||||
|
||||
@@ -245,11 +245,10 @@ pub struct Renderer {
|
||||
uniform_bind_group: wgpu::BindGroup,
|
||||
texture_bind_group_layout: wgpu::BindGroupLayout,
|
||||
|
||||
/// Uniform buffers each holding a single `u32`:
|
||||
/// 1 if the texture sampler uses nearest filtering, 0 otherwise.
|
||||
/// Indexed by that flag value.
|
||||
/// Uniform buffers each holding a single `u32` of texture flags
|
||||
/// (see [`texture_flags`]), indexed by that flag value.
|
||||
/// Read by the shader when `predictable_texture_filtering` is on.
|
||||
nearest_filtering_flag_buffers: [wgpu::Buffer; 2],
|
||||
texture_flag_buffers: [wgpu::Buffer; NUM_TEXTURE_FLAGS],
|
||||
|
||||
/// Map of egui texture IDs to textures and their associated bindgroups (texture view +
|
||||
/// sampler). The texture may be None if the `TextureId` is just a handle to a user-provided
|
||||
@@ -367,9 +366,10 @@ impl Renderer {
|
||||
})
|
||||
};
|
||||
|
||||
let nearest_filtering_flag_buffers = [0_u32, 1_u32].map(|flag| {
|
||||
let texture_flag_buffers = core::array::from_fn::<_, NUM_TEXTURE_FLAGS, _>(|flag| {
|
||||
let flag = flag as u32;
|
||||
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some(&format!("egui_nearest_filtering_flag_{flag}")),
|
||||
label: Some(&format!("egui_texture_flags_{flag}")),
|
||||
contents: bytemuck::bytes_of(&flag),
|
||||
usage: wgpu::BufferUsages::UNIFORM,
|
||||
})
|
||||
@@ -482,7 +482,7 @@ impl Renderer {
|
||||
previous_uniform_buffer_content: UniformBuffer::zeroed(),
|
||||
uniform_bind_group,
|
||||
texture_bind_group_layout,
|
||||
nearest_filtering_flag_buffers,
|
||||
texture_flag_buffers,
|
||||
textures: HashMap::default(),
|
||||
next_user_texture_id: 0,
|
||||
samplers: HashMap::default(),
|
||||
@@ -736,6 +736,7 @@ impl Renderer {
|
||||
let bind_group = bind_group.unwrap_or_else(|| {
|
||||
let nearest =
|
||||
image_delta.options.magnification == epaint::textures::TextureFilter::Nearest;
|
||||
let wrap_mode = wrap_mode_flag(image_delta.options.wrap_mode);
|
||||
let sampler = self
|
||||
.samplers
|
||||
.entry(image_delta.options)
|
||||
@@ -756,7 +757,7 @@ impl Renderer {
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: self.nearest_filtering_flag_buffers[usize::from(nearest)]
|
||||
resource: self.texture_flag_buffers[texture_flags(nearest, wrap_mode)]
|
||||
.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
@@ -862,6 +863,7 @@ impl Renderer {
|
||||
profiling::function_scope!();
|
||||
|
||||
let nearest = sampler_descriptor.mag_filter == wgpu::FilterMode::Nearest;
|
||||
let wrap_mode = address_mode_wrap_flag(sampler_descriptor.address_mode_u);
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
compare: None,
|
||||
..sampler_descriptor
|
||||
@@ -881,7 +883,7 @@ impl Renderer {
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: self.nearest_filtering_flag_buffers[usize::from(nearest)]
|
||||
resource: self.texture_flag_buffers[texture_flags(nearest, wrap_mode)]
|
||||
.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
@@ -924,6 +926,7 @@ impl Renderer {
|
||||
.expect("Tried to update a texture that has not been allocated yet.");
|
||||
|
||||
let nearest = sampler_descriptor.mag_filter == wgpu::FilterMode::Nearest;
|
||||
let wrap_mode = address_mode_wrap_flag(sampler_descriptor.address_mode_u);
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
compare: None,
|
||||
..sampler_descriptor
|
||||
@@ -943,7 +946,7 @@ impl Renderer {
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: self.nearest_filtering_flag_buffers[usize::from(nearest)]
|
||||
resource: self.texture_flag_buffers[texture_flags(nearest, wrap_mode)]
|
||||
.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
@@ -1124,6 +1127,49 @@ impl Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set in bit 0 of the texture flags if the sampler uses nearest filtering.
|
||||
///
|
||||
/// Must match `TEX_FLAG_NEAREST` in `egui.wgsl`.
|
||||
const TEX_FLAG_NEAREST: u32 = 1;
|
||||
|
||||
/// Wrap modes, stored in bits 1+ of the texture flags.
|
||||
///
|
||||
/// Must match the `WRAP_MODE_*` constants in `egui.wgsl`.
|
||||
const WRAP_MODE_CLAMP_TO_EDGE: u32 = 0;
|
||||
const WRAP_MODE_REPEAT: u32 = 1;
|
||||
const WRAP_MODE_MIRRORED_REPEAT: u32 = 2;
|
||||
|
||||
/// Number of distinct values [`texture_flags`] can return.
|
||||
const NUM_TEXTURE_FLAGS: usize = 6;
|
||||
|
||||
fn wrap_mode_flag(wrap_mode: epaint::textures::TextureWrapMode) -> u32 {
|
||||
match wrap_mode {
|
||||
epaint::textures::TextureWrapMode::ClampToEdge => WRAP_MODE_CLAMP_TO_EDGE,
|
||||
epaint::textures::TextureWrapMode::Repeat => WRAP_MODE_REPEAT,
|
||||
epaint::textures::TextureWrapMode::MirroredRepeat => WRAP_MODE_MIRRORED_REPEAT,
|
||||
}
|
||||
}
|
||||
|
||||
fn address_mode_wrap_flag(address_mode: wgpu::AddressMode) -> u32 {
|
||||
match address_mode {
|
||||
wgpu::AddressMode::Repeat => WRAP_MODE_REPEAT,
|
||||
wgpu::AddressMode::MirrorRepeat => WRAP_MODE_MIRRORED_REPEAT,
|
||||
wgpu::AddressMode::ClampToEdge | wgpu::AddressMode::ClampToBorder => {
|
||||
WRAP_MODE_CLAMP_TO_EDGE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Index into [`Renderer::texture_flag_buffers`]: the texture flags
|
||||
/// read by the shader when `predictable_texture_filtering` is on.
|
||||
///
|
||||
/// Bit 0: [`TEX_FLAG_NEAREST`].
|
||||
/// Bits 1+: one of the `WRAP_MODE_*` constants.
|
||||
fn texture_flags(nearest: bool, wrap_mode: u32) -> usize {
|
||||
let nearest = if nearest { TEX_FLAG_NEAREST } else { 0 };
|
||||
(nearest | (wrap_mode << 1)) as usize
|
||||
}
|
||||
|
||||
fn create_sampler(
|
||||
options: epaint::textures::TextureOptions,
|
||||
device: &wgpu::Device,
|
||||
|
||||
Reference in New Issue
Block a user