Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 2 commits into from
Mar 27, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions crates/epaint/src/shadow.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use emath::NumExt as _;

use super::*;

/// The color and fuzziness of a fuzzy shape.
Expand All @@ -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.
Expand Down Expand Up @@ -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(
Expand Down
Loading