From dde13b30c79776accf1b54fcd131441f44fc8f95 Mon Sep 17 00:00:00 2001 From: Daniel van Vugt Date: Thu, 26 Sep 2024 18:26:21 +0800 Subject: [PATCH] st/theme-node-drawing: Trace the correct path for inset box shadows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: --- src/st/st-theme-node-drawing.c | 73 +++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c index 5933b3a3b..0b2b7b535 100644 --- a/src/st/st-theme-node-drawing.c +++ b/src/st/st-theme-node-drawing.c @@ -909,6 +909,7 @@ static void paint_inset_box_shadow_to_cairo_context (StThemeNode *node, StShadow *shadow_spec, float resource_scale, + const guint outer_radius[4], cairo_t *cr, 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_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_t *temp_cr; cairo_matrix_t matrix; @@ -970,14 +967,67 @@ paint_inset_box_shadow_to_cairo_context (StThemeNode *node, /* Shadow offset */ cairo_translate (temp_cr, shadow_spec->xoffset, shadow_spec->yoffset); - /* Scale the path around the center to match the shrunk bounds */ - cairo_translate (temp_cr, x_center, y_center); - cairo_scale (temp_cr, - (shrunk_extents_x2 - shrunk_extents_x1) / (extents_x2 - extents_x1), - (shrunk_extents_y2 - shrunk_extents_y1) / (extents_y2 - extents_y1)); - cairo_translate (temp_cr, - x_center, - y_center); + cairo_new_path (temp_cr); + + int inner_radius; + + inner_radius = MAX (0, outer_radius[ST_CORNER_TOPLEFT] - shadow_spec->spread); + 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_destroy (temp_cr); @@ -1330,6 +1380,7 @@ st_theme_node_prerender_background (StThemeNode *node, paint_inset_box_shadow_to_cairo_context (node, box_shadow_spec, resource_scale, + radius, cr, interior_path ? interior_path : outline_path);