gpui: Add support for inset shadow (#57685)
This PR adds support for inset shadows in the box shadow through the `inset: true` field. It includes support for both the macOS as well WGSL shaders. For now, there is no immediate application of it in Zed, so nothing should change in the app. <img width="600" alt="Screenshot 2026-05-25 at 8 46@2x" src="https://github.com/user-attachments/assets/db564a6b-8af5-491a-a573-17c060a3647c" /> Run the example above with `cargo run --example shadow -p gpui`. Release Notes: - N/A --------- Co-authored-by: Agus Zubiaga <agus@zed.dev>
This commit is contained in:
1 parent
e2d07eaa0c
commit
d7b27f9ead
18 files changed
+341
-53
No files matched your search
@@ -854,6 +854,10 @@ struct Shadow {
|
||||
Corners corner_radii;
|
||||
Bounds content_mask;
|
||||
Hsla color;
|
||||
Bounds element_bounds;
|
||||
Corners element_corner_radii;
|
||||
uint inset;
|
||||
uint pad; // align to 8 bytes
|
||||
};
|
||||
|
||||
struct ShadowVertexOutput {
|
||||
@@ -875,10 +879,16 @@ ShadowVertexOutput shadow_vertex(uint vertex_id: SV_VertexID, uint shadow_id: SV
|
||||
float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u));
|
||||
Shadow shadow = shadows[shadow_id];
|
||||
|
||||
float margin = 3.0 * shadow.blur_radius;
|
||||
Bounds bounds = shadow.bounds;
|
||||
bounds.origin -= margin;
|
||||
bounds.size += 2.0 * margin;
|
||||
Bounds bounds;
|
||||
if (shadow.inset != 0u) {
|
||||
bounds = shadow.element_bounds;
|
||||
} else {
|
||||
// Leave room for the gaussian tail outside the shadow rect.
|
||||
float margin = 3.0 * shadow.blur_radius;
|
||||
bounds = shadow.bounds;
|
||||
bounds.origin -= margin;
|
||||
bounds.size += 2.0 * margin;
|
||||
}
|
||||
|
||||
float4 device_position = to_device_position(unit_vertex, bounds);
|
||||
float4 clip_distance = distance_from_clip_rect(unit_vertex, bounds, shadow.content_mask);
|
||||
@@ -901,21 +911,36 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET {
|
||||
float2 point0 = input.position.xy - center;
|
||||
float corner_radius = pick_corner_radius(point0, shadow.corner_radii);
|
||||
|
||||
// The signal is only non-zero in a limited range, so don't waste samples
|
||||
float low = point0.y - half_size.y;
|
||||
float high = point0.y + half_size.y;
|
||||
float start = clamp(-3. * shadow.blur_radius, low, high);
|
||||
float end = clamp(3. * shadow.blur_radius, low, high);
|
||||
float alpha;
|
||||
if (shadow.blur_radius == 0.) {
|
||||
float distance = quad_sdf(input.position.xy, shadow.bounds, shadow.corner_radii);
|
||||
alpha = saturate(0.5 - distance);
|
||||
} else {
|
||||
// The signal is only non-zero in a limited range, so don't waste samples
|
||||
float low = point0.y - half_size.y;
|
||||
float high = point0.y + half_size.y;
|
||||
float start = clamp(-3. * shadow.blur_radius, low, high);
|
||||
float end = clamp(3. * shadow.blur_radius, low, high);
|
||||
|
||||
// Accumulate samples (we can get away with surprisingly few samples)
|
||||
float step = (end - start) / 4.;
|
||||
float y = start + step * 0.5;
|
||||
float alpha = 0.;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
alpha += blur_along_x(point0.x, point0.y - y, shadow.blur_radius,
|
||||
corner_radius, half_size) *
|
||||
gaussian(y, shadow.blur_radius) * step;
|
||||
y += step;
|
||||
// Accumulate samples (we can get away with surprisingly few samples)
|
||||
float step = (end - start) / 4.;
|
||||
float y = start + step * 0.5;
|
||||
alpha = 0.;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
alpha += blur_along_x(point0.x, point0.y - y, shadow.blur_radius,
|
||||
corner_radius, half_size) *
|
||||
gaussian(y, shadow.blur_radius) * step;
|
||||
y += step;
|
||||
}
|
||||
}
|
||||
|
||||
if (shadow.inset != 0u) {
|
||||
// The inset shadow is the complement of the (blurred) hole rect, clipped to the element.
|
||||
// `saturate(0.5 - d)` gives a 1-pixel antialiased edge: d <= -0.5 -> 1, d >= 0.5 -> 0.
|
||||
alpha = 1.0 - alpha;
|
||||
float element_distance = quad_sdf(input.position.xy, shadow.element_bounds,
|
||||
shadow.element_corner_radii);
|
||||
alpha *= saturate(0.5 - element_distance);
|
||||
}
|
||||
|
||||
return input.color * float4(1., 1., 1., alpha);
|
||||
|
||||
Reference in new issue
Block a user