Prevent visual glitch when shadow blur width is very high (#4245)

* Closes https://github.com/emilk/egui/issues/4238

The comment in the code explains it well, but the short of it is this:
we can't handle a shadow blur width larger than the shadow rectangle, so
we need to clamp the blur. This means smaller things will cast shadows
with a smaller blur width, but that's better than having visual
glitches.
This commit is contained in:
Emil Ernerfeldt 2024-03-27 11:22:38 +01:00 committed by GitHub
parent 947b5813d7
commit a15e6c2122
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 20 additions and 4 deletions

View File

@ -1,3 +1,5 @@
use emath::NumExt as _;
use super::*;
/// The color and fuzziness of a fuzzy shape.
@ -16,7 +18,7 @@ pub struct Shadow {
/// The width of the blur, i.e. the width of the fuzzy penumbra.
///
/// A value of 0.0 means no blur.
/// A value of 0.0 means a sharp shadow.
pub blur: f32,
/// Expand the shadow in all directions by this much.
@ -47,12 +49,26 @@ impl Shadow {
color,
} = *self;
let rect = rect.translate(offset);
let rect = rect.translate(offset).expand(spread);
// We simulate a blurry shadow by tessellating a solid rectangle using a very large feathering.
// Feathering is usually used to make the edges of a shape softer for anti-aliasing.
// The tessellator can't handle blurring/feathering larger than the smallest side of the rect.
// Thats because the tessellator approximate very thin rectangles as line segments,
// and these line segments don't have rounded corners.
// When the feathering is small (the size of a pixel), this is usually fine,
// but here we have a huge feathering to simulate blur,
// so we need to avoid this optimization in the tessellator,
// which is also why we add this rather big epsilon:
let eps = 0.1;
let blur = blur.at_most(rect.size().min_elem() - eps).at_least(0.0);
// TODO(emilk): if blur <= 0, return a simple `Shape::Rect` instead of using the tessellator
let rounding_expansion = spread.abs() + 0.5 * blur;
let rounding = rounding.into() + Rounding::from(rounding_expansion);
let rounding = rounding.into() + Rounding::same(rounding_expansion);
let rect = RectShape::filled(rect.expand(spread), rounding, color);
let rect = RectShape::filled(rect, rounding, color);
let pixels_per_point = 1.0; // doesn't matter here
let font_tex_size = [1; 2]; // unused since we are not tessellating text.
let mut tessellator = Tessellator::new(