st: Use scaled surfaces for creating cairo background shadows

Create the surfaces for background shadows at scaled sizes and then draw on them
using logical coordinates, by setting the surface device scale accordingly.

Use the said surface scale when generating the actual shadow cairo pattern
but in such case, to reduce the number of code changes, is better to work in
absolute coordinates, and to do so:
  1) Create a temporary shadow-spec copy with scaled values to absolute sizes
  2) Invert the scaling on the shadow matrix
  3) Do the actual painting in absolute coordinates
  4) Set the shadow matrix scaling back to the logical coordinates.

Finally scale down the created shadow pattern surface size when painting it,
applying again a reverse scale to the matrix.

https://bugzilla.gnome.org/show_bug.cgi?id=765011
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5
This commit is contained in:
Marco Trevisan (Treviño) 2019-02-25 13:45:02 +01:00 committed by Jonas Ådahl
parent ca4d86e9e5
commit 5617ffc79c
2 changed files with 52 additions and 6 deletions

View File

@ -536,9 +536,10 @@ _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec,
* the offset.
*/
cairo_pattern_t *
_st_create_shadow_cairo_pattern (StShadow *shadow_spec,
_st_create_shadow_cairo_pattern (StShadow *shadow_spec_in,
cairo_pattern_t *src_pattern)
{
g_autoptr(StShadow) shadow_spec = NULL;
static cairo_user_data_key_t shadow_pattern_user_data;
cairo_t *cr;
cairo_surface_t *src_surface;
@ -549,9 +550,10 @@ _st_create_shadow_cairo_pattern (StShadow *shadow_spec,
gint width_in, height_in, rowstride_in;
gint width_out, height_out, rowstride_out;
cairo_matrix_t shadow_matrix;
double xscale_in, yscale_in;
int i, j;
g_return_val_if_fail (shadow_spec != NULL, NULL);
g_return_val_if_fail (shadow_spec_in != NULL, NULL);
g_return_val_if_fail (src_pattern != NULL, NULL);
if (cairo_pattern_get_surface (src_pattern, &src_surface) != CAIRO_STATUS_SUCCESS)
@ -564,6 +566,25 @@ _st_create_shadow_cairo_pattern (StShadow *shadow_spec,
width_in = cairo_image_surface_get_width (src_surface);
height_in = cairo_image_surface_get_height (src_surface);
cairo_surface_get_device_scale (src_surface, &xscale_in, &yscale_in);
if (xscale_in != 1.0 || yscale_in != 1.0)
{
/* Scale the shadow specifications in a temporary copy so that
* we can work everywhere in absolute surface coordinates */
double scale = (xscale_in + yscale_in) / 2.0;
shadow_spec = st_shadow_new (&shadow_spec_in->color,
shadow_spec_in->xoffset * xscale_in,
shadow_spec_in->yoffset * yscale_in,
shadow_spec_in->blur * scale,
shadow_spec_in->spread * scale,
shadow_spec_in->inset);
}
else
{
shadow_spec = st_shadow_ref (shadow_spec_in);
}
/* We want the output to be a color agnostic alpha mask,
* so we need to strip the color channels from the input
*/
@ -606,6 +627,7 @@ _st_create_shadow_cairo_pattern (StShadow *shadow_spec,
width_out,
height_out,
rowstride_out);
cairo_surface_set_device_scale (surface_out, xscale_in, yscale_in);
cairo_surface_set_user_data (surface_out, &shadow_pattern_user_data,
pixels_out, (cairo_destroy_func_t) g_free);
@ -616,6 +638,9 @@ _st_create_shadow_cairo_pattern (StShadow *shadow_spec,
if (shadow_spec->inset)
{
/* Scale the matrix in surface absolute coordinates */
cairo_matrix_scale (&shadow_matrix, 1.0 / xscale_in, 1.0 / yscale_in);
/* For inset shadows, offsets and spread radius have already been
* applied to the original pattern, so all left to do is shift the
* blurred image left, so that it aligns centered under the
@ -624,6 +649,10 @@ _st_create_shadow_cairo_pattern (StShadow *shadow_spec,
cairo_matrix_translate (&shadow_matrix,
(width_out - width_in) / 2.0,
(height_out - height_in) / 2.0);
/* Scale back the matrix in original coordinates */
cairo_matrix_scale (&shadow_matrix, xscale_in, yscale_in);
cairo_pattern_set_matrix (dst_pattern, &shadow_matrix);
return dst_pattern;
}
@ -636,6 +665,9 @@ _st_create_shadow_cairo_pattern (StShadow *shadow_spec,
/* 6. Invert the matrix back */
cairo_matrix_invert (&shadow_matrix);
/* Scale the matrix in surface absolute coordinates */
cairo_matrix_scale (&shadow_matrix, 1.0 / xscale_in, 1.0 / yscale_in);
/* 5. Adjust based on specified offsets */
cairo_matrix_translate (&shadow_matrix,
shadow_spec->xoffset,
@ -657,6 +689,9 @@ _st_create_shadow_cairo_pattern (StShadow *shadow_spec,
- (width_out - width_in) / 2.0,
- (height_out - height_in) / 2.0);
/* Scale back the matrix in scaled coordinates */
cairo_matrix_scale (&shadow_matrix, xscale_in, yscale_in);
/* 1. Invert the matrix so we can work with it in pattern space
*/
cairo_matrix_invert (&shadow_matrix);

View File

@ -759,6 +759,7 @@ paint_shadow_pattern_to_cairo_context (StShadow *shadow_spec,
{
cairo_surface_t *surface;
int width, height;
double xscale, yscale;
cairo_matrix_t matrix;
cairo_save (cr);
@ -775,11 +776,13 @@ paint_shadow_pattern_to_cairo_context (StShadow *shadow_spec,
/* Something went wrong previously */
goto no_surface;
cairo_surface_get_device_scale (surface, &xscale, &yscale);
width = cairo_image_surface_get_width (surface);
height = cairo_image_surface_get_height (surface);
cairo_pattern_get_matrix (pattern, &matrix);
cairo_matrix_invert (&matrix);
cairo_matrix_scale (&matrix, 1.0 / xscale, 1.0 / yscale);
cairo_transform (cr, &matrix);
cairo_rectangle (cr, 0, height, width, - height);
@ -803,7 +806,8 @@ paint_background_image_shadow_to_cairo_context (StThemeNode *node,
int x,
int y,
int width,
int height)
int height,
float resource_scale)
{
cairo_pattern_t *shadow_pattern;
@ -819,7 +823,10 @@ paint_background_image_shadow_to_cairo_context (StThemeNode *node,
/* Prerender the pattern to a temporary surface,
* so it's properly clipped before we create a shadow from it
*/
width = ceilf (width * resource_scale);
height = ceilf (height * resource_scale);
clipped_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
cairo_surface_set_device_scale (clipped_surface, resource_scale, resource_scale);
temp_cr = cairo_create (clipped_surface);
cairo_set_operator (temp_cr, CAIRO_OPERATOR_CLEAR);
@ -883,6 +890,7 @@ path_extents (cairo_path_t *path,
static void
paint_inset_box_shadow_to_cairo_context (StThemeNode *node,
StShadow *shadow_spec,
float resource_scale,
cairo_t *cr,
cairo_path_t *shadow_outline)
{
@ -923,8 +931,8 @@ paint_inset_box_shadow_to_cairo_context (StThemeNode *node,
/* Bounds of temporary surface */
int surface_x = floor (shrunk_extents_x1);
int surface_y = floor (shrunk_extents_y1);
int surface_width = ceil (shrunk_extents_x2) - surface_x;
int surface_height = ceil (shrunk_extents_y2) - surface_y;
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;
@ -935,6 +943,7 @@ paint_inset_box_shadow_to_cairo_context (StThemeNode *node,
cairo_matrix_t matrix;
shadow_surface = cairo_image_surface_create (CAIRO_FORMAT_A8, surface_width, surface_height);
cairo_surface_set_device_scale (shadow_surface, resource_scale, resource_scale);
temp_cr = cairo_create (shadow_surface);
/* Match the coordinates in the temporary context to the parent context */
@ -1284,7 +1293,8 @@ st_theme_node_prerender_background (StThemeNode *node,
has_visible_outline? outline_path : NULL,
actor_box.x1,
actor_box.y1,
width, height);
width, height,
resource_scale);
cairo_append_path (cr, outline_path);
}
@ -1301,6 +1311,7 @@ st_theme_node_prerender_background (StThemeNode *node,
{
paint_inset_box_shadow_to_cairo_context (node,
box_shadow_spec,
resource_scale,
cr,
interior_path ? interior_path
: outline_path);