st/theme-node-drawing: Trace the correct path for inset box shadows

Previously we just copied the outer path and scaled it down, but that
resulted in an inconsistent thickness at the corners because the pivot
points for the arcs also moved inward.

So now we trace the inset path explicitly to maintain the correct
thickness (spread) around all the corners.

The spec actually mentions you need to do it in section 6.1 [1]:

> Note that for inner shadows, expanding the shadow (creating more
> shadow area) means contracting the shadow’s perimeter shape.

Despite the fact the example diagram gets it wrong in section 6.1.1 [2],
it does then follow with more confirmation that the shadow radius should
be reduced:

> To preserve the box’s shape when spread is applied, the corner radii
> of the shadow are also increased (decreased, for inner shadows) from
> the border-box (padding-box) radii by adding (subtracting) the spread
> distance (and flooring at zero).

[1] https://www.w3.org/TR/css-backgrounds-3/#box-shadow
[2] https://www.w3.org/TR/css-backgrounds-3/#shadow-shape

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/7913
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3488>
This commit is contained in:
Daniel van Vugt 2024-09-26 18:26:21 +08:00
parent 934dbe5495
commit dde13b30c7

View File

@ -909,6 +909,7 @@ static void
paint_inset_box_shadow_to_cairo_context (StThemeNode *node, paint_inset_box_shadow_to_cairo_context (StThemeNode *node,
StShadow *shadow_spec, StShadow *shadow_spec,
float resource_scale, float resource_scale,
const guint outer_radius[4],
cairo_t *cr, cairo_t *cr,
cairo_path_t *shadow_outline) cairo_path_t *shadow_outline)
{ {
@ -952,10 +953,6 @@ paint_inset_box_shadow_to_cairo_context (StThemeNode *node,
int surface_width = ceil ((shrunk_extents_x2 - surface_x) * resource_scale); int surface_width = ceil ((shrunk_extents_x2 - surface_x) * resource_scale);
int surface_height = ceil ((shrunk_extents_y2 - surface_y) * resource_scale); int surface_height = ceil ((shrunk_extents_y2 - surface_y) * resource_scale);
/* Center of the original path */
double x_center = (extents_x1 + extents_x2) / 2;
double y_center = (extents_y1 + extents_y2) / 2;
cairo_pattern_t *pattern; cairo_pattern_t *pattern;
cairo_t *temp_cr; cairo_t *temp_cr;
cairo_matrix_t matrix; cairo_matrix_t matrix;
@ -970,14 +967,67 @@ paint_inset_box_shadow_to_cairo_context (StThemeNode *node,
/* Shadow offset */ /* Shadow offset */
cairo_translate (temp_cr, shadow_spec->xoffset, shadow_spec->yoffset); cairo_translate (temp_cr, shadow_spec->xoffset, shadow_spec->yoffset);
/* Scale the path around the center to match the shrunk bounds */ cairo_new_path (temp_cr);
cairo_translate (temp_cr, x_center, y_center);
cairo_scale (temp_cr, int inner_radius;
(shrunk_extents_x2 - shrunk_extents_x1) / (extents_x2 - extents_x1),
(shrunk_extents_y2 - shrunk_extents_y1) / (extents_y2 - extents_y1)); inner_radius = MAX (0, outer_radius[ST_CORNER_TOPLEFT] - shadow_spec->spread);
cairo_translate (temp_cr, - x_center, - y_center); if (inner_radius > 0)
{
cairo_arc (temp_cr,
shrunk_extents_x1 + inner_radius,
shrunk_extents_y1 + inner_radius,
inner_radius,
M_PI, 3 * M_PI / 2);
}
inner_radius = MAX (0, outer_radius[ST_CORNER_TOPRIGHT] - shadow_spec->spread);
cairo_line_to (temp_cr,
shrunk_extents_x2 - inner_radius,
shrunk_extents_y1);
if (inner_radius > 0)
{
cairo_arc (temp_cr,
shrunk_extents_x2 - inner_radius,
shrunk_extents_y1 + inner_radius,
inner_radius,
3 * M_PI / 2, 0);
}
inner_radius = MAX (0, outer_radius[ST_CORNER_BOTTOMRIGHT] - shadow_spec->spread);
cairo_line_to (temp_cr,
shrunk_extents_x2,
shrunk_extents_y2 - inner_radius);
if (inner_radius > 0)
{
cairo_arc (temp_cr,
shrunk_extents_x2 - inner_radius,
shrunk_extents_y2 - inner_radius,
inner_radius,
0, M_PI / 2);
}
inner_radius = MAX (0, outer_radius[ST_CORNER_BOTTOMLEFT] - shadow_spec->spread);
cairo_line_to (temp_cr,
shrunk_extents_x1 + inner_radius,
shrunk_extents_y2);
if (inner_radius > 0)
{
cairo_arc (temp_cr,
shrunk_extents_x1 + inner_radius,
shrunk_extents_y2 - inner_radius,
inner_radius,
M_PI / 2, M_PI);
}
cairo_close_path (temp_cr);
cairo_append_path (temp_cr, shadow_outline);
cairo_fill (temp_cr); cairo_fill (temp_cr);
cairo_destroy (temp_cr); cairo_destroy (temp_cr);
@ -1330,6 +1380,7 @@ st_theme_node_prerender_background (StThemeNode *node,
paint_inset_box_shadow_to_cairo_context (node, paint_inset_box_shadow_to_cairo_context (node,
box_shadow_spec, box_shadow_spec,
resource_scale, resource_scale,
radius,
cr, cr,
interior_path ? interior_path interior_path ? interior_path
: outline_path); : outline_path);