From 9f4ae9618ae6b5d73c48c8d43976e4b0e72b9863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 11 May 2017 06:30:04 +0200 Subject: [PATCH 01/32] st-widget: Introduce widget resource scale This commit makes StWidget manage the scale of which its associated resources should be multiplied with. The resource scale is calculated by clutter, and is retrieved by clutter_actor_get_resource_scale(). Due to the resource scale not always being available, the getter may fail, and the actual widget that draws the content will have to deal with this situation. As the resource scale depends on where on the stage the widget is drawn, the resource scale will in general be available once the widget is mapped. https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- src/st/st-widget.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- src/st/st-widget.h | 3 +++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/st/st-widget.c b/src/st/st-widget.c index d12dbb3e28..78053804b2 100644 --- a/src/st/st-widget.c +++ b/src/st/st-widget.c @@ -121,6 +121,7 @@ enum { STYLE_CHANGED, POPUP_MENU, + RESOURCE_SCALE_CHANGED, LAST_SIGNAL }; @@ -452,6 +453,7 @@ static void st_widget_parent_set (ClutterActor *widget, ClutterActor *old_parent) { + StWidget *self = ST_WIDGET (widget); ClutterActorClass *parent_class; ClutterActor *new_parent; @@ -463,7 +465,7 @@ st_widget_parent_set (ClutterActor *widget, /* don't send the style changed signal if we no longer have a parent actor */ if (new_parent) - st_widget_style_changed (ST_WIDGET (widget)); + st_widget_style_changed (self); } static void @@ -1019,6 +1021,21 @@ st_widget_class_init (StWidgetClass *klass) G_STRUCT_OFFSET (StWidgetClass, popup_menu), NULL, NULL, NULL, G_TYPE_NONE, 0); + + /** + * StWidget::resource-scale-changed: + * @widget: the #StWidget + * + * Emitted when the paint scale that the widget will be painted as + * changed. + */ + signals[RESOURCE_SCALE_CHANGED] = + g_signal_new ("resource-scale-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (StWidgetClass, resource_scale_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 0); } /** @@ -1448,6 +1465,23 @@ st_widget_get_style (StWidget *actor) return ST_WIDGET_PRIVATE (actor)->inline_style; } +/** + * st_widget_get_resource_scale: + * @widget: A #StWidget + * @resource_scale: (out): return location for the resource scale + * + * Retrieves the resource scale for this #StWidget, if available. + * + * The resource scale refers to the scale the actor should use for its resources. + */ +gboolean +st_widget_get_resource_scale (StWidget *widget, + float *resource_scale) +{ + return clutter_actor_get_resource_scale (CLUTTER_ACTOR (widget), + resource_scale); +} + static void st_widget_name_notify (StWidget *widget, GParamSpec *pspec, @@ -1456,6 +1490,14 @@ st_widget_name_notify (StWidget *widget, st_widget_style_changed (widget); } +static void +st_widget_resource_scale_notify (StWidget *widget, + GParamSpec *pspec, + gpointer data) +{ + g_signal_emit (widget, signals[RESOURCE_SCALE_CHANGED], 0); +} + static void st_widget_reactive_notify (StWidget *widget, GParamSpec *pspec, @@ -1536,6 +1578,7 @@ st_widget_init (StWidget *actor) /* connect style changed */ g_signal_connect (actor, "notify::name", G_CALLBACK (st_widget_name_notify), NULL); + g_signal_connect (actor, "notify::resource-scale", G_CALLBACK (st_widget_resource_scale_notify), NULL); g_signal_connect (actor, "notify::reactive", G_CALLBACK (st_widget_reactive_notify), NULL); g_signal_connect (actor, "notify::first-child", G_CALLBACK (st_widget_first_child_notify), NULL); diff --git a/src/st/st-widget.h b/src/st/st-widget.h index 36a6325674..7c76c51368 100644 --- a/src/st/st-widget.h +++ b/src/st/st-widget.h @@ -63,6 +63,7 @@ struct _StWidgetClass /* signals */ void (* style_changed) (StWidget *self); void (* popup_menu) (StWidget *self); + void (* resource_scale_changed) (StWidget *self); /* vfuncs */ @@ -137,6 +138,8 @@ StThemeNode * st_widget_peek_theme_node (StWidget *widg GList * st_widget_get_focus_chain (StWidget *widget); void st_widget_paint_background (StWidget *widget); +gboolean st_widget_get_resource_scale (StWidget *widget, + float *resource_scale); /* debug methods */ char *st_describe_actor (ClutterActor *actor); -- GitLab From 03c4628cadc72b627581f82a889c10e6d4514af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 31 Jul 2017 14:08:29 +0200 Subject: [PATCH 02/32] st-drawing-area: Draw content taking care of the resource_scale https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- src/st/st-drawing-area.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/st/st-drawing-area.c b/src/st/st-drawing-area.c index e8fc4f6d25..f561759c62 100644 --- a/src/st/st-drawing-area.c +++ b/src/st/st-drawing-area.c @@ -35,6 +35,7 @@ #include "st-drawing-area.h" #include +#include typedef struct _StDrawingAreaPrivate StDrawingAreaPrivate; struct _StDrawingAreaPrivate { @@ -84,12 +85,22 @@ st_drawing_area_allocate (ClutterActor *self, ClutterContent *content = clutter_actor_get_content (self); ClutterActorBox content_box; int width, height; + float resource_scale; + + if (!st_widget_get_resource_scale (ST_WIDGET (self), &resource_scale)) + { + ClutterActorBox empty = CLUTTER_ACTOR_BOX_INIT_ZERO; + clutter_actor_set_allocation (self, &empty, 0); + return; + } clutter_actor_set_allocation (self, box, flags); st_theme_node_get_content_box (theme_node, box, &content_box); width = (int)(0.5 + content_box.x2 - content_box.x1); height = (int)(0.5 + content_box.y2 - content_box.y1); + + clutter_canvas_set_scale_factor (CLUTTER_CANVAS (content), resource_scale); clutter_canvas_set_size (CLUTTER_CANVAS (content), width, height); } @@ -101,6 +112,16 @@ st_drawing_area_style_changed (StWidget *self) st_drawing_area_queue_repaint (ST_DRAWING_AREA (self)); } +static void +st_drawing_area_resource_scale_changed (StWidget *self) +{ + float resource_scale; + ClutterContent *content = clutter_actor_get_content (CLUTTER_ACTOR (self)); + + if (st_widget_get_resource_scale (ST_WIDGET (self), &resource_scale)) + clutter_canvas_set_scale_factor (CLUTTER_CANVAS (content), resource_scale); +} + static void st_drawing_area_class_init (StDrawingAreaClass *klass) { @@ -109,6 +130,7 @@ st_drawing_area_class_init (StDrawingAreaClass *klass) actor_class->allocate = st_drawing_area_allocate; widget_class->style_changed = st_drawing_area_style_changed; + widget_class->resource_scale_changed = st_drawing_area_resource_scale_changed; st_drawing_area_signals[REPAINT] = g_signal_new ("repaint", @@ -185,7 +207,7 @@ st_drawing_area_get_surface_size (StDrawingArea *area, { StDrawingAreaPrivate *priv; ClutterContent *content; - float w, h; + float w, h, resource_scale; g_return_if_fail (ST_IS_DRAWING_AREA (area)); @@ -195,8 +217,18 @@ st_drawing_area_get_surface_size (StDrawingArea *area, content = clutter_actor_get_content (CLUTTER_ACTOR (area)); clutter_content_get_preferred_size (content, &w, &h); + if (st_widget_get_resource_scale (ST_WIDGET (area), &resource_scale)) + { + w /= resource_scale; + h /= resource_scale; + } + else + { + w = h = 0.0f; + } + if (width) - *width = (guint)w; + *width = ceilf (w); if (height) - *height = (guint)h; + *height = ceilf (h); } -- GitLab From 0bcf76970a2b579088ea46cb9774dd6a48daa975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Sat, 2 Sep 2017 04:08:55 +0200 Subject: [PATCH 03/32] st-private: Scale shadows accordingly to actor resource scaling Use scaled offscreen framebuffer to paint shadows so that it will match the scaling applied to the actual actor. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- src/st/st-private.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/st/st-private.c b/src/st/st-private.c index aa1e42462a..c9bcf4c3af 100644 --- a/src/st/st-private.c +++ b/src/st/st-private.c @@ -431,6 +431,7 @@ _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec, { ClutterContent *image = NULL; CoglPipeline *shadow_pipeline = NULL; + float resource_scale; float width, height; g_return_val_if_fail (clutter_actor_has_allocation (actor), NULL); @@ -440,6 +441,12 @@ _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec, if (width == 0 || height == 0) return NULL; + if (!clutter_actor_get_resource_scale (actor, &resource_scale)) + return NULL; + + width *= resource_scale; + height *= resource_scale; + image = clutter_actor_get_content (actor); if (image && CLUTTER_IS_IMAGE (image)) { @@ -491,6 +498,7 @@ _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec, cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color); cogl_framebuffer_translate (fb, -x, -y, 0); cogl_framebuffer_orthographic (fb, 0, 0, width, height, 0, 1.0); + cogl_framebuffer_scale (fb, resource_scale, resource_scale, 1); clutter_actor_set_opacity_override (actor, 255); clutter_actor_paint (actor); -- GitLab From 15f69bdc3b23e9c652aa4c72e16050f594abd526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Sat, 2 Sep 2017 04:11:39 +0200 Subject: [PATCH 04/32] st-label: Paint shadow using resource-scaled texture https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- src/st/st-label.c | 59 +++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/src/st/st-label.c b/src/st/st-label.c index 84e685705d..ed68f67f58 100644 --- a/src/st/st-label.c +++ b/src/st/st-label.c @@ -197,34 +197,52 @@ st_label_paint (ClutterActor *actor) if (shadow_spec) { - ClutterActorBox allocation; - float width, height; + float resource_scale; - clutter_actor_get_allocation_box (priv->label, &allocation); - clutter_actor_box_get_size (&allocation, &width, &height); - - if (priv->text_shadow_pipeline == NULL || - width != priv->shadow_width || - height != priv->shadow_height) + if (clutter_actor_get_resource_scale (priv->label, &resource_scale)) { - g_clear_pointer (&priv->text_shadow_pipeline, cogl_object_unref); - - priv->shadow_width = width; - priv->shadow_height = height; - priv->text_shadow_pipeline = _st_create_shadow_pipeline_from_actor (shadow_spec, priv->label); + ClutterActorBox allocation; + float width, height; + + clutter_actor_get_allocation_box (priv->label, &allocation); + clutter_actor_box_get_size (&allocation, &width, &height); + + width *= resource_scale; + height *= resource_scale; + + if (priv->text_shadow_pipeline == NULL || + width != priv->shadow_width || + height != priv->shadow_height) + { + g_clear_pointer (&priv->text_shadow_pipeline, cogl_object_unref); + + priv->shadow_width = width; + priv->shadow_height = height; + priv->text_shadow_pipeline = + _st_create_shadow_pipeline_from_actor (shadow_spec, + priv->label); + } + + if (priv->text_shadow_pipeline != NULL) + _st_paint_shadow_with_opacity (shadow_spec, + cogl_get_draw_framebuffer (), + priv->text_shadow_pipeline, + &allocation, + clutter_actor_get_paint_opacity (priv->label)); } - - if (priv->text_shadow_pipeline != NULL) - _st_paint_shadow_with_opacity (shadow_spec, - cogl_get_draw_framebuffer (), - priv->text_shadow_pipeline, - &allocation, - clutter_actor_get_paint_opacity (priv->label)); } clutter_actor_paint (priv->label); } +static void +st_label_resource_scale_changed (StWidget *widget) +{ + StLabelPrivate *priv = ST_LABEL (widget)->priv; + + g_clear_pointer (&priv->text_shadow_pipeline, cogl_object_unref); +} + static void st_label_class_init (StLabelClass *klass) { @@ -243,6 +261,7 @@ st_label_class_init (StLabelClass *klass) actor_class->get_preferred_height = st_label_get_preferred_height; widget_class->style_changed = st_label_style_changed; + widget_class->resource_scale_changed = st_label_resource_scale_changed; widget_class->get_accessible_type = st_label_accessible_get_type; pspec = g_param_spec_object ("clutter-text", -- GitLab From 6bc3300e5a3d828be546149df9adf77bf6c2efd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 20 Oct 2017 06:10:58 +0200 Subject: [PATCH 05/32] st-scroll-view-fade: Include outer bound of pixel on border The fade shader will draw the fade effect up until the border pixel. If we set the bottom right coordinate to the outer edge of the pixel we might end up not drawing the fade effect on all of the pixels. This could for example happen if one logical pixel (clutter stage pixel) consists of more than one physical pixel. https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- src/st/st-scroll-view-fade.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/st/st-scroll-view-fade.c b/src/st/st-scroll-view-fade.c index 37f44c42ca..3942cbcf0f 100644 --- a/src/st/st-scroll-view-fade.c +++ b/src/st/st-scroll-view-fade.c @@ -110,8 +110,8 @@ st_scroll_view_fade_paint_target (ClutterOffscreenEffect *effect) */ fade_area_topleft[0] = content_box.x1 + (verts[0].x - paint_box.x1); fade_area_topleft[1] = content_box.y1 + (verts[0].y - paint_box.y1); - fade_area_bottomright[0] = content_box.x2 + (verts[3].x - paint_box.x2); - fade_area_bottomright[1] = content_box.y2 + (verts[3].y - paint_box.y2); + fade_area_bottomright[0] = content_box.x2 + (verts[3].x - paint_box.x2) + 1; + fade_area_bottomright[1] = content_box.y2 + (verts[3].y - paint_box.y2) + 1; g_object_get (ST_SCROLL_VIEW (self->actor), "hscrollbar-visible", &h_scroll_visible, -- GitLab From 5d4a804c90ef824f7edc76d460cd4dd495b5df15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 26 Oct 2017 11:15:46 +0200 Subject: [PATCH 06/32] st-shadow: Ceil size before comparing with texture A fractional resource scale would mean we never use the fast path for creating the shadow, because we'd cast the int to a float before comparing, which would never match. Instead compare the expected texture size with the source texture, to actually potentially trigger the fast path. https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- src/st/st-private.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/st/st-private.c b/src/st/st-private.c index c9bcf4c3af..688b17b785 100644 --- a/src/st/st-private.c +++ b/src/st/st-private.c @@ -444,8 +444,8 @@ _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec, if (!clutter_actor_get_resource_scale (actor, &resource_scale)) return NULL; - width *= resource_scale; - height *= resource_scale; + width = ceilf (width * resource_scale); + height = ceilf (height * resource_scale); image = clutter_actor_get_content (actor); if (image && CLUTTER_IS_IMAGE (image)) -- GitLab From 0141fef5611ed09fe5f660fe009e62024733770e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Sat, 28 Oct 2017 10:57:26 +0200 Subject: [PATCH 07/32] st-theme-node: Paint elements in resource-scale scaled surfaces Pass resource-scale to drawing phase, and use it to create texture surfaces scaled with the widget current scaling. Also redraw by default widgets when the resource scale changes. https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- src/st/st-theme-node-drawing.c | 118 +++++++++++++++++++++--------- src/st/st-theme-node-transition.c | 19 +++-- src/st/st-theme-node-transition.h | 3 +- src/st/st-theme-node.h | 5 +- src/st/st-widget.c | 19 ++++- 5 files changed, 118 insertions(+), 46 deletions(-) diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c index 967f0a96c8..a94253f4ed 100644 --- a/src/st/st-theme-node-drawing.c +++ b/src/st/st-theme-node-drawing.c @@ -48,6 +48,7 @@ typedef struct { guint radius; guint border_width_1; guint border_width_2; + float resource_scale; } StCornerSpec; static void @@ -78,10 +79,13 @@ create_corner_material (StCornerSpec *corner) guint rowstride; guint8 *data; guint size; + guint logical_size; guint max_border_width; + double device_scaling; max_border_width = MAX(corner->border_width_2, corner->border_width_1); - size = 2 * MAX(max_border_width, corner->radius); + logical_size = 2 * MAX(max_border_width, corner->radius); + size = ceilf (logical_size * corner->resource_scale); rowstride = size * 4; data = g_new0 (guint8, size * rowstride); @@ -89,9 +93,11 @@ create_corner_material (StCornerSpec *corner) CAIRO_FORMAT_ARGB32, size, size, rowstride); + device_scaling = (double) size / logical_size; + cairo_surface_set_device_scale (surface, device_scaling, device_scaling); cr = cairo_create (surface); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_scale (cr, size, size); + cairo_scale (cr, logical_size, logical_size); if (max_border_width <= corner->radius) { @@ -189,13 +195,14 @@ create_corner_material (StCornerSpec *corner) static char * corner_to_string (StCornerSpec *corner) { - return g_strdup_printf ("st-theme-node-corner:%02x%02x%02x%02x,%02x%02x%02x%02x,%02x%02x%02x%02x,%u,%u,%u", + return g_strdup_printf ("st-theme-node-corner:%02x%02x%02x%02x,%02x%02x%02x%02x,%02x%02x%02x%02x,%u,%u,%u,%.4f", corner->color.red, corner->color.blue, corner->color.green, corner->color.alpha, corner->border_color_1.red, corner->border_color_1.green, corner->border_color_1.blue, corner->border_color_1.alpha, corner->border_color_2.red, corner->border_color_2.green, corner->border_color_2.blue, corner->border_color_2.alpha, corner->radius, corner->border_width_1, - corner->border_width_2); + corner->border_width_2, + corner->resource_scale); } static CoglTexture * @@ -352,6 +359,7 @@ static CoglPipeline * st_theme_node_lookup_corner (StThemeNode *node, float width, float height, + float resource_scale, StCorner corner_id) { CoglTexture *texture = NULL; @@ -370,6 +378,7 @@ st_theme_node_lookup_corner (StThemeNode *node, corner.radius = radius[corner_id]; corner.color = node->background_color; + corner.resource_scale = resource_scale; st_theme_node_get_corner_border_widths (node, corner_id, &corner.border_width_1, &corner.border_width_2); @@ -431,7 +440,7 @@ get_background_scale (StThemeNode *node, switch (node->background_size) { case ST_BACKGROUND_SIZE_AUTO: - *scale_w = 1.0; + *scale_w = 1.0f; break; case ST_BACKGROUND_SIZE_CONTAIN: *scale_w = MIN (painting_area_width / background_image_width, @@ -485,6 +494,7 @@ get_background_coordinates (StThemeNode *node, static void get_background_position (StThemeNode *self, const ClutterActorBox *allocation, + float resource_scale, ClutterActorBox *result, ClutterActorBox *texture_coords) { @@ -497,6 +507,9 @@ get_background_position (StThemeNode *self, background_image_width = cogl_texture_get_width (self->background_texture); background_image_height = cogl_texture_get_height (self->background_texture); + background_image_width /= resource_scale; + background_image_height /= resource_scale; + /* get the painting area size */ painting_area_width = allocation->x2 - allocation->x1; painting_area_height = allocation->y2 - allocation->y1; @@ -506,6 +519,7 @@ get_background_position (StThemeNode *self, painting_area_width, painting_area_height, background_image_width, background_image_height, &scale_w, &scale_h); + background_image_width *= scale_w; background_image_height *= scale_h; @@ -615,6 +629,7 @@ static cairo_pattern_t * create_cairo_pattern_of_background_image (StThemeNode *node, float width, float height, + float resource_scale, gboolean *needs_background_fill) { cairo_surface_t *surface; @@ -655,9 +670,11 @@ create_cairo_pattern_of_background_image (StThemeNode *node, get_background_scale (node, width, height, background_image_width, background_image_height, - &scale_w, &scale_h); + resource_scale, &scale_w, &scale_h); + if ((scale_w != 1) || (scale_h != 1)) cairo_matrix_scale (&matrix, 1.0/scale_w, 1.0/scale_h); + background_image_width *= scale_w; background_image_height *= scale_h; @@ -966,7 +983,8 @@ paint_inset_box_shadow_to_cairo_context (StThemeNode *node, static CoglTexture * st_theme_node_prerender_background (StThemeNode *node, float actor_width, - float actor_height) + float actor_height, + float resource_scale) { ClutterBackend *backend = clutter_get_default_backend (); CoglContext *ctx = clutter_backend_get_cogl_context (backend); @@ -994,6 +1012,8 @@ st_theme_node_prerender_background (StThemeNode *node, ClutterActorBox paint_box; cairo_path_t *interior_path = NULL; float width, height; + int texture_width; + int texture_height; border_image = st_theme_node_get_border_image (node); @@ -1021,8 +1041,11 @@ st_theme_node_prerender_background (StThemeNode *node, width = paint_box.x2 - paint_box.x1; height = paint_box.y2 - paint_box.y1; - rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width); - data = g_new0 (guchar, height * rowstride); + texture_width = ceilf (width * resource_scale); + texture_height = ceilf (height * resource_scale); + + rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, texture_width); + data = g_new0 (guchar, texture_height * rowstride); /* We zero initialize the destination memory, so it's fully transparent * by default. @@ -1031,8 +1054,9 @@ st_theme_node_prerender_background (StThemeNode *node, surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, - width, height, + texture_width, texture_height, rowstride); + cairo_surface_set_device_scale (surface, resource_scale, resource_scale); cr = cairo_create (surface); /* TODO - support non-uniform border colors */ @@ -1070,7 +1094,9 @@ st_theme_node_prerender_background (StThemeNode *node, if (background_image != NULL) { - pattern = create_cairo_pattern_of_background_image (node, width, height, + pattern = create_cairo_pattern_of_background_image (node, + width, height, + resource_scale, &draw_solid_background); if (shadow_spec && pattern != NULL) draw_background_image_shadow = TRUE; @@ -1286,7 +1312,8 @@ st_theme_node_prerender_background (StThemeNode *node, if (interior_path != NULL) cairo_path_destroy (interior_path); - texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, width, height, + texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, texture_width, + texture_height, CLUTTER_CAIRO_FORMAT_ARGB32, rowstride, data, @@ -1424,7 +1451,8 @@ static void st_theme_node_render_resources (StThemeNodePaintState *state, StThemeNode *node, float width, - float height) + float height, + float resource_scale) { gboolean has_border; gboolean has_border_radius; @@ -1443,6 +1471,7 @@ st_theme_node_render_resources (StThemeNodePaintState *state, st_theme_node_paint_state_set_node (state, node); state->alloc_width = width; state->alloc_height = height; + state->resource_scale = resource_scale; _st_theme_node_ensure_background (node); _st_theme_node_ensure_geometry (node); @@ -1488,13 +1517,13 @@ st_theme_node_render_resources (StThemeNodePaintState *state, } state->corner_material[ST_CORNER_TOPLEFT] = - st_theme_node_lookup_corner (node, width, height, ST_CORNER_TOPLEFT); + st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_TOPLEFT); state->corner_material[ST_CORNER_TOPRIGHT] = - st_theme_node_lookup_corner (node, width, height, ST_CORNER_TOPRIGHT); + st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_TOPRIGHT); state->corner_material[ST_CORNER_BOTTOMRIGHT] = - st_theme_node_lookup_corner (node, width, height, ST_CORNER_BOTTOMRIGHT); + st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_BOTTOMRIGHT); state->corner_material[ST_CORNER_BOTTOMLEFT] = - st_theme_node_lookup_corner (node, width, height, ST_CORNER_BOTTOMLEFT); + st_theme_node_lookup_corner (node, width, height, resource_scale, ST_CORNER_BOTTOMLEFT); /* Use cairo to prerender the node if there is a gradient, or * background image with borders and/or rounded corners, @@ -1509,7 +1538,8 @@ st_theme_node_render_resources (StThemeNodePaintState *state, || (has_inset_box_shadow && (has_border || node->background_color.alpha > 0)) || (st_theme_node_get_background_image (node) && (has_border || has_border_radius)) || has_large_corners) - state->prerendered_texture = st_theme_node_prerender_background (node, width, height); + state->prerendered_texture = st_theme_node_prerender_background (node, width, height, + resource_scale); if (state->prerendered_texture) state->prerendered_pipeline = _st_create_texture_pipeline (state->prerendered_texture); @@ -1546,7 +1576,8 @@ static void st_theme_node_update_resources (StThemeNodePaintState *state, StThemeNode *node, float width, - float height) + float height, + float resource_scale) { gboolean had_prerendered_texture = FALSE; gboolean had_box_shadow = FALSE; @@ -1573,12 +1604,13 @@ st_theme_node_update_resources (StThemeNodePaintState *state, st_theme_node_paint_state_set_node (state, node); state->alloc_width = width; state->alloc_height = height; + state->resource_scale = resource_scale; box_shadow_spec = st_theme_node_get_box_shadow (node); if (had_prerendered_texture) { - state->prerendered_texture = st_theme_node_prerender_background (node, width, height); + state->prerendered_texture = st_theme_node_prerender_background (node, width, height, resource_scale); state->prerendered_pipeline = _st_create_texture_pipeline (state->prerendered_texture); } else @@ -1588,7 +1620,7 @@ st_theme_node_update_resources (StThemeNodePaintState *state, for (corner_id = 0; corner_id < 4; corner_id++) if (state->corner_material[corner_id] == NULL) state->corner_material[corner_id] = - st_theme_node_lookup_corner (node, width, height, corner_id); + st_theme_node_lookup_corner (node, width, resource_scale, height, corner_id); } if (had_box_shadow) @@ -2223,6 +2255,7 @@ st_theme_node_prerender_shadow (StThemeNodePaintState *state) guint border_radius[4]; int max_borders[4]; int center_radius, corner_id; + int fb_width, fb_height; CoglTexture *buffer; CoglFramebuffer *offscreen = NULL; CoglError *error = NULL; @@ -2264,9 +2297,9 @@ st_theme_node_prerender_shadow (StThemeNodePaintState *state) } /* Render offscreen */ - buffer = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, - state->box_shadow_width, - state->box_shadow_height)); + fb_width = ceilf (state->box_shadow_width * state->resource_scale); + fb_height = ceilf (state->box_shadow_height * state->resource_scale); + buffer = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, fb_width, fb_height)); if (buffer == NULL) return; @@ -2277,8 +2310,10 @@ st_theme_node_prerender_shadow (StThemeNodePaintState *state) ClutterActorBox box = { 0, 0, state->box_shadow_width, state->box_shadow_height}; cogl_framebuffer_orthographic (offscreen, 0, 0, - state->box_shadow_width, - state->box_shadow_height, 0, 1.0); + fb_width, fb_height, 0, 1.0); + cogl_framebuffer_scale (offscreen, + state->resource_scale, + state->resource_scale, 1); cogl_framebuffer_clear4f (offscreen, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 0); st_theme_node_paint_borders (state, offscreen, &box, 0xFF); @@ -2457,11 +2492,16 @@ static gboolean st_theme_node_needs_new_box_shadow_for_size (StThemeNodePaintState *state, StThemeNode *node, float width, - float height) + float height, + float resource_scale) { if (!node->rendered_once) return TRUE; + /* The resource scale changed, so need to recompute a new box-shadow */ + if (fabsf (state->resource_scale - resource_scale) > FLT_EPSILON) + return TRUE; + /* The allocation hasn't changed, no need to recompute a new box-shadow. */ if (state->alloc_width == width && @@ -2495,7 +2535,8 @@ st_theme_node_paint (StThemeNode *node, StThemeNodePaintState *state, CoglFramebuffer *framebuffer, const ClutterActorBox *box, - guint8 paint_opacity) + guint8 paint_opacity, + float resource_scale) { float width, height; ClutterActorBox allocation; @@ -2507,7 +2548,7 @@ st_theme_node_paint (StThemeNode *node, allocation.x2 = width; allocation.y2 = height; - if (width <= 0 || height <= 0) + if (width <= 0 || height <= 0 || resource_scale <= 0.0f) return; /* Check whether we need to recreate the textures of the paint @@ -2516,22 +2557,25 @@ st_theme_node_paint (StThemeNode *node, * 2) the allocation size change requires recreating textures */ if (state->node != node || - st_theme_node_needs_new_box_shadow_for_size (state, node, width, height)) + st_theme_node_needs_new_box_shadow_for_size (state, node, width, height, + resource_scale)) { /* If we had the ability to cache textures on the node, then we can just copy them over to the paint state and avoid all rendering. We end up sharing textures a cross different widgets. */ if (node->rendered_once && node->cached_textures && - width >= node->box_shadow_min_width && height >= node->box_shadow_min_height) + width >= node->box_shadow_min_width && height >= node->box_shadow_min_height && + fabsf (resource_scale - state->resource_scale) < FLT_EPSILON) st_theme_node_paint_state_copy (state, &node->cached_state); else - st_theme_node_render_resources (state, node, width, height); + st_theme_node_render_resources (state, node, width, height, resource_scale); node->rendered_once = TRUE; } - else if (state->alloc_width != width || state->alloc_height != height) - st_theme_node_update_resources (state, node, width, height); + else if (state->alloc_width != width || state->alloc_height != height || + fabsf (state->resource_scale - resource_scale) > FLT_EPSILON) + st_theme_node_update_resources (state, node, width, height, resource_scale); /* Rough notes about the relationship of borders and backgrounds in CSS3; * see http://www.w3.org/TR/css3-background/ for more accurate details. @@ -2616,7 +2660,8 @@ st_theme_node_paint (StThemeNode *node, */ has_visible_outline = st_theme_node_has_visible_outline (node); - get_background_position (node, &allocation, &background_box, &texture_coords); + get_background_position (node, &allocation, resource_scale, + &background_box, &texture_coords); if (has_visible_outline || node->background_repeat) cogl_framebuffer_push_rectangle_clip (framebuffer, @@ -2707,6 +2752,7 @@ st_theme_node_paint_state_init (StThemeNodePaintState *state) state->alloc_width = 0; state->alloc_height = 0; + state->resource_scale = -1; state->node = NULL; state->box_shadow_pipeline = NULL; state->prerendered_texture = NULL; @@ -2731,6 +2777,7 @@ st_theme_node_paint_state_copy (StThemeNodePaintState *state, state->alloc_width = other->alloc_width; state->alloc_height = other->alloc_height; + state->resource_scale = other->resource_scale; state->box_shadow_width = other->box_shadow_width; state->box_shadow_height = other->box_shadow_height; @@ -2750,6 +2797,7 @@ st_theme_node_paint_state_invalidate (StThemeNodePaintState *state) { state->alloc_width = 0; state->alloc_height = 0; + state->resource_scale = -1.0f; } gboolean diff --git a/src/st/st-theme-node-transition.c b/src/st/st-theme-node-transition.c index 294c7e664c..34dd1a7a94 100644 --- a/src/st/st-theme-node-transition.c +++ b/src/st/st-theme-node-transition.c @@ -19,6 +19,8 @@ * along with this program. If not, see . */ +#include + #include "st-theme-node-transition.h" enum { @@ -237,7 +239,8 @@ st_theme_node_transition_get_paint_box (StThemeNodeTransition *transition, static gboolean setup_framebuffers (StThemeNodeTransition *transition, - const ClutterActorBox *allocation) + const ClutterActorBox *allocation, + float resource_scale) { StThemeNodeTransitionPrivate *priv = transition->priv; CoglContext *ctx; @@ -248,8 +251,8 @@ setup_framebuffers (StThemeNodeTransition *transition, static CoglPipeline *material_template = NULL; ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); - width = priv->offscreen_box.x2 - priv->offscreen_box.x1; - height = priv->offscreen_box.y2 - priv->offscreen_box.y1; + width = ceilf ((priv->offscreen_box.x2 - priv->offscreen_box.x1) * resource_scale); + height = ceilf ((priv->offscreen_box.y2 - priv->offscreen_box.y1) * resource_scale); g_return_val_if_fail (width > 0, FALSE); g_return_val_if_fail (height > 0, FALSE); @@ -320,7 +323,7 @@ setup_framebuffers (StThemeNodeTransition *transition, priv->offscreen_box.y2, 0.0, 1.0); st_theme_node_paint (priv->old_theme_node, &priv->old_paint_state, - priv->old_offscreen, allocation, 255); + priv->old_offscreen, allocation, 255, resource_scale); cogl_framebuffer_clear4f (priv->new_offscreen, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 0); @@ -330,7 +333,7 @@ setup_framebuffers (StThemeNodeTransition *transition, priv->offscreen_box.x2, priv->offscreen_box.y2, 0.0, 1.0); st_theme_node_paint (priv->new_theme_node, &priv->new_paint_state, - priv->new_offscreen, allocation, 255); + priv->new_offscreen, allocation, 255, resource_scale); return TRUE; } @@ -339,7 +342,8 @@ void st_theme_node_transition_paint (StThemeNodeTransition *transition, CoglFramebuffer *framebuffer, ClutterActorBox *allocation, - guint8 paint_opacity) + guint8 paint_opacity, + float resource_scale) { StThemeNodeTransitionPrivate *priv = transition->priv; @@ -360,7 +364,8 @@ st_theme_node_transition_paint (StThemeNodeTransition *transition, priv->last_allocation = *allocation; calculate_offscreen_box (transition, allocation); - priv->needs_setup = !setup_framebuffers (transition, allocation); + priv->needs_setup = !setup_framebuffers (transition, allocation, + resource_scale); if (priv->needs_setup) /* setting up framebuffers failed */ return; diff --git a/src/st/st-theme-node-transition.h b/src/st/st-theme-node-transition.h index 6f45ab8348..61f82d295f 100644 --- a/src/st/st-theme-node-transition.h +++ b/src/st/st-theme-node-transition.h @@ -43,7 +43,8 @@ void st_theme_node_transition_update (StThemeNodeTransition *transition, void st_theme_node_transition_paint (StThemeNodeTransition *transition, CoglFramebuffer *framebuffer, ClutterActorBox *allocation, - guint8 paint_opacity); + guint8 paint_opacity, + float resource_scale); void st_theme_node_transition_get_paint_box (StThemeNodeTransition *transition, const ClutterActorBox *allocation, diff --git a/src/st/st-theme-node.h b/src/st/st-theme-node.h index 22f8b430b3..53c0ad05d7 100644 --- a/src/st/st-theme-node.h +++ b/src/st/st-theme-node.h @@ -104,6 +104,8 @@ struct _StThemeNodePaintState { float box_shadow_width; float box_shadow_height; + float resource_scale; + CoglPipeline *box_shadow_pipeline; CoglPipeline *prerendered_texture; CoglPipeline *prerendered_pipeline; @@ -279,7 +281,8 @@ void st_theme_node_paint (StThemeNode *node, StThemeNodePaintState *state, CoglFramebuffer *framebuffer, const ClutterActorBox *box, - guint8 paint_opacity); + guint8 paint_opacity, + float resource_scale); void st_theme_node_invalidate_background_image (StThemeNode *node); void st_theme_node_invalidate_border_image (StThemeNode *node); diff --git a/src/st/st-widget.c b/src/st/st-widget.c index 78053804b2..eea5f7f3ba 100644 --- a/src/st/st-widget.c +++ b/src/st/st-widget.c @@ -418,8 +418,12 @@ st_widget_paint_background (StWidget *widget) CoglFramebuffer *framebuffer; StThemeNode *theme_node; ClutterActorBox allocation; + float resource_scale; guint8 opacity; + if (!st_widget_get_resource_scale (widget, &resource_scale)) + return; + framebuffer = cogl_get_draw_framebuffer (); theme_node = st_widget_get_theme_node (widget); @@ -431,13 +435,15 @@ st_widget_paint_background (StWidget *widget) st_theme_node_transition_paint (priv->transition_animation, framebuffer, &allocation, - opacity); + opacity, + resource_scale); else st_theme_node_paint (theme_node, current_paint_state (widget), framebuffer, &allocation, - opacity); + opacity, + resource_scale); } static void @@ -1495,7 +1501,16 @@ st_widget_resource_scale_notify (StWidget *widget, GParamSpec *pspec, gpointer data) { + StWidgetPrivate *priv = st_widget_get_instance_private (widget); + int i; + + for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++) + st_theme_node_paint_state_invalidate (&priv->paint_states[i]); + g_signal_emit (widget, signals[RESOURCE_SCALE_CHANGED], 0); + + if (clutter_actor_is_mapped (CLUTTER_ACTOR (widget))) + clutter_actor_queue_redraw (CLUTTER_ACTOR (widget)); } static void -- GitLab From ca4d86e9e549982761d07a3e48320f20e2383343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 25 Feb 2019 15:53:25 +0100 Subject: [PATCH 08/32] st-shadow: Define autoptr cleanup function for StShadow https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 --- src/st/st-shadow.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/st/st-shadow.h b/src/st/st-shadow.h index 8173f34f48..2870d41dd4 100644 --- a/src/st/st-shadow.h +++ b/src/st/st-shadow.h @@ -72,6 +72,8 @@ void st_shadow_get_box (StShadow *shadow, const ClutterActorBox *actor_box, ClutterActorBox *shadow_box); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (StShadow, st_shadow_unref); + GType st_shadow_helper_get_type (void) G_GNUC_CONST; -- GitLab From 5617ffc79ceb3acb291332879cbbe161cea0a316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 25 Feb 2019 13:45:02 +0100 Subject: [PATCH 09/32] 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 --- src/st/st-private.c | 39 ++++++++++++++++++++++++++++++++-- src/st/st-theme-node-drawing.c | 19 +++++++++++++---- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/st/st-private.c b/src/st/st-private.c index 688b17b785..a74368709c 100644 --- a/src/st/st-private.c +++ b/src/st/st-private.c @@ -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); diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c index a94253f4ed..7db928a0d7 100644 --- a/src/st/st-theme-node-drawing.c +++ b/src/st/st-theme-node-drawing.c @@ -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); -- GitLab From 37f53a42da12ece4b0024b2227a5d6dcd82b3cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 25 Feb 2019 15:42:51 +0100 Subject: [PATCH 10/32] st: Create shadow pipeline taking resource scale in account The shadow pipeline is created doing software blurring of the texture so the shadow spec blur parameter should be scaled accordingly with the texture scaling otherwise we won't take enough pixels in account creating stronger shadows. https://bugzilla.gnome.org/show_bug.cgi?id=765011 https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 --- src/st/st-private.c | 11 +++++++---- src/st/st-private.h | 3 ++- src/st/st-theme-node-drawing.c | 14 +++++++++----- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/st/st-private.c b/src/st/st-private.c index a74368709c..895e33b1e2 100644 --- a/src/st/st-private.c +++ b/src/st/st-private.c @@ -359,7 +359,8 @@ blur_pixels (guchar *pixels_in, CoglPipeline * _st_create_shadow_pipeline (StShadow *shadow_spec, - CoglTexture *src_texture) + CoglTexture *src_texture, + float resource_scale) { ClutterBackend *backend = clutter_get_default_backend (); CoglContext *ctx = clutter_backend_get_cogl_context (backend); @@ -386,7 +387,7 @@ _st_create_shadow_pipeline (StShadow *shadow_spec, rowstride_in, pixels_in); pixels_out = blur_pixels (pixels_in, width_in, height_in, rowstride_in, - shadow_spec->blur, + shadow_spec->blur * resource_scale, &width_out, &height_out, &rowstride_out); g_free (pixels_in); @@ -456,7 +457,8 @@ _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec, if (texture && cogl_texture_get_width (texture) == width && cogl_texture_get_height (texture) == height) - shadow_pipeline = _st_create_shadow_pipeline (shadow_spec, texture); + shadow_pipeline = _st_create_shadow_pipeline (shadow_spec, texture, + resource_scale); } if (shadow_pipeline == NULL) @@ -510,7 +512,8 @@ _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec, cogl_object_unref (fb); - shadow_pipeline = _st_create_shadow_pipeline (shadow_spec, buffer); + shadow_pipeline = _st_create_shadow_pipeline (shadow_spec, buffer, + resource_scale); cogl_object_unref (buffer); } diff --git a/src/st/st-private.h b/src/st/st-private.h index f88f930094..0f3cb2f2c6 100644 --- a/src/st/st-private.h +++ b/src/st/st-private.h @@ -63,7 +63,8 @@ CoglPipeline * _st_create_texture_pipeline (CoglTexture *src_texture); /* Helper for widgets which need to draw additional shadows */ CoglPipeline * _st_create_shadow_pipeline (StShadow *shadow_spec, - CoglTexture *src_texture); + CoglTexture *src_texture, + float resource_scale); CoglPipeline * _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec, ClutterActor *actor); cairo_pattern_t *_st_create_shadow_cairo_pattern (StShadow *shadow_spec, diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c index 7db928a0d7..3aaba3424e 100644 --- a/src/st/st-theme-node-drawing.c +++ b/src/st/st-theme-node-drawing.c @@ -1422,7 +1422,8 @@ st_theme_node_load_background_image (StThemeNode *node) if (background_image_shadow_spec) { node->background_shadow_pipeline = _st_create_shadow_pipeline (background_image_shadow_spec, - node->background_texture); + node->background_texture, + resource_scale); } } @@ -1561,10 +1562,12 @@ st_theme_node_render_resources (StThemeNodePaintState *state, { if (st_theme_node_load_border_image (node)) state->box_shadow_pipeline = _st_create_shadow_pipeline (box_shadow_spec, - node->border_slices_texture); + node->border_slices_texture, + state->resource_scale); else if (state->prerendered_texture != NULL) state->box_shadow_pipeline = _st_create_shadow_pipeline (box_shadow_spec, - state->prerendered_texture); + state->prerendered_texture, + state->resource_scale); else if (node->background_color.alpha > 0 || has_border) st_theme_node_prerender_shadow (state); } @@ -1636,7 +1639,8 @@ st_theme_node_update_resources (StThemeNodePaintState *state, if (had_box_shadow) state->box_shadow_pipeline = _st_create_shadow_pipeline (box_shadow_spec, - state->prerendered_texture); + state->prerendered_texture, + state->resource_scale); } static void @@ -2330,7 +2334,7 @@ st_theme_node_prerender_shadow (StThemeNodePaintState *state) st_theme_node_paint_borders (state, offscreen, &box, 0xFF); state->box_shadow_pipeline = _st_create_shadow_pipeline (st_theme_node_get_box_shadow (node), - buffer); + buffer, state->resource_scale); } else { -- GitLab From 420697693b2f659fd7faccdc5c675f25494f00e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 11 May 2017 06:25:00 +0200 Subject: [PATCH 11/32] st-texture-cache: Separate 'scale' to 'paint_scale' and 'resource_scale' Instead of just passing a scale when getting a cached icon, pass both a 'paint_scale', the scale of which the icon will be painted on the stage, and a 'resource_scale', the scale of the resource used for painting. In effect, the texture size will use the scale 'paint_scale * resource_scale' in a ceiled value while the size of the actor will use 'paint_scale' when determining the size. this would load a bigger texture, but the downscaling would keep the visual quality. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- js/gdm/loginDialog.js | 3 +- src/st/st-icon.c | 6 +- src/st/st-texture-cache.c | 112 ++++++++++++++++++++++----------- src/st/st-texture-cache.h | 15 +++-- src/st/st-theme-node-drawing.c | 37 +++++++---- 5 files changed, 117 insertions(+), 56 deletions(-) diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js index 8ea1a86a23..bf7577c360 100644 --- a/js/gdm/loginDialog.js +++ b/js/gdm/loginDialog.js @@ -782,7 +782,8 @@ var LoginDialog = GObject.registerClass({ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; this._logoBin.add_child(this._textureCache.load_file_async(this._logoFile, -1, _LOGO_ICON_HEIGHT, - scaleFactor)); + scaleFactor, + 1)); } } diff --git a/src/st/st-icon.c b/src/st/st-icon.c index b9c92c86ca..9242d6b4e9 100644 --- a/src/st/st-icon.c +++ b/src/st/st-icon.c @@ -393,14 +393,16 @@ st_icon_update (StIcon *icon) theme_node, priv->gicon, priv->icon_size, - scale); + scale, + 1); if (priv->pending_texture == NULL && priv->fallback_gicon != NULL) priv->pending_texture = st_texture_cache_load_gicon (cache, theme_node, priv->fallback_gicon, priv->icon_size, - scale); + scale, + 1); if (priv->pending_texture) { diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c index 967ad9e2e1..0075845f89 100644 --- a/src/st/st-texture-cache.c +++ b/src/st/st-texture-cache.c @@ -25,6 +25,7 @@ #include "st-private.h" #include "st-settings.h" #include +#include #include #include @@ -284,7 +285,8 @@ typedef struct { guint width; guint height; - guint scale; + guint paint_scale; + gfloat resource_scale; GSList *actors; GtkIconInfo *icon_info; @@ -430,7 +432,8 @@ static GdkPixbuf * impl_load_pixbuf_file (GFile *file, int available_width, int available_height, - int scale, + int paint_scale, + float resource_scale, GError **error) { GdkPixbuf *pixbuf = NULL; @@ -439,6 +442,7 @@ impl_load_pixbuf_file (GFile *file, if (g_file_load_contents (file, NULL, &contents, &size, NULL, error)) { + int scale = ceilf (paint_scale * resource_scale); pixbuf = impl_load_pixbuf_data ((const guchar *) contents, size, available_width, available_height, scale, @@ -463,7 +467,9 @@ load_pixbuf_thread (GTask *result, g_assert (data != NULL); g_assert (data->file != NULL); - pixbuf = impl_load_pixbuf_file (data->file, data->width, data->height, data->scale, &error); + pixbuf = impl_load_pixbuf_file (data->file, data->width, data->height, + data->paint_scale, data->resource_scale, + &error); if (error != NULL) g_task_return_error (result, error); @@ -869,7 +875,8 @@ ensure_request (StTextureCache *cache, * if the icon must not be recolored * @icon: the #GIcon to load * @size: Size of themed - * @scale: Scale factor of display + * @paint_scale: Scale factor of display + * @resource_scale: Resource scale factor * * This method returns a new #ClutterActor for a given #GIcon. If the * icon isn't loaded already, the texture will be filled @@ -882,12 +889,15 @@ st_texture_cache_load_gicon (StTextureCache *cache, StThemeNode *theme_node, GIcon *icon, gint size, - gint scale) + gint paint_scale, + gfloat resource_scale) { AsyncTextureLoadData *request; ClutterActor *actor; + gint scale; char *gicon_string; char *key; + float actor_size; GtkIconTheme *theme; GtkIconInfo *info; StTextureCachePolicy policy; @@ -916,7 +926,10 @@ st_texture_cache_load_gicon (StTextureCache *cache, else lookup_flags |= GTK_ICON_LOOKUP_DIR_LTR; - info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon, size, scale, lookup_flags); + scale = ceilf (paint_scale * resource_scale); + info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon, + size, scale, + lookup_flags); if (info == NULL) return NULL; @@ -945,8 +958,8 @@ st_texture_cache_load_gicon (StTextureCache *cache, g_free (gicon_string); actor = create_invisible_actor (); - clutter_actor_set_size (actor, size * scale, size * scale); - + actor_size = size * paint_scale; + clutter_actor_set_size (actor, actor_size, actor_size); if (ensure_request (cache, key, policy, &request, actor)) { /* If there's an outstanding request, we've just added ourselves to it */ @@ -964,7 +977,8 @@ st_texture_cache_load_gicon (StTextureCache *cache, request->colors = colors ? st_icon_colors_ref (colors) : NULL; request->icon_info = info; request->width = request->height = size; - request->scale = scale; + request->paint_scale = paint_scale; + request->resource_scale = resource_scale; load_texture_async (cache, request); } @@ -1041,7 +1055,8 @@ ensure_monitor_for_file (StTextureCache *cache, typedef struct { GFile *gfile; gint grid_width, grid_height; - gint scale_factor; + gint paint_scale; + gfloat resource_scale; ClutterActor *actor; GFunc load_callback; gpointer load_callback_data; @@ -1097,9 +1112,9 @@ on_loader_size_prepared (GdkPixbufLoader *loader, gpointer user_data) { AsyncImageData *data = user_data; - gdk_pixbuf_loader_set_size (loader, - width * data->scale_factor, - height * data->scale_factor); + int scale = ceilf (data->paint_scale * data->resource_scale); + + gdk_pixbuf_loader_set_size (loader, width * scale, height * scale); } static void @@ -1112,6 +1127,7 @@ load_sliced_image (GTask *result, GList *res = NULL; GdkPixbuf *pix; gint width, height, y, x; + gint scale_factor; GdkPixbufLoader *loader; GError *error = NULL; gchar *buffer = NULL; @@ -1143,13 +1159,14 @@ load_sliced_image (GTask *result, pix = gdk_pixbuf_loader_get_pixbuf (loader); width = gdk_pixbuf_get_width (pix); height = gdk_pixbuf_get_height (pix); - for (y = 0; y < height; y += data->grid_height * data->scale_factor) + scale_factor = ceilf (data->paint_scale * data->resource_scale); + for (y = 0; y < height; y += data->grid_height * scale_factor) { - for (x = 0; x < width; x += data->grid_width * data->scale_factor) + for (x = 0; x < width; x += data->grid_width * scale_factor) { GdkPixbuf *pixbuf = gdk_pixbuf_new_subpixbuf (pix, x, y, - data->grid_width * data->scale_factor, - data->grid_height * data->scale_factor); + data->grid_width * scale_factor, + data->grid_height * scale_factor); g_assert (pixbuf != NULL); res = g_list_append (res, pixbuf); } @@ -1170,13 +1187,13 @@ load_sliced_image (GTask *result, * @file: A #GFile * @grid_width: Width in pixels * @grid_height: Height in pixels - * @scale: Scale factor of the display + * @paint_scale: Scale factor of the display * @load_callback: (scope async) (nullable): Function called when the image is loaded, or %NULL * @user_data: Data to pass to the load callback * * This function reads a single image file which contains multiple images internally. * The image file will be divided using @grid_width and @grid_height; - * note that the dimensions of the image loaded from @path + * note that the dimensions of the image loaded from @path * should be a multiple of the specified grid dimensions. * * Returns: (transfer none): A new #ClutterActor @@ -1186,7 +1203,8 @@ st_texture_cache_load_sliced_image (StTextureCache *cache, GFile *file, gint grid_width, gint grid_height, - gint scale, + gint paint_scale, + gfloat resource_scale, GFunc load_callback, gpointer user_data) { @@ -1194,10 +1212,15 @@ st_texture_cache_load_sliced_image (StTextureCache *cache, GTask *result; ClutterActor *actor = clutter_actor_new (); + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_assert (paint_scale > 0); + g_assert (resource_scale > 0); + data = g_new0 (AsyncImageData, 1); data->grid_width = grid_width; data->grid_height = grid_height; - data->scale_factor = scale; + data->paint_scale = paint_scale; + data->resource_scale = resource_scale; data->gfile = g_object_ref (file); data->actor = actor; data->load_callback = load_callback; @@ -1219,7 +1242,8 @@ st_texture_cache_load_sliced_image (StTextureCache *cache, * @file: a #GFile of the image file from which to create a pixbuf * @available_width: available width for the image, can be -1 if not limited * @available_height: available height for the image, can be -1 if not limited - * @scale: scale factor of the display + * @paint_scale: scale factor of the display + * @resource_scale: Resource scale factor * * Asynchronously load an image. Initially, the returned texture will have a natural * size of zero. At some later point, either the image will be loaded successfully @@ -1232,14 +1256,17 @@ st_texture_cache_load_file_async (StTextureCache *cache, GFile *file, int available_width, int available_height, - int scale) + int paint_scale, + gfloat resource_scale) { ClutterActor *actor; AsyncTextureLoadData *request; StTextureCachePolicy policy; gchar *key; + int scale; - key = g_strdup_printf (CACHE_PREFIX_FILE "%u", g_file_hash (file)); + scale = ceilf (paint_scale * resource_scale); + key = g_strdup_printf (CACHE_PREFIX_FILE "%u%d", g_file_hash (file), scale); policy = ST_TEXTURE_CACHE_POLICY_NONE; /* XXX */ @@ -1261,7 +1288,8 @@ st_texture_cache_load_file_async (StTextureCache *cache, request->policy = policy; request->width = available_width; request->height = available_height; - request->scale = scale; + request->paint_scale = paint_scale; + request->resource_scale = resource_scale; load_texture_async (cache, request); } @@ -1277,7 +1305,8 @@ st_texture_cache_load_file_sync_to_cogl_texture (StTextureCache *cache, GFile *file, int available_width, int available_height, - int scale, + int paint_scale, + gfloat resource_scale, GError **error) { ClutterContent *image; @@ -1285,14 +1314,15 @@ st_texture_cache_load_file_sync_to_cogl_texture (StTextureCache *cache, GdkPixbuf *pixbuf; char *key; - key = g_strdup_printf (CACHE_PREFIX_FILE "%u", g_file_hash (file)); + key = g_strdup_printf (CACHE_PREFIX_FILE "%u%f", g_file_hash (file), resource_scale); texdata = NULL; image = g_hash_table_lookup (cache->priv->keyed_cache, key); if (image == NULL) { - pixbuf = impl_load_pixbuf_file (file, available_width, available_height, scale, error); + pixbuf = impl_load_pixbuf_file (file, available_width, available_height, + paint_scale, resource_scale, error); if (!pixbuf) goto out; @@ -1325,20 +1355,22 @@ st_texture_cache_load_file_sync_to_cairo_surface (StTextureCache *cache, GFile *file, int available_width, int available_height, - int scale, + int paint_scale, + gfloat resource_scale, GError **error) { cairo_surface_t *surface; GdkPixbuf *pixbuf; char *key; - key = g_strdup_printf (CACHE_PREFIX_FILE_FOR_CAIRO "%u", g_file_hash (file)); + key = g_strdup_printf (CACHE_PREFIX_FILE_FOR_CAIRO "%u%f", g_file_hash (file), resource_scale); surface = g_hash_table_lookup (cache->priv->keyed_surface_cache, key); if (surface == NULL) { - pixbuf = impl_load_pixbuf_file (file, available_width, available_height, scale, error); + pixbuf = impl_load_pixbuf_file (file, available_width, available_height, + paint_scale, resource_scale, error); if (!pixbuf) goto out; @@ -1366,7 +1398,8 @@ out: * st_texture_cache_load_file_to_cogl_texture: (skip) * @cache: A #StTextureCache * @file: A #GFile in supported image format - * @scale: Scale factor of the display + * @paint_scale: Scale factor of the display + * @resource_scale: Resource scale factor * * This function synchronously loads the given file path * into a COGL texture. On error, a warning is emitted @@ -1377,13 +1410,15 @@ out: CoglTexture * st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache, GFile *file, - gint scale) + gint paint_scale, + gfloat resource_scale) { CoglTexture *texture; GError *error = NULL; texture = st_texture_cache_load_file_sync_to_cogl_texture (cache, ST_TEXTURE_CACHE_POLICY_FOREVER, - file, -1, -1, scale, &error); + file, -1, -1, paint_scale, resource_scale, + &error); if (texture == NULL) { @@ -1400,7 +1435,8 @@ st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache, * st_texture_cache_load_file_to_cairo_surface: * @cache: A #StTextureCache * @file: A #GFile in supported image format - * @scale: Scale factor of the display + * @paint_scale: Scale factor of the display + * @resource_scale: Resource scale factor * * This function synchronously loads the given file path * into a cairo surface. On error, a warning is emitted @@ -1411,13 +1447,15 @@ st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache, cairo_surface_t * st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache, GFile *file, - gint scale) + gint paint_scale, + gfloat resource_scale) { cairo_surface_t *surface; GError *error = NULL; surface = st_texture_cache_load_file_sync_to_cairo_surface (cache, ST_TEXTURE_CACHE_POLICY_FOREVER, - file, -1, -1, scale, &error); + file, -1, -1, paint_scale, resource_scale, + &error); if (surface == NULL) { diff --git a/src/st/st-texture-cache.h b/src/st/st-texture-cache.h index 26f9c30ac1..0e0202c50a 100644 --- a/src/st/st-texture-cache.h +++ b/src/st/st-texture-cache.h @@ -58,7 +58,8 @@ st_texture_cache_load_sliced_image (StTextureCache *cache, GFile *file, gint grid_width, gint grid_height, - gint scale, + gint paint_scale, + gfloat resource_scale, GFunc load_callback, gpointer user_data); @@ -70,21 +71,25 @@ ClutterActor *st_texture_cache_load_gicon (StTextureCache *cache, StThemeNode *theme_node, GIcon *icon, gint size, - gint scale); + gint paint_scale, + gfloat resource_scale); ClutterActor *st_texture_cache_load_file_async (StTextureCache *cache, GFile *file, int available_width, int available_height, - int scale); + int paint_scale, + gfloat resource_scale); CoglTexture *st_texture_cache_load_file_to_cogl_texture (StTextureCache *cache, GFile *file, - gint scale); + gint paint_scale, + gfloat resource_scale); cairo_surface_t *st_texture_cache_load_file_to_cairo_surface (StTextureCache *cache, GFile *file, - gint scale); + gint paint_scale, + gfloat resource_scale); /** * StTextureCacheLoader: (skip) diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c index 3aaba3424e..9877afdae0 100644 --- a/src/st/st-theme-node-drawing.c +++ b/src/st/st-theme-node-drawing.c @@ -650,7 +650,9 @@ create_cairo_pattern_of_background_image (StThemeNode *node, texture_cache = st_texture_cache_get_default (); g_object_get (node->context, "scale-factor", &scale_factor, NULL); - surface = st_texture_cache_load_file_to_cairo_surface (texture_cache, file, scale_factor); + surface = st_texture_cache_load_file_to_cairo_surface (texture_cache, file, + scale_factor, + resource_scale); if (surface == NULL) return NULL; @@ -667,10 +669,18 @@ create_cairo_pattern_of_background_image (StThemeNode *node, cairo_matrix_init_identity (&matrix); + if (resource_scale != 1.0) + { + background_image_width /= resource_scale; + background_image_height /= resource_scale; + + cairo_matrix_scale (&matrix, resource_scale, resource_scale); + } + get_background_scale (node, width, height, background_image_width, background_image_height, - resource_scale, &scale_w, &scale_h); + &scale_w, &scale_h); if ((scale_w != 1) || (scale_h != 1)) cairo_matrix_scale (&matrix, 1.0/scale_w, 1.0/scale_h); @@ -1323,7 +1333,8 @@ st_theme_node_prerender_background (StThemeNode *node, if (interior_path != NULL) cairo_path_destroy (interior_path); - texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, texture_width, + texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, + texture_width, texture_height, CLUTTER_CAIRO_FORMAT_ARGB32, rowstride, @@ -1356,7 +1367,8 @@ st_theme_node_invalidate_border_image (StThemeNode *node) } static gboolean -st_theme_node_load_border_image (StThemeNode *node) +st_theme_node_load_border_image (StThemeNode *node, + gfloat resource_scale) { if (node->border_slices_texture == NULL) { @@ -1373,7 +1385,8 @@ st_theme_node_load_border_image (StThemeNode *node) g_object_get (node->context, "scale-factor", &scale_factor, NULL); node->border_slices_texture = st_texture_cache_load_file_to_cogl_texture (st_texture_cache_get_default (), - file, scale_factor); + file, scale_factor, + resource_scale); if (node->border_slices_texture == NULL) goto out; @@ -1393,7 +1406,8 @@ st_theme_node_invalidate_background_image (StThemeNode *node) } static gboolean -st_theme_node_load_background_image (StThemeNode *node) +st_theme_node_load_background_image (StThemeNode *node, + gfloat resource_scale) { if (node->background_texture == NULL) { @@ -1409,7 +1423,8 @@ st_theme_node_load_background_image (StThemeNode *node) background_image_shadow_spec = st_theme_node_get_background_image_shadow (node); node->background_texture = st_texture_cache_load_file_to_cogl_texture (st_texture_cache_get_default (), - background_image, scale_factor); + background_image, scale_factor, + resource_scale); if (node->background_texture == NULL) goto out; @@ -1560,7 +1575,7 @@ st_theme_node_render_resources (StThemeNodePaintState *state, if (box_shadow_spec && !has_inset_box_shadow) { - if (st_theme_node_load_border_image (node)) + if (st_theme_node_load_border_image (node, resource_scale)) state->box_shadow_pipeline = _st_create_shadow_pipeline (box_shadow_spec, node->border_slices_texture, state->resource_scale); @@ -1634,7 +1649,7 @@ st_theme_node_update_resources (StThemeNodePaintState *state, for (corner_id = 0; corner_id < 4; corner_id++) if (state->corner_material[corner_id] == NULL) state->corner_material[corner_id] = - st_theme_node_lookup_corner (node, width, resource_scale, height, corner_id); + st_theme_node_lookup_corner (node, width, height, resource_scale, corner_id); } if (had_box_shadow) @@ -2635,7 +2650,7 @@ st_theme_node_paint (StThemeNode *node, } if (state->prerendered_pipeline != NULL || - st_theme_node_load_border_image (node)) + st_theme_node_load_border_image (node, resource_scale)) { if (state->prerendered_pipeline != NULL) { @@ -2663,7 +2678,7 @@ st_theme_node_paint (StThemeNode *node, st_theme_node_paint_outline (node, framebuffer, box, paint_opacity); if (state->prerendered_pipeline == NULL && - st_theme_node_load_background_image (node)) + st_theme_node_load_background_image (node, resource_scale)) { ClutterActorBox background_box; ClutterActorBox texture_coords; -- GitLab From 0464361ca5470645995e3aeb9a113ac1d50e4b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Feb 2019 00:23:40 +0100 Subject: [PATCH 12/32] st-image-content: Wrap ClutterImage with explicit preferred size Create StImageContent as a simple ClutterImage with preferred width/height properties in order to be able to use explicit sizing when creating clutter contents that will be applied to actors whose size depends on the content itself. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 --- src/st/meson.build | 2 + src/st/st-image-content.c | 191 ++++++++++++++++++++++++++++++++++++++ src/st/st-image-content.h | 33 +++++++ 3 files changed, 226 insertions(+) create mode 100644 src/st/st-image-content.c create mode 100644 src/st/st-image-content.h diff --git a/src/st/meson.build b/src/st/meson.build index 21756eb07f..118d0fda04 100644 --- a/src/st/meson.build +++ b/src/st/meson.build @@ -12,6 +12,7 @@ st_headers = [ 'st-generic-accessible.h', 'st-icon.h', 'st-icon-colors.h', + 'st-image-content.h', 'st-label.h', 'st-private.h', 'st-scrollable.h', @@ -66,6 +67,7 @@ st_sources = [ 'st-generic-accessible.c', 'st-icon.c', 'st-icon-colors.c', + 'st-image-content.c', 'st-label.c', 'st-private.c', 'st-scrollable.c', diff --git a/src/st/st-image-content.c b/src/st/st-image-content.c new file mode 100644 index 0000000000..6195fdc8f1 --- /dev/null +++ b/src/st/st-image-content.c @@ -0,0 +1,191 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * st-image-content.h: A content image with scaling support + * + * Copyright 2019 Canonical, Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "st-image-content.h" + +struct _StImageContent +{ + /*< private >*/ + ClutterImage parent_instance; +}; + +typedef struct _StImageContentPrivate StImageContentPrivate; +struct _StImageContentPrivate +{ + int width; + int height; +}; + +enum +{ + PROP_0, + PROP_PREFERRED_WIDTH, + PROP_PREFERRED_HEIGHT, +}; + +static void clutter_content_interface_init (ClutterContentInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (StImageContent, st_image_content, CLUTTER_TYPE_IMAGE, + G_ADD_PRIVATE (StImageContent) + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT, + clutter_content_interface_init)) + +static void +st_image_content_init (StImageContent *self) +{ +} + +static void +st_image_content_constructed (GObject *object) +{ + StImageContent *self = ST_IMAGE_CONTENT (object); + StImageContentPrivate *priv = st_image_content_get_instance_private (self); + + if (priv->width < 0 || priv->height < 0) + g_warning ("StImageContent initialized with invalid preferred size: %dx%d\n", + priv->width, priv->height); +} + +static void +st_image_content_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + StImageContent *self = ST_IMAGE_CONTENT (object); + StImageContentPrivate *priv = st_image_content_get_instance_private (self); + + switch (prop_id) + { + case PROP_PREFERRED_WIDTH: + g_value_set_int (value, priv->width); + break; + + case PROP_PREFERRED_HEIGHT: + g_value_set_int (value, priv->height); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +st_image_content_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + StImageContent *self = ST_IMAGE_CONTENT (object); + StImageContentPrivate *priv = st_image_content_get_instance_private (self); + + switch (prop_id) + { + case PROP_PREFERRED_WIDTH: + priv->width = g_value_get_int (value); + break; + + case PROP_PREFERRED_HEIGHT: + priv->height = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +st_image_content_class_init (StImageContentClass *klass) +{ + GParamSpec *pspec; + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = st_image_content_constructed; + object_class->get_property = st_image_content_get_property; + object_class->set_property = st_image_content_set_property; + + pspec = g_param_spec_int ("preferred-width", + "Preferred Width", + "Preferred Width of the Content when painted", + -1, G_MAXINT, -1, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_PREFERRED_WIDTH, pspec); + + pspec = g_param_spec_int ("preferred-height", + "Preferred Height", + "Preferred Height of the Content when painted", + -1, G_MAXINT, -1, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_PREFERRED_HEIGHT, pspec); +} + +static gboolean +st_image_content_get_preferred_size (ClutterContent *content, + float *width, + float *height) +{ + StImageContent *self = ST_IMAGE_CONTENT (content); + StImageContentPrivate *priv = st_image_content_get_instance_private (self); + ClutterTexture *texture; + + texture = clutter_image_get_texture (CLUTTER_IMAGE (content)); + + if (texture == NULL) + return FALSE; + + g_assert_cmpint (priv->width, >, -1); + g_assert_cmpint (priv->height, >, -1); + + if (width != NULL) + *width = (float) priv->width; + + if (height != NULL) + *height = (float) priv->height; + + return TRUE; +} + +static void +clutter_content_interface_init (ClutterContentInterface *iface) +{ + iface->get_preferred_size = st_image_content_get_preferred_size; +} + +/** + * st_image_content_new_with_preferred_size: + * @width: The preferred width to be used when drawing the content + * @height: The preferred width to be used when drawing the content + * + * Creates a new #StImageContent, a simple content for sized images. + * + * Return value: (transfer full): the newly created #StImageContent content + * Use g_object_unref() when done. + */ +ClutterContent * +st_image_content_new_with_preferred_size (int width, + int height) +{ + return g_object_new (ST_TYPE_IMAGE_CONTENT, + "preferred-width", width, + "preferred-height", height, + NULL); +} diff --git a/src/st/st-image-content.h b/src/st/st-image-content.h new file mode 100644 index 0000000000..0ebb0b74ff --- /dev/null +++ b/src/st/st-image-content.h @@ -0,0 +1,33 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * st-image-content.h: A content image with scaling support + * + * Copyright 2019 Canonical, Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef __ST_IMAGE_CONTENT_H__ +#define __ST_IMAGE_CONTENT_H__ + +#include + +#define ST_TYPE_IMAGE_CONTENT (st_image_content_get_type ()) +G_DECLARE_FINAL_TYPE (StImageContent, st_image_content, + ST, IMAGE_CONTENT, ClutterImage) + +ClutterContent *st_image_content_new_with_preferred_size (int width, + int height); + +#endif /* __ST_IMAGE_CONTENT_H__ */ -- GitLab From aaf69b28989f3206779617e4e0761bb8ef71af9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Feb 2019 03:18:39 +0100 Subject: [PATCH 13/32] st-texture-cache: Use StContentImage to create actor contents This will allow to use resource-scale aware content that will use a preferred size that takes in account the icon scaling and the resource scaling. If no size is passed we should just divide the actual resource size by the resource scale, as we assume that it's loaded with such scaling applied. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- src/st/st-texture-cache.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c index 0075845f89..4021307706 100644 --- a/src/st/st-texture-cache.c +++ b/src/st/st-texture-cache.c @@ -21,6 +21,7 @@ #include "config.h" +#include "st-image-content.h" #include "st-texture-cache.h" #include "st-private.h" #include "st-settings.h" @@ -486,12 +487,26 @@ load_pixbuf_async_finish (StTextureCache *cache, GAsyncResult *result, GError ** } static ClutterContent * -pixbuf_to_clutter_image (GdkPixbuf *pixbuf) +pixbuf_to_st_content_image (GdkPixbuf *pixbuf, + int width, + int height, + int paint_scale, + float resource_scale) { ClutterContent *image; g_autoptr(GError) error = NULL; - image = clutter_image_new (); + if (width < 0) + width = ceilf (gdk_pixbuf_get_width (pixbuf) / resource_scale); + else + width *= paint_scale; + + if (height < 0) + height = ceilf (gdk_pixbuf_get_width (pixbuf) / resource_scale); + else + height *= paint_scale; + + image = st_image_content_new_with_preferred_size (width, height); clutter_image_set_data (CLUTTER_IMAGE (image), gdk_pixbuf_get_pixels (pixbuf), gdk_pixbuf_get_has_alpha (pixbuf) ? @@ -553,7 +568,10 @@ finish_texture_load (AsyncTextureLoadData *data, if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, data->key, &orig_key, &value)) { - image = pixbuf_to_clutter_image (pixbuf); + image = pixbuf_to_st_content_image (pixbuf, + data->width, data->height, + data->paint_scale, + data->resource_scale); if (!image) goto out; @@ -567,7 +585,10 @@ finish_texture_load (AsyncTextureLoadData *data, } else { - image = pixbuf_to_clutter_image (pixbuf); + image = pixbuf_to_st_content_image (pixbuf, + data->width, data->height, + data->paint_scale, + data->resource_scale); if (!image) goto out; } @@ -994,7 +1015,7 @@ load_from_pixbuf (GdkPixbuf *pixbuf) int width = gdk_pixbuf_get_width (pixbuf); int height = gdk_pixbuf_get_height (pixbuf); - image = pixbuf_to_clutter_image (pixbuf); + image = pixbuf_to_st_content_image (pixbuf, width, height, 1, 1.0f); actor = clutter_actor_new (); clutter_actor_set_size (actor, width, height); @@ -1326,7 +1347,9 @@ st_texture_cache_load_file_sync_to_cogl_texture (StTextureCache *cache, if (!pixbuf) goto out; - image = pixbuf_to_clutter_image (pixbuf); + image = pixbuf_to_st_content_image (pixbuf, + available_height, available_width, + paint_scale, resource_scale); g_object_unref (pixbuf); if (!image) -- GitLab From 40e7638a4b12ef3c68e87d20963e40fccaedfe14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 11 May 2017 06:35:09 +0200 Subject: [PATCH 14/32] st-icon: Add resource-scale support This commit adds support for scaling the StIcon resource (the icon texture) according to the resource scale. https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- src/st/st-icon.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/st/st-icon.c b/src/st/st-icon.c index 9242d6b4e9..04d2d1d3cc 100644 --- a/src/st/st-icon.c +++ b/src/st/st-icon.c @@ -210,6 +210,12 @@ st_icon_style_changed (StWidget *widget) st_icon_update (self); } +static void +st_icon_resource_scale_changed (StWidget *widget) +{ + st_icon_update (ST_ICON (widget)); +} + static void st_icon_class_init (StIconClass *klass) { @@ -226,6 +232,7 @@ st_icon_class_init (StIconClass *klass) actor_class->paint = st_icon_paint; widget_class->style_changed = st_icon_style_changed; + widget_class->resource_scale_changed = st_icon_resource_scale_changed; pspec = g_param_spec_object ("gicon", "GIcon", @@ -344,6 +351,8 @@ st_icon_finish_update (StIcon *icon) g_signal_connect_object (priv->icon_texture, "notify::content", G_CALLBACK (on_content_changed), icon, 0); } + + clutter_actor_queue_relayout (CLUTTER_ACTOR (icon)); } static void @@ -366,9 +375,10 @@ st_icon_update (StIcon *icon) StIconPrivate *priv = icon->priv; StThemeNode *theme_node; StTextureCache *cache; - gint scale; + gint paint_scale; ClutterActor *stage; StThemeContext *context; + float resource_scale; if (priv->pending_texture) { @@ -378,13 +388,16 @@ st_icon_update (StIcon *icon) priv->opacity_handler_id = 0; } + if (!st_widget_get_resource_scale (ST_WIDGET (icon), &resource_scale)) + return; + theme_node = st_widget_peek_theme_node (ST_WIDGET (icon)); if (theme_node == NULL) return; stage = clutter_actor_get_stage (CLUTTER_ACTOR (icon)); context = st_theme_context_get_for_stage (CLUTTER_STAGE (stage)); - g_object_get (context, "scale-factor", &scale, NULL); + g_object_get (context, "scale-factor", &paint_scale, NULL); cache = st_texture_cache_get_default (); @@ -393,16 +406,16 @@ st_icon_update (StIcon *icon) theme_node, priv->gicon, priv->icon_size, - scale, - 1); + paint_scale, + resource_scale); if (priv->pending_texture == NULL && priv->fallback_gicon != NULL) priv->pending_texture = st_texture_cache_load_gicon (cache, theme_node, priv->fallback_gicon, priv->icon_size, - scale, - 1); + paint_scale, + resource_scale); if (priv->pending_texture) { -- GitLab From ef1697d00d83116572c0e71f955f3136041d6f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 25 Feb 2019 21:26:46 +0100 Subject: [PATCH 15/32] st-texture-cache: Set sliced actor size in logical size The actor size should be set in logical size more than in the actual size of the texture when using resource scale. https://bugzilla.gnome.org/show_bug.cgi?id=765011 https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 --- src/st/st-texture-cache.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c index 4021307706..b84f3ffdcf 100644 --- a/src/st/st-texture-cache.c +++ b/src/st/st-texture-cache.c @@ -1008,14 +1008,16 @@ st_texture_cache_load_gicon (StTextureCache *cache, } static ClutterActor * -load_from_pixbuf (GdkPixbuf *pixbuf) +load_from_pixbuf (GdkPixbuf *pixbuf, + int paint_scale, + float resource_scale) { g_autoptr(ClutterContent) image = NULL; ClutterActor *actor; int width = gdk_pixbuf_get_width (pixbuf); int height = gdk_pixbuf_get_height (pixbuf); - image = pixbuf_to_st_content_image (pixbuf, width, height, 1, 1.0f); + image = pixbuf_to_st_content_image (pixbuf, -1, -1, paint_scale, resource_scale); actor = clutter_actor_new (); clutter_actor_set_size (actor, width, height); @@ -1109,7 +1111,9 @@ on_sliced_image_loaded (GObject *source_object, for (list = pixbufs; list; list = list->next) { - ClutterActor *actor = load_from_pixbuf (GDK_PIXBUF (list->data)); + ClutterActor *actor = load_from_pixbuf (GDK_PIXBUF (list->data), + data->paint_scale, + data->resource_scale); clutter_actor_hide (actor); clutter_actor_add_child (data->actor, actor); } -- GitLab From 2c17c186b8cd14592bb05e2ce4e4013ffbc63995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Feb 2019 03:31:19 +0100 Subject: [PATCH 16/32] st-texture-cache: Load sliced contents with REQUEST_CONTENTENT_SIZE When loading an actor for a sliced image actor, we can now use the REQUEST_CONTENT_SIZE request-mode for the actor since we the content image has now a predictable size and thus we can be sure that the size will be applied taking care of the resource scale. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 --- src/st/st-texture-cache.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c index b84f3ffdcf..90b85d0bd3 100644 --- a/src/st/st-texture-cache.c +++ b/src/st/st-texture-cache.c @@ -1014,13 +1014,12 @@ load_from_pixbuf (GdkPixbuf *pixbuf, { g_autoptr(ClutterContent) image = NULL; ClutterActor *actor; - int width = gdk_pixbuf_get_width (pixbuf); - int height = gdk_pixbuf_get_height (pixbuf); image = pixbuf_to_st_content_image (pixbuf, -1, -1, paint_scale, resource_scale); - actor = clutter_actor_new (); - clutter_actor_set_size (actor, width, height); + actor = g_object_new (CLUTTER_TYPE_ACTOR, + "request-mode", CLUTTER_REQUEST_CONTENT_SIZE, + NULL); clutter_actor_set_content (actor, image); return actor; -- GitLab From e3c5c9a2e7b5d2aca275a064b40d38145a16af95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 30 Nov 2017 02:48:02 +0100 Subject: [PATCH 17/32] st-texture-cache: Cancel sliced image loading on target actor destroy It might happen that the target clutter actor that we return on call of st_texture_cache_load_sliced_image might be destroyed while the loading task is still running. To protect from this, let's connect to "destroy" signal and when this happens we use a cancellable to stop the task. This allows to safely reuse the return value of this function to cancel the execution and avoiding that load_callback is called even for a request that is not anymore under our control. https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- src/st/st-texture-cache.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c index 90b85d0bd3..ae5ac09cb4 100644 --- a/src/st/st-texture-cache.c +++ b/src/st/st-texture-cache.c @@ -1080,6 +1080,7 @@ typedef struct { gint paint_scale; gfloat resource_scale; ClutterActor *actor; + GCancellable *cancellable; GFunc load_callback; gpointer load_callback_data; } AsyncImageData; @@ -1090,9 +1091,20 @@ on_data_destroy (gpointer data) AsyncImageData *d = (AsyncImageData *)data; g_object_unref (d->gfile); g_object_unref (d->actor); + g_object_unref (d->cancellable); g_free (d); } +static void +on_sliced_image_actor_destroyed (ClutterActor *actor, + gpointer data) +{ + GTask *task = data; + GCancellable *cancellable = g_task_get_cancellable (task); + + g_cancellable_cancel (cancellable); +} + static void on_sliced_image_loaded (GObject *source_object, GAsyncResult *res, @@ -1103,7 +1115,7 @@ on_sliced_image_loaded (GObject *source_object, GTask *task = G_TASK (res); GList *list, *pixbufs; - if (g_task_had_error (task)) + if (g_task_had_error (task) || g_cancellable_is_cancelled (data->cancellable)) return; pixbufs = g_task_propagate_pointer (task, NULL); @@ -1119,6 +1131,10 @@ on_sliced_image_loaded (GObject *source_object, g_list_free_full (pixbufs, g_object_unref); + g_signal_handlers_disconnect_by_func (data->actor, + on_sliced_image_actor_destroyed, + task); + if (data->load_callback != NULL) data->load_callback (cache, data->load_callback_data); } @@ -1157,7 +1173,7 @@ load_sliced_image (GTask *result, gchar *buffer = NULL; gsize length; - g_assert (!cancellable); + g_assert (cancellable); data = task_data; g_assert (data); @@ -1165,7 +1181,7 @@ load_sliced_image (GTask *result, loader = gdk_pixbuf_loader_new (); g_signal_connect (loader, "size-prepared", G_CALLBACK (on_loader_size_prepared), data); - if (!g_file_load_contents (data->gfile, NULL, &buffer, &length, NULL, &error)) + if (!g_file_load_contents (data->gfile, cancellable, &buffer, &length, NULL, &error)) { g_warning ("Failed to open sliced image: %s", error->message); goto out; @@ -1235,6 +1251,7 @@ st_texture_cache_load_sliced_image (StTextureCache *cache, AsyncImageData *data; GTask *result; ClutterActor *actor = clutter_actor_new (); + GCancellable *cancellable = g_cancellable_new (); g_return_val_if_fail (G_IS_FILE (file), NULL); g_assert (paint_scale > 0); @@ -1247,11 +1264,16 @@ st_texture_cache_load_sliced_image (StTextureCache *cache, data->resource_scale = resource_scale; data->gfile = g_object_ref (file); data->actor = actor; + data->cancellable = cancellable; data->load_callback = load_callback; data->load_callback_data = user_data; g_object_ref (G_OBJECT (actor)); - result = g_task_new (cache, NULL, on_sliced_image_loaded, data); + result = g_task_new (cache, cancellable, on_sliced_image_loaded, data); + + g_signal_connect (actor, "destroy", + G_CALLBACK (on_sliced_image_actor_destroyed), result); + g_task_set_task_data (result, data, on_data_destroy); g_task_run_in_thread (result, load_sliced_image); -- GitLab From 19c60ff5c52fc866ad3c24c7bd6065e8b28df3e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 1 Mar 2019 06:56:26 +0100 Subject: [PATCH 18/32] st-texture-cache: use StImageContent for cairo bound surface https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 --- js/ui/ctrlAltTab.js | 4 +++- src/shell-app.c | 4 ++-- src/st/st-texture-cache.c | 10 +++++++--- src/st/st-texture-cache.h | 3 ++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/js/ui/ctrlAltTab.js b/js/ui/ctrlAltTab.js index 146f8ead9d..338080fc47 100644 --- a/js/ui/ctrlAltTab.js +++ b/js/ui/ctrlAltTab.js @@ -94,7 +94,9 @@ var CtrlAltTabManager = class CtrlAltTabManager { if (app) icon = app.create_icon_texture(POPUP_APPICON_SIZE); else - icon = textureCache.bind_cairo_surface_property(windows[i], 'icon'); + icon = textureCache.bind_cairo_surface_property(windows[i], + 'icon', + POPUP_APPICON_SIZE); } items.push({ name: windows[i].title, diff --git a/src/shell-app.c b/src/shell-app.c index 5a0f81d52c..3f91613fab 100644 --- a/src/shell-app.c +++ b/src/shell-app.c @@ -211,8 +211,8 @@ window_backed_app_get_icon (ShellApp *app, actor = st_texture_cache_bind_cairo_surface_property (st_texture_cache_get_default (), G_OBJECT (window), - "icon"); - g_object_set (actor, "width", (float) size, "height", (float) size, NULL); + "icon", + size); return actor; } diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c index ae5ac09cb4..6da186fba4 100644 --- a/src/st/st-texture-cache.c +++ b/src/st/st-texture-cache.c @@ -679,6 +679,7 @@ load_texture_async (StTextureCache *cache, typedef struct { StTextureCache *cache; ClutterActor *actor; + gint size; GObject *source; guint notify_signal_id; gboolean weakref_active; @@ -702,7 +703,7 @@ st_texture_cache_reset_texture (StTextureCachePropertyBind *bind, image = clutter_actor_get_content (bind->actor); if (!image || !CLUTTER_IS_IMAGE (image)) - image = clutter_image_new (); + image = st_image_content_new_with_preferred_size (bind->size, bind->size); else g_object_ref (image); @@ -771,17 +772,20 @@ st_texture_cache_free_bind (gpointer data) ClutterActor * st_texture_cache_bind_cairo_surface_property (StTextureCache *cache, GObject *object, - const char *property_name) + const char *property_name, + gint size) { ClutterActor *actor; gchar *notify_key; StTextureCachePropertyBind *bind; - actor = clutter_actor_new (); + actor = create_invisible_actor (); + clutter_actor_set_size (actor, size, size); bind = g_new0 (StTextureCachePropertyBind, 1); bind->cache = cache; bind->actor = actor; + bind->size = size; bind->source = object; g_object_weak_ref (G_OBJECT (actor), st_texture_cache_bind_weak_notify, bind); bind->weakref_active = TRUE; diff --git a/src/st/st-texture-cache.h b/src/st/st-texture-cache.h index 0e0202c50a..a3311a8629 100644 --- a/src/st/st-texture-cache.h +++ b/src/st/st-texture-cache.h @@ -65,7 +65,8 @@ st_texture_cache_load_sliced_image (StTextureCache *cache, ClutterActor *st_texture_cache_bind_cairo_surface_property (StTextureCache *cache, GObject *object, - const char *property_name); + const char *property_name, + gint size); ClutterActor *st_texture_cache_load_gicon (StTextureCache *cache, StThemeNode *theme_node, -- GitLab From 2bda79cb3a057fded34e30206b04dd8fa3703876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Feb 2019 03:48:43 +0100 Subject: [PATCH 19/32] st-texture-cache: Use GSlice to manage memory of leaders data This allows some memory optimizations when possible, so let's use it. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 --- src/st/st-texture-cache.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c index 6da186fba4..a9688ec60b 100644 --- a/src/st/st-texture-cache.c +++ b/src/st/st-texture-cache.c @@ -315,7 +315,7 @@ texture_load_data_free (gpointer p) if (data->actors) g_slist_free_full (data->actors, (GDestroyNotify) g_object_unref); - g_free (data); + g_slice_free (AsyncTextureLoadData, data); } /** @@ -751,7 +751,7 @@ st_texture_cache_free_bind (gpointer data) StTextureCachePropertyBind *bind = data; if (bind->weakref_active) g_object_weak_unref (G_OBJECT (bind->actor), st_texture_cache_bind_weak_notify, bind); - g_free (bind); + g_slice_free (StTextureCachePropertyBind, bind); } /** @@ -782,7 +782,7 @@ st_texture_cache_bind_cairo_surface_property (StTextureCache *cache, actor = create_invisible_actor (); clutter_actor_set_size (actor, size, size); - bind = g_new0 (StTextureCachePropertyBind, 1); + bind = g_slice_new0 (StTextureCachePropertyBind); bind->cache = cache; bind->actor = actor; bind->size = size; @@ -880,7 +880,7 @@ ensure_request (StTextureCache *cache, if (pending == NULL) { /* Not cached and no pending request, create it */ - *request = g_new0 (AsyncTextureLoadData, 1); + *request = g_slice_new0 (AsyncTextureLoadData); if (policy != ST_TEXTURE_CACHE_POLICY_NONE) g_hash_table_insert (cache->priv->outstanding_requests, g_strdup (key), *request); } @@ -1096,7 +1096,7 @@ on_data_destroy (gpointer data) g_object_unref (d->gfile); g_object_unref (d->actor); g_object_unref (d->cancellable); - g_free (d); + g_slice_free (AsyncImageData, d); } static void @@ -1261,7 +1261,7 @@ st_texture_cache_load_sliced_image (StTextureCache *cache, g_assert (paint_scale > 0); g_assert (resource_scale > 0); - data = g_new0 (AsyncImageData, 1); + data = g_slice_new0 (AsyncImageData); data->grid_width = grid_width; data->grid_height = grid_height; data->paint_scale = paint_scale; -- GitLab From 26e3ccda496cb62f0b4e3b6e1edbfbb0f738eac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 30 Nov 2017 02:34:42 +0100 Subject: [PATCH 20/32] loginDialog: Load logo file texture given resource scale And reload the logo if the resource scale changes. https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- js/gdm/loginDialog.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js index bf7577c360..6c4d1357d0 100644 --- a/js/gdm/loginDialog.js +++ b/js/gdm/loginDialog.js @@ -481,6 +481,9 @@ var LoginDialog = GObject.registerClass({ this._logoBin = new St.Widget({ style_class: 'login-dialog-logo-bin', x_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.END }); + this._logoBin.connect('resource-scale-changed', () => { + this._updateLogoTexture(this._textureCache, this._logoFile); + }); this.add_child(this._logoBin); this._updateLogo(); @@ -778,12 +781,12 @@ var LoginDialog = GObject.registerClass({ return; this._logoBin.destroy_all_children(); - if (this._logoFile) { + if (this._logoFile && this._logoBin.resource_scale > 0) { let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; this._logoBin.add_child(this._textureCache.load_file_async(this._logoFile, -1, _LOGO_ICON_HEIGHT, scaleFactor, - 1)); + this._logoBin.resource_scale)); } } -- GitLab From b6ec02cef249ad6b0d52e80f7df3d3ee862515b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 30 Nov 2017 02:36:05 +0100 Subject: [PATCH 21/32] animation: Load sliced image using resource scale, and reload on change Also make sure that the textures size is matching the container size. https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- js/ui/animation.js | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/js/ui/animation.js b/js/ui/animation.js index 470a6ffbbf..0d711567fd 100644 --- a/js/ui/animation.js +++ b/js/ui/animation.js @@ -12,7 +12,12 @@ var SPINNER_ANIMATION_DELAY = 1.0; var Animation = class { constructor(file, width, height, speed) { this.actor = new St.Bin(); + this.actor.set_size(width, height); this.actor.connect('destroy', this._onDestroy.bind(this)); + this.actor.connect('notify::size', this._syncAnimationSize.bind(this)); + this.actor.connect('resource-scale-changed', + this._loadFile.bind(this, file, width, height)); + this._speed = speed; this._isLoaded = false; @@ -20,10 +25,7 @@ var Animation = class { this._timeoutId = 0; this._frame = 0; - let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; - this._animations = St.TextureCache.get_default().load_sliced_image (file, width, height, scaleFactor, - this._animationsLoaded.bind(this)); - this.actor.set_child(this._animations); + this._loadFile(file, width, height); } play() { @@ -47,6 +49,23 @@ var Animation = class { this._isPlaying = false; } + _loadFile(file, width, height) { + let [validResourceScale, resourceScale] = this.actor.get_resource_scale(); + + this._isLoaded = false; + this.actor.destroy_all_children(); + + if (!validResourceScale) + return; + + let texture_cache = St.TextureCache.get_default(); + let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; + this._animations = texture_cache.load_sliced_image(file, width, height, + scaleFactor, resourceScale, + this._animationsLoaded.bind(this)); + this.actor.set_child(this._animations); + } + _showFrame(frame) { let oldFrameActor = this._animations.get_child_at_index(this._frame); if (oldFrameActor) @@ -64,9 +83,21 @@ var Animation = class { return GLib.SOURCE_CONTINUE; } + _syncAnimationSize() { + if (!this._isLoaded) + return; + + let [width, height] = this.actor.get_size(); + + for (let i = 0; i < this._animations.get_n_children(); ++i) + this._animations.get_child_at_index(i).set_size(width, height); + } + _animationsLoaded() { this._isLoaded = this._animations.get_n_children() > 0; + this._syncAnimationSize(); + if (this._isPlaying) this.play(); } -- GitLab From 9e881ab6371fd57cb9cafe5597c59c2e8484b1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 27 Feb 2019 02:08:26 +0100 Subject: [PATCH 22/32] animation: Reload sliced texture on global scale change When the scale has changed we need to reload the texture at proper size. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 --- js/ui/animation.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/js/ui/animation.js b/js/ui/animation.js index 0d711567fd..c21b225650 100644 --- a/js/ui/animation.js +++ b/js/ui/animation.js @@ -18,6 +18,10 @@ var Animation = class { this.actor.connect('resource-scale-changed', this._loadFile.bind(this, file, width, height)); + let themeContext = St.ThemeContext.get_for_stage(global.stage); + this._scaleChangedId = themeContext.connect('notify::scale-factor', + this._loadFile.bind(this, file, width, height)); + this._speed = speed; this._isLoaded = false; @@ -104,6 +108,11 @@ var Animation = class { _onDestroy() { this.stop(); + + let themeContext = St.ThemeContext.get_for_stage(global.stage); + if (this._scaleChangedId) + themeContext.disconnect(this._scaleChangedId); + this._scaleChangedId = 0; } }; -- GitLab From deead2af97fd2c8e2fc154ba5a57c091d6213b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 3 Aug 2017 15:51:42 +0200 Subject: [PATCH 23/32] layout: Add scale property to Monitor https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- js/ui/layout.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/js/ui/layout.js b/js/ui/layout.js index c25cd474d8..b6cf05c13c 100644 --- a/js/ui/layout.js +++ b/js/ui/layout.js @@ -146,12 +146,13 @@ var MonitorConstraint = GObject.registerClass({ }); var Monitor = class Monitor { - constructor(index, geometry) { + constructor(index, geometry, geometry_scale) { this.index = index; this.x = geometry.x; this.y = geometry.y; this.width = geometry.width; this.height = geometry.height; + this.geometry_scale = geometry_scale; } get inFullscreen() { @@ -318,7 +319,9 @@ var LayoutManager = GObject.registerClass({ this.monitors = []; let nMonitors = display.get_n_monitors(); for (let i = 0; i < nMonitors; i++) - this.monitors.push(new Monitor(i, display.get_monitor_geometry(i))); + this.monitors.push(new Monitor(i, + display.get_monitor_geometry(i), + display.get_monitor_scale(i))); if (nMonitors == 0) { this.primaryIndex = this.bottomIndex = -1; -- GitLab From 8f1fff137410011cb799541349edf33736e1023f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 3 Aug 2017 15:52:06 +0200 Subject: [PATCH 24/32] background: Don't set background actor sizing and scaling Mutter BackgroundActor is able do detect this at lower level https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- js/ui/background.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/js/ui/background.js b/js/ui/background.js index bcd9ffc13e..06e0388167 100644 --- a/js/ui/background.js +++ b/js/ui/background.js @@ -746,10 +746,8 @@ var BackgroundManager = class BackgroundManager { this._container.add_child(backgroundActor); - let monitor = this._layoutManager.monitors[this._monitorIndex]; - - backgroundActor.set_size(monitor.width, monitor.height); if (this._controlPosition) { + let monitor = this._layoutManager.monitors[this._monitorIndex]; backgroundActor.set_position(monitor.x, monitor.y); backgroundActor.lower_bottom(); } -- GitLab From 4a4f75245960f43a2936c3687a1a78f0354a7c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Sat, 2 Sep 2017 04:04:22 +0200 Subject: [PATCH 25/32] dateMenu: Relayout IndicatorPad when parent size changes This happens often in resource-scaled world https://bugzilla.gnome.org/show_bug.cgi?id=765011 --- js/ui/dateMenu.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/ui/dateMenu.js b/js/ui/dateMenu.js index c29d33719d..697d6fbc8d 100644 --- a/js/ui/dateMenu.js +++ b/js/ui/dateMenu.js @@ -379,6 +379,7 @@ class IndicatorPad extends St.Widget { _init(actor) { this._source = actor; this._source.connect('notify::visible', () => { this.queue_relayout(); }); + this._source.connect('notify::size', () => { this.queue_relayout(); }); super._init(); } -- GitLab From 6d6c2e5b995e0c5e56be0c9fe39ab84298bc75f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Sat, 23 Dec 2017 04:54:48 +0100 Subject: [PATCH 26/32] shell-recorder: Use clutter-computed sizes and scaling for the capture Get from clutter the capture sizes and scale and don't mind about doing any rounding here, as it might be different from the one done at clutter level (causing mismatch and not-working videos). Delegate this to clutter, and forget about the internal details. These values are then used to composte the image and set the video caps. https://bugzilla.gnome.org/show_bug.cgi?id=7650111 --- src/shell-recorder.c | 34 +++++++++++++++++++++++++--------- src/shell-screenshot.c | 35 +++++++++++++++++++---------------- src/shell-util.c | 21 +++++---------------- src/shell-util.h | 5 +++-- 4 files changed, 52 insertions(+), 43 deletions(-) diff --git a/src/shell-recorder.c b/src/shell-recorder.c index 9bf36e6db4..c2e83412cd 100644 --- a/src/shell-recorder.c +++ b/src/shell-recorder.c @@ -51,6 +51,10 @@ struct _ShellRecorder { int stage_width; int stage_height; + int capture_width; + int capture_height; + float scale; + int pointer_x; int pointer_y; @@ -430,10 +434,8 @@ recorder_record_frame (ShellRecorder *recorder, return; recorder->last_frame_time = now; - clutter_stage_capture (recorder->stage, paint, &recorder->area, - &captures, &n_captures); - - if (n_captures == 0) + if (!clutter_stage_capture (recorder->stage, paint, &recorder->area, + &captures, &n_captures)) return; if (n_captures == 1) @@ -443,8 +445,9 @@ recorder_record_frame (ShellRecorder *recorder, n_captures, recorder->area.x, recorder->area.y, - recorder->area.width, - recorder->area.height); + recorder->capture_width, + recorder->capture_height, + recorder->scale); data = cairo_image_surface_get_data (image); size = (cairo_image_surface_get_height (image) * @@ -500,6 +503,11 @@ recorder_update_size (ShellRecorder *recorder) recorder->area.y = 0; recorder->area.width = recorder->stage_width; recorder->area.height = recorder->stage_height; + + clutter_stage_get_capture_final_size (recorder->stage, NULL, + &recorder->capture_width, + &recorder->capture_height, + &recorder->scale); } } @@ -618,6 +626,8 @@ recorder_connect_stage_callbacks (ShellRecorder *recorder) G_CALLBACK (recorder_on_stage_notify_size), recorder); g_signal_connect (recorder->stage, "notify::height", G_CALLBACK (recorder_on_stage_notify_size), recorder); + g_signal_connect (recorder->stage, "notify::resource-scale", + G_CALLBACK (recorder_on_stage_notify_size), recorder); } static void @@ -875,6 +885,7 @@ shell_recorder_class_init (ShellRecorderClass *klass) static void recorder_pipeline_set_caps (RecorderPipeline *pipeline) { + ShellRecorder *recorder = pipeline->recorder; GstCaps *caps; /* The data is always native-endian xRGB; videoconvert @@ -887,9 +898,9 @@ recorder_pipeline_set_caps (RecorderPipeline *pipeline) #else "format", G_TYPE_STRING, "xRGB", #endif - "framerate", GST_TYPE_FRACTION, pipeline->recorder->framerate, 1, - "width", G_TYPE_INT, pipeline->recorder->area.width, - "height", G_TYPE_INT, pipeline->recorder->area.height, + "framerate", GST_TYPE_FRACTION, recorder->framerate, 1, + "width", G_TYPE_INT, recorder->capture_width, + "height", G_TYPE_INT, recorder->capture_height, NULL); g_object_set (pipeline->src, "caps", caps, NULL); gst_caps_unref (caps); @@ -1496,6 +1507,11 @@ shell_recorder_set_area (ShellRecorder *recorder, recorder->area.height = CLAMP (height, 0, recorder->stage_height - recorder->area.y); + clutter_stage_get_capture_final_size (recorder->stage, &recorder->area, + &recorder->capture_width, + &recorder->capture_height, + &recorder->scale); + /* This breaks the recording but tweaking the GStreamer pipeline a bit * might make it work, at least if the codec can handle a stream where * the frame size changes in the middle. diff --git a/src/shell-screenshot.c b/src/shell-screenshot.c index 4a119a1530..125af823ee 100644 --- a/src/shell-screenshot.c +++ b/src/shell-screenshot.c @@ -222,29 +222,32 @@ do_grab_screenshot (ShellScreenshot *screenshot, int height) { ShellScreenshotPrivate *priv = screenshot->priv; + cairo_rectangle_int_t screenshot_rect = { x, y, width, height }; ClutterCapture *captures; int n_captures; int i; - clutter_stage_capture (stage, FALSE, - &(cairo_rectangle_int_t) { - .x = x, - .y = y, - .width = width, - .height = height - }, - &captures, - &n_captures); - - if (n_captures == 0) + if (!clutter_stage_capture (stage, FALSE, + &screenshot_rect, + &captures, + &n_captures)) return; - else if (n_captures == 1) + + if (n_captures == 1) priv->image = cairo_surface_reference (captures[0].image); else - priv->image = shell_util_composite_capture_images (captures, - n_captures, - x, y, - width, height); + { + float target_scale; + + clutter_stage_get_capture_final_size (stage, &screenshot_rect, + &width, &height, &target_scale); + priv->image = shell_util_composite_capture_images (captures, + n_captures, + x, y, + width, height, + target_scale); + } + priv->datetime = g_date_time_new_now_local (); for (i = 0; i < n_captures; i++) diff --git a/src/shell-util.c b/src/shell-util.c index 95d9f8e142..88a9ce11e9 100644 --- a/src/shell-util.c +++ b/src/shell-util.c @@ -462,31 +462,20 @@ shell_util_composite_capture_images (ClutterCapture *captures, int n_captures, int x, int y, - int width, - int height) + int target_width, + int target_height, + float target_scale) { int i; - double target_scale; cairo_format_t format; cairo_surface_t *image; cairo_t *cr; g_assert (n_captures > 0); - - target_scale = 0.0; - for (i = 0; i < n_captures; i++) - { - ClutterCapture *capture = &captures[i]; - double capture_scale = 1.0; - - cairo_surface_get_device_scale (capture->image, &capture_scale, NULL); - target_scale = MAX (target_scale, capture_scale); - } + g_assert (target_scale > 0.0f); format = cairo_image_surface_get_format (captures[0].image); - image = cairo_image_surface_create (format, - width * target_scale, - height * target_scale); + image = cairo_image_surface_create (format, target_width, target_height); cairo_surface_set_device_scale (image, target_scale, target_scale); cr = cairo_create (image); diff --git a/src/shell-util.h b/src/shell-util.h index 914f438d20..6904f43bc3 100644 --- a/src/shell-util.h +++ b/src/shell-util.h @@ -53,8 +53,9 @@ cairo_surface_t * shell_util_composite_capture_images (ClutterCapture *captures int n_captures, int x, int y, - int width, - int height); + int target_width, + int target_height, + float target_scale); void shell_util_check_cloexec_fds (void); -- GitLab From 6f027ee7dc8032895d29b6d4fd569597b19f38b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Thu, 8 Jun 2017 14:22:27 +0200 Subject: [PATCH 27/32] shell-util: Apply resource_scale on clipping when getting the meta texture image https://bugzilla.gnome.org/show_bug.cgi?id=765011 https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 --- src/shell-util.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/shell-util.c b/src/shell-util.c index 88a9ce11e9..31bb18e70a 100644 --- a/src/shell-util.c +++ b/src/shell-util.c @@ -3,6 +3,7 @@ #include "config.h" #include +#include #include #include #include @@ -434,14 +435,23 @@ shell_util_get_content_for_window_actor (MetaWindowActor *window_actor, cairo_surface_t *surface; cairo_rectangle_int_t clip; gfloat actor_x, actor_y; + gfloat resource_scale; texture = meta_window_actor_get_texture (window_actor); clutter_actor_get_position (CLUTTER_ACTOR (window_actor), &actor_x, &actor_y); + if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (window_actor), + &resource_scale)) + { + resource_scale = 1.0; + g_warning ("Actor resource scale is not know at this point, " + "falling back to default 1.0"); + } + clip.x = window_rect->x - (gint) actor_x; clip.y = window_rect->y - (gint) actor_y; - clip.width = window_rect->width; - clip.height = window_rect->height; + clip.width = ceilf (window_rect->width * resource_scale); + clip.height = ceilf (window_rect->height * resource_scale); surface = meta_shaped_texture_get_image (META_SHAPED_TEXTURE (texture), &clip); -- GitLab From 02c3980b837af3df094f7d0ec9da507cf74dd16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 13 Feb 2019 02:01:03 +0100 Subject: [PATCH 28/32] shell-screenshot: Initialize cursor tracker in draw cursor function Avoid code duplication, since we're using this only if the cursor has to be painted differently. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 --- src/shell-screenshot.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/shell-screenshot.c b/src/shell-screenshot.c index 125af823ee..a1fba9e4f7 100644 --- a/src/shell-screenshot.c +++ b/src/shell-screenshot.c @@ -257,21 +257,26 @@ do_grab_screenshot (ShellScreenshot *screenshot, } static void -_draw_cursor_image (MetaCursorTracker *tracker, - cairo_surface_t *surface, - cairo_rectangle_int_t area) +draw_cursor_image (cairo_surface_t *surface, + cairo_rectangle_int_t area) { CoglTexture *texture; int width, height; int stride; guint8 *data; + MetaDisplay *display; + MetaCursorTracker *tracker; cairo_surface_t *cursor_surface; cairo_region_t *screenshot_region; cairo_t *cr; int x, y; int xhot, yhot; + double xscale, yscale; + display = shell_global_get_display (shell_global_get ()); + tracker = meta_cursor_tracker_get_for_display (display); texture = meta_cursor_tracker_get_sprite (tracker); + if (!texture) return; @@ -315,7 +320,6 @@ grab_screenshot (ClutterActor *stage, GTask *result) { MetaDisplay *display; - MetaCursorTracker *tracker; int width, height; GSettings *settings; ShellScreenshot *screenshot = g_task_get_source_object (result); @@ -374,10 +378,8 @@ grab_screenshot (ClutterActor *stage, settings = g_settings_new (A11Y_APPS_SCHEMA); if (priv->include_cursor && !g_settings_get_boolean (settings, MAGNIFIER_ACTIVE_KEY)) - { - tracker = meta_cursor_tracker_get_for_display (display); - _draw_cursor_image (tracker, priv->image, priv->screenshot_area); - } + draw_cursor_image (priv->image, priv->screenshot_area); + g_object_unref (settings); g_signal_handlers_disconnect_by_func (stage, grab_screenshot, result); @@ -417,7 +419,6 @@ grab_window_screenshot (ClutterActor *stage, GTask *task; GSettings *settings; MetaDisplay *display = shell_global_get_display (priv->global); - MetaCursorTracker *tracker; MetaWindow *window = meta_display_get_focus_window (display); ClutterActor *window_actor; gfloat actor_x, actor_y; @@ -447,10 +448,8 @@ grab_window_screenshot (ClutterActor *stage, settings = g_settings_new (A11Y_APPS_SCHEMA); if (priv->include_cursor && !g_settings_get_boolean (settings, MAGNIFIER_ACTIVE_KEY)) - { - tracker = meta_cursor_tracker_get_for_display (display); - _draw_cursor_image (tracker, priv->image, priv->screenshot_area); - } + draw_cursor_image (priv->image, priv->screenshot_area); + g_object_unref (settings); g_signal_handlers_disconnect_by_func (stage, grab_window_screenshot, result); -- GitLab From 559ec8750a3da7026163f817d3e6d57f9dd953bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 13 Feb 2019 02:13:47 +0100 Subject: [PATCH 29/32] shell-screenshot: Add utility function to check when draw the cursor Don't repeat the same logic in multiple places, just set include_cursor to the proper value once depending on the settings we have. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 --- src/shell-screenshot.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/shell-screenshot.c b/src/shell-screenshot.c index a1fba9e4f7..4ad90a1ae8 100644 --- a/src/shell-screenshot.c +++ b/src/shell-screenshot.c @@ -256,6 +256,17 @@ do_grab_screenshot (ShellScreenshot *screenshot, g_free (captures); } +static gboolean +should_draw_cursor_image (void) +{ + g_autoptr (GSettings) settings = g_settings_new (A11Y_APPS_SCHEMA); + + if (!g_settings_get_boolean (settings, MAGNIFIER_ACTIVE_KEY)) + return TRUE; + + return FALSE; +} + static void draw_cursor_image (cairo_surface_t *surface, cairo_rectangle_int_t area) @@ -321,7 +332,6 @@ grab_screenshot (ClutterActor *stage, { MetaDisplay *display; int width, height; - GSettings *settings; ShellScreenshot *screenshot = g_task_get_source_object (result); ShellScreenshotPrivate *priv = screenshot->priv; GTask *task; @@ -375,13 +385,9 @@ grab_screenshot (ClutterActor *stage, priv->screenshot_area.width = width; priv->screenshot_area.height = height; - settings = g_settings_new (A11Y_APPS_SCHEMA); - if (priv->include_cursor && - !g_settings_get_boolean (settings, MAGNIFIER_ACTIVE_KEY)) + if (priv->include_cursor) draw_cursor_image (priv->image, priv->screenshot_area); - g_object_unref (settings); - g_signal_handlers_disconnect_by_func (stage, grab_screenshot, result); task = g_task_new (screenshot, NULL, on_screenshot_written, result); @@ -417,7 +423,6 @@ grab_window_screenshot (ClutterActor *stage, ShellScreenshot *screenshot = g_task_get_source_object (result); ShellScreenshotPrivate *priv = screenshot->priv; GTask *task; - GSettings *settings; MetaDisplay *display = shell_global_get_display (priv->global); MetaWindow *window = meta_display_get_focus_window (display); ClutterActor *window_actor; @@ -446,12 +451,9 @@ grab_window_screenshot (ClutterActor *stage, priv->image = meta_shaped_texture_get_image (stex, &clip); priv->datetime = g_date_time_new_now_local (); - settings = g_settings_new (A11Y_APPS_SCHEMA); - if (priv->include_cursor && !g_settings_get_boolean (settings, MAGNIFIER_ACTIVE_KEY)) + if (priv->include_cursor) draw_cursor_image (priv->image, priv->screenshot_area); - g_object_unref (settings); - g_signal_handlers_disconnect_by_func (stage, grab_window_screenshot, result); task = g_task_new (screenshot, NULL, on_screenshot_written, result); g_task_run_in_thread (task, write_screenshot_thread); @@ -541,7 +543,7 @@ shell_screenshot_screenshot (ShellScreenshot *screenshot, g_task_set_source_tag (result, shell_screenshot_screenshot); priv->filename = g_strdup (filename); - priv->include_cursor = include_cursor; + priv->include_cursor = include_cursor && should_draw_cursor_image (); stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global)); @@ -715,7 +717,7 @@ shell_screenshot_screenshot_window (ShellScreenshot *screenshot, priv->filename = g_strdup (filename); priv->include_frame = include_frame; - priv->include_cursor = include_cursor; + priv->include_cursor = include_cursor && should_draw_cursor_image (); stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global)); -- GitLab From b3e178af9d4d3d7abbccae8658cc84896fed7ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 13 Feb 2019 02:23:41 +0100 Subject: [PATCH 30/32] shell-screenshot: Use mutter paint signals to optionally draw cursor Under wayland, if the cursor should be included when doing a fullscreen screenshot, we can rely on mutter "paint" signal to have it composited for free. Otherwise if it's not requested, we can use the "actors-painted" signal to get a stage texture before the mouse overlay has been added. Instead, under X11 or when only a window screenshot is requested, we still need to draw it manually. https://bugzilla.gnome.org/show_bug.cgi?id=765011 https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 --- src/shell-screenshot.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/shell-screenshot.c b/src/shell-screenshot.c index 4ad90a1ae8..17b0f93eab 100644 --- a/src/shell-screenshot.c +++ b/src/shell-screenshot.c @@ -40,6 +40,13 @@ struct _ShellScreenshotPrivate gboolean include_frame; }; +typedef enum +{ + SHELL_SCREENSHOT_SCREEN, + SHELL_SCREENSHOT_WINDOW, + SHELL_SCREENSHOT_AREA, +} ShellScreenshotMode; + G_DEFINE_TYPE_WITH_PRIVATE (ShellScreenshot, shell_screenshot, G_TYPE_OBJECT); static void @@ -257,12 +264,15 @@ do_grab_screenshot (ShellScreenshot *screenshot, } static gboolean -should_draw_cursor_image (void) +should_draw_cursor_image (ShellScreenshotMode mode) { - g_autoptr (GSettings) settings = g_settings_new (A11Y_APPS_SCHEMA); + if (mode == SHELL_SCREENSHOT_WINDOW || !meta_is_wayland_compositor ()) + { + g_autoptr (GSettings) settings = g_settings_new (A11Y_APPS_SCHEMA); - if (!g_settings_get_boolean (settings, MAGNIFIER_ACTIVE_KEY)) - return TRUE; + if (!g_settings_get_boolean (settings, MAGNIFIER_ACTIVE_KEY)) + return TRUE; + } return FALSE; } @@ -524,6 +534,7 @@ shell_screenshot_screenshot (ShellScreenshot *screenshot, { ClutterActor *stage; ShellScreenshotPrivate *priv = screenshot->priv; + const char *paint_signal; GTask *result; if (priv->filename != NULL) { @@ -543,13 +554,22 @@ shell_screenshot_screenshot (ShellScreenshot *screenshot, g_task_set_source_tag (result, shell_screenshot_screenshot); priv->filename = g_strdup (filename); - priv->include_cursor = include_cursor && should_draw_cursor_image (); + priv->include_cursor = FALSE; stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global)); + paint_signal = "actors-painted"; meta_disable_unredirect_for_display (shell_global_get_display (priv->global)); - g_signal_connect_after (stage, "paint", G_CALLBACK (grab_screenshot), result); + if (include_cursor) + { + if (should_draw_cursor_image (SHELL_SCREENSHOT_SCREEN)) + priv->include_cursor = TRUE; + else + paint_signal = "paint"; + } + + g_signal_connect_after (stage, paint_signal, G_CALLBACK (grab_screenshot), result); clutter_actor_queue_redraw (stage); } @@ -717,7 +737,8 @@ shell_screenshot_screenshot_window (ShellScreenshot *screenshot, priv->filename = g_strdup (filename); priv->include_frame = include_frame; - priv->include_cursor = include_cursor && should_draw_cursor_image (); + priv->include_cursor = include_cursor && + should_draw_cursor_image (SHELL_SCREENSHOT_WINDOW); stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global)); -- GitLab From d027e35ceffb8375a90efd3dc7cebda0da894c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 13 Feb 2019 02:25:26 +0100 Subject: [PATCH 31/32] shell-screenshot: Always use "actors-painted" signal Since we don't care about all the stage to be painted here, we can just proceed when all the actors have been put in place. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 --- src/shell-screenshot.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/shell-screenshot.c b/src/shell-screenshot.c index 17b0f93eab..00c8cd227b 100644 --- a/src/shell-screenshot.c +++ b/src/shell-screenshot.c @@ -658,7 +658,7 @@ shell_screenshot_screenshot_area (ShellScreenshot *screenshot, meta_disable_unredirect_for_display (shell_global_get_display (shell_global_get ())); - g_signal_connect_after (stage, "paint", G_CALLBACK (grab_area_screenshot), result); + g_signal_connect_after (stage, "actors-painted", G_CALLBACK (grab_area_screenshot), result); clutter_actor_queue_redraw (stage); } @@ -744,7 +744,7 @@ shell_screenshot_screenshot_window (ShellScreenshot *screenshot, meta_disable_unredirect_for_display (shell_global_get_display (shell_global_get ())); - g_signal_connect_after (stage, "paint", G_CALLBACK (grab_window_screenshot), result); + g_signal_connect_after (stage, "actors-painted", G_CALLBACK (grab_window_screenshot), result); clutter_actor_queue_redraw (stage); } @@ -812,7 +812,7 @@ shell_screenshot_pick_color (ShellScreenshot *screenshot, meta_disable_unredirect_for_display (display); - g_signal_connect_after (stage, "paint", G_CALLBACK (grab_pixel), result); + g_signal_connect_after (stage, "actors-painted", G_CALLBACK (grab_pixel), result); clutter_actor_queue_redraw (stage); } -- GitLab From 115eda96501102ba26d4a46c7184c7f079c25602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 13 Feb 2019 02:30:28 +0100 Subject: [PATCH 32/32] shell-screenshot: Apply monitor scale to cursor surface When we grab a screenshot of a framebuffer scaled shell, we shoudl apply the device scale to the image surface, while the monitor scaling should be applied to the cursor surface, so that it's painted at proper coordinates and in proper size in the generated image. This is not needed for XWayland clients as they are not scaled anyways, while for wayland clients that are painted in multiple monitors, this might cause a lower quality cursor in the lower dpi monitor, because the cursor sprite is generated for the monitor scale, and not for the surface scale. https://bugzilla.gnome.org/show_bug.cgi?id=765011 https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/5 --- src/shell-screenshot.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/shell-screenshot.c b/src/shell-screenshot.c index 00c8cd227b..61fdc1d1ee 100644 --- a/src/shell-screenshot.c +++ b/src/shell-screenshot.c @@ -323,6 +323,22 @@ draw_cursor_image (cairo_surface_t *surface, width, height, stride); + cairo_surface_get_device_scale (surface, &xscale, &yscale); + + if (xscale != 1.0 || yscale != 1.0) + { + int monitor; + float monitor_scale; + MetaRectangle cursor_rect = { + .x = x, .y = y, .width = width, .height = height + }; + + monitor = meta_display_get_monitor_index_for_rect (display, &cursor_rect); + monitor_scale = meta_display_get_monitor_scale (display, monitor); + + cairo_surface_set_device_scale (cursor_surface, monitor_scale, monitor_scale); + } + cr = cairo_create (surface); cairo_set_source_surface (cr, cursor_surface, @@ -462,7 +478,18 @@ grab_window_screenshot (ClutterActor *stage, priv->datetime = g_date_time_new_now_local (); if (priv->include_cursor) - draw_cursor_image (priv->image, priv->screenshot_area); + { + if (meta_window_get_client_type (window) == META_WINDOW_CLIENT_TYPE_WAYLAND) + { + float resource_scale; + if (!clutter_actor_get_resource_scale (window_actor, &resource_scale)) + resource_scale = 1.0f; + + cairo_surface_set_device_scale (priv->image, resource_scale, resource_scale); + } + + draw_cursor_image (priv->image, priv->screenshot_area); + } g_signal_handlers_disconnect_by_func (stage, grab_window_screenshot, result); task = g_task_new (screenshot, NULL, on_screenshot_written, result); -- GitLab