From 882fab71ee0260b1b70cb53edbc2bd2e9d35ebd3 Mon Sep 17 00:00:00 2001 From: Victor Kareh Date: Fri, 27 Nov 2020 11:25:08 -0500 Subject: [PATCH 2/5] icons: Use cairo surfaces to render icons This replaces GdkPixbuf manipulation with the cairo_surface equivalents. As a result, icons can now render sharply in HiDPI displays. --- libwnck/application.c | 151 ++++++++++++++++++--- libwnck/application.h | 3 + libwnck/class-group.c | 172 +++++++++++++++++++----- libwnck/class-group.h | 3 + libwnck/pager.c | 20 +-- libwnck/selector.c | 103 +++++++-------- libwnck/tasklist.c | 173 ++++++++++++------------- libwnck/window.c | 139 +++++++++++++++++--- libwnck/window.h | 3 + libwnck/wnck-image-menu-item-private.h | 3 + libwnck/wnck-image-menu-item.c | 8 ++ 11 files changed, 556 insertions(+), 222 deletions(-) diff --git a/libwnck/application.c b/libwnck/application.c index 9ab7f48..d8283cc 100644 --- a/libwnck/application.c +++ b/libwnck/application.c @@ -62,8 +62,8 @@ struct _WnckApplicationPrivate WnckWindow *name_window; /* window we are using name of */ - GdkPixbuf *icon; - GdkPixbuf *mini_icon; + cairo_surface_t *icon; + cairo_surface_t *mini_icon; WnckIconCache *icon_cache; @@ -159,13 +159,8 @@ wnck_application_finalize (GObject *object) g_free (application->priv->name); application->priv->name = NULL; - if (application->priv->icon) - g_object_unref (G_OBJECT (application->priv->icon)); - application->priv->icon = NULL; - - if (application->priv->mini_icon) - g_object_unref (G_OBJECT (application->priv->mini_icon)); - application->priv->mini_icon = NULL; + g_clear_pointer (&application->priv->icon, cairo_surface_destroy); + g_clear_pointer (&application->priv->mini_icon, cairo_surface_destroy); _wnck_icon_cache_free (application->priv->icon_cache); application->priv->icon_cache = NULL; @@ -336,14 +331,20 @@ get_icons (WnckApplication *app) { app->priv->need_emit_icon_changed = TRUE; - if (app->priv->icon) - g_object_unref (G_OBJECT (app->priv->icon)); + g_clear_pointer (&app->priv->icon, cairo_surface_destroy); + g_clear_pointer (&app->priv->mini_icon, cairo_surface_destroy); - if (app->priv->mini_icon) - g_object_unref (G_OBJECT (app->priv->mini_icon)); + if (icon) + { + app->priv->icon = gdk_cairo_surface_create_from_pixbuf (icon, 0, NULL); + g_clear_object (&icon); + } - app->priv->icon = icon; - app->priv->mini_icon = mini_icon; + if (mini_icon) + { + app->priv->mini_icon = gdk_cairo_surface_create_from_pixbuf (mini_icon, 0, NULL); + g_clear_object (&mini_icon); + } } /* FIXME we should really fall back to using the icon @@ -404,12 +405,39 @@ find_icon_window (WnckApplication *app) GdkPixbuf* wnck_application_get_icon (WnckApplication *app) { + static const cairo_user_data_key_t app_icon_pixbuf_key; + g_return_val_if_fail (WNCK_IS_APPLICATION (app), NULL); _wnck_application_load_icons (app); if (app->priv->icon) - return app->priv->icon; + { + GdkPixbuf *pixbuf; + + pixbuf = cairo_surface_get_user_data (app->priv->icon, &app_icon_pixbuf_key); + + if (pixbuf == NULL) + { + int scaling_factor; + + pixbuf = gdk_pixbuf_get_from_surface (app->priv->icon, + 0, + 0, + cairo_image_surface_get_width (app->priv->icon), + cairo_image_surface_get_height (app->priv->icon)); + + scaling_factor = _wnck_get_window_scaling_factor (); + pixbuf = gdk_pixbuf_scale_simple (pixbuf, + gdk_pixbuf_get_width (pixbuf) / scaling_factor, + gdk_pixbuf_get_height (pixbuf) / scaling_factor, + GDK_INTERP_BILINEAR); + + cairo_surface_set_user_data (app->priv->icon, &app_icon_pixbuf_key, pixbuf, g_object_unref); + } + + return pixbuf; + } else { WnckWindow *w = find_icon_window (app); @@ -435,12 +463,39 @@ wnck_application_get_icon (WnckApplication *app) GdkPixbuf* wnck_application_get_mini_icon (WnckApplication *app) { + static const cairo_user_data_key_t app_mini_icon_pixbuf_key; + g_return_val_if_fail (WNCK_IS_APPLICATION (app), NULL); _wnck_application_load_icons (app); if (app->priv->mini_icon) - return app->priv->mini_icon; + { + GdkPixbuf *pixbuf; + + pixbuf = cairo_surface_get_user_data (app->priv->mini_icon, &app_mini_icon_pixbuf_key); + + if (pixbuf == NULL) + { + int scaling_factor; + + pixbuf = gdk_pixbuf_get_from_surface (app->priv->mini_icon, + 0, + 0, + cairo_image_surface_get_width (app->priv->mini_icon), + cairo_image_surface_get_height (app->priv->mini_icon)); + + scaling_factor = _wnck_get_window_scaling_factor (); + pixbuf = gdk_pixbuf_scale_simple (pixbuf, + gdk_pixbuf_get_width (pixbuf) / scaling_factor, + gdk_pixbuf_get_height (pixbuf) / scaling_factor, + GDK_INTERP_BILINEAR); + + cairo_surface_set_user_data (app->priv->mini_icon, &app_mini_icon_pixbuf_key, pixbuf, g_object_unref); + } + + return pixbuf; + } else { WnckWindow *w = find_icon_window (app); @@ -451,6 +506,68 @@ wnck_application_get_mini_icon (WnckApplication *app) } } +/** + * wnck_application_get_icon_surface: + * @app: a #WnckApplication. + * + * Gets the icon-surface to be used for @app. If no icon-surfaceis set for @app, + * a suboptimal heuristic is used to find an appropriate icon. If no icon-surface + * was found, a fallback icon-surface is used. + * + * Return value: (transfer full): a reference to the icon-surface for @app. The + * caller should unreference the cairo_surface_t once done + * with it. + **/ +cairo_surface_t* +wnck_application_get_icon_surface (WnckApplication *app) +{ + g_return_val_if_fail (WNCK_IS_APPLICATION (app), NULL); + + _wnck_application_load_icons (app); + + if (app->priv->icon) + return cairo_surface_reference (app->priv->icon); + else + { + WnckWindow *w = find_icon_window (app); + if (w) + return wnck_window_get_icon_surface (w); + else + return NULL; + } +} + +/** + * wnck_application_get_mini_icon_surface: + * @app: a #WnckApplication. + * + * Gets the mini-icon-surface to be used for @app. If no mini-icon-surfaceis set + * for @app, a suboptimal heuristic is used to find an appropriate icon. If no + * mini-icon-surface was found, a fallback mini-icon-surface is used. + * + * Return value: (transfer full): a reference to the mini-icon-surface for @app. + * The caller should unreference the cairo_surface_t once + * done with it. + **/ +cairo_surface_t* +wnck_application_get_mini_icon_surface (WnckApplication *app) +{ + g_return_val_if_fail (WNCK_IS_APPLICATION (app), NULL); + + _wnck_application_load_icons (app); + + if (app->priv->mini_icon) + return cairo_surface_reference (app->priv->mini_icon); + else + { + WnckWindow *w = find_icon_window (app); + if (w) + return wnck_window_get_mini_icon_surface (w); + else + return NULL; + } +} + /** * wnck_application_get_icon_is_fallback: * @app: a #WnckApplication diff --git a/libwnck/application.h b/libwnck/application.h index 40fe4c6..f3ea970 100644 --- a/libwnck/application.h +++ b/libwnck/application.h @@ -29,6 +29,7 @@ #include #include #include +#include G_BEGIN_DECLS @@ -92,6 +93,8 @@ const char* wnck_application_get_icon_name (WnckApplication *app); int wnck_application_get_pid (WnckApplication *app); GdkPixbuf* wnck_application_get_icon (WnckApplication *app); GdkPixbuf* wnck_application_get_mini_icon (WnckApplication *app); +cairo_surface_t* wnck_application_get_icon_surface (WnckApplication *app); +cairo_surface_t* wnck_application_get_mini_icon_surface (WnckApplication *app); gboolean wnck_application_get_icon_is_fallback (WnckApplication *app); const char* wnck_application_get_startup_id (WnckApplication *app); diff --git a/libwnck/class-group.c b/libwnck/class-group.c index 46d1f24..e6c45d6 100644 --- a/libwnck/class-group.c +++ b/libwnck/class-group.c @@ -59,8 +59,8 @@ struct _WnckClassGroupPrivate { GHashTable *window_icon_handlers; GHashTable *window_name_handlers; - GdkPixbuf *icon; - GdkPixbuf *mini_icon; + cairo_surface_t *icon; + cairo_surface_t *mini_icon; }; G_DEFINE_TYPE_WITH_PRIVATE (WnckClassGroup, wnck_class_group, G_TYPE_OBJECT); @@ -171,17 +171,8 @@ wnck_class_group_finalize (GObject *object) class_group->priv->window_name_handlers = NULL; } - if (class_group->priv->icon) - { - g_object_unref (class_group->priv->icon); - class_group->priv->icon = NULL; - } - - if (class_group->priv->mini_icon) - { - g_object_unref (class_group->priv->mini_icon); - class_group->priv->mini_icon = NULL; - } + g_clear_pointer (&class_group->priv->icon, cairo_surface_destroy); + g_clear_pointer (&class_group->priv->mini_icon, cairo_surface_destroy); G_OBJECT_CLASS (wnck_class_group_parent_class)->finalize (object); } @@ -370,7 +361,8 @@ set_name (WnckClassGroup *class_group) /* Walks the list of applications, trying to get an icon from them */ static void -get_icons_from_applications (WnckClassGroup *class_group, GdkPixbuf **icon, GdkPixbuf **mini_icon) +get_icons_from_applications (WnckClassGroup *class_group, + cairo_surface_t **icon, cairo_surface_t **mini_icon) { GList *l; @@ -386,15 +378,15 @@ get_icons_from_applications (WnckClassGroup *class_group, GdkPixbuf **icon, GdkP app = wnck_window_get_application (window); if (app) { - *icon = wnck_application_get_icon (app); - *mini_icon = wnck_application_get_mini_icon (app); + *icon = wnck_application_get_icon_surface (app); + *mini_icon = wnck_application_get_mini_icon_surface (app); if (*icon && *mini_icon) return; else { - *icon = NULL; - *mini_icon = NULL; + g_clear_pointer (icon, cairo_surface_destroy); + g_clear_pointer (mini_icon, cairo_surface_destroy); } } } @@ -402,7 +394,8 @@ get_icons_from_applications (WnckClassGroup *class_group, GdkPixbuf **icon, GdkP /* Walks the list of windows, trying to get an icon from them */ static void -get_icons_from_windows (WnckClassGroup *class_group, GdkPixbuf **icon, GdkPixbuf **mini_icon) +get_icons_from_windows (WnckClassGroup *class_group, + cairo_surface_t **icon, cairo_surface_t **mini_icon) { GList *l; @@ -415,15 +408,15 @@ get_icons_from_windows (WnckClassGroup *class_group, GdkPixbuf **icon, GdkPixbuf window = WNCK_WINDOW (l->data); - *icon = wnck_window_get_icon (window); - *mini_icon = wnck_window_get_mini_icon (window); + *icon = wnck_window_get_icon_surface (window); + *mini_icon = wnck_window_get_mini_icon_surface (window); if (*icon && *mini_icon) return; else { - *icon = NULL; - *mini_icon = NULL; + g_clear_pointer (icon, cairo_surface_destroy); + g_clear_pointer (mini_icon, cairo_surface_destroy); } } } @@ -434,7 +427,7 @@ get_icons_from_windows (WnckClassGroup *class_group, GdkPixbuf **icon, GdkPixbuf static void set_icon (WnckClassGroup *class_group) { - GdkPixbuf *icon, *mini_icon; + cairo_surface_t *icon, *mini_icon; gboolean icons_reffed = FALSE; get_icons_from_applications (class_group, &icon, &mini_icon); @@ -448,28 +441,39 @@ set_icon (WnckClassGroup *class_group) handle = wnck_screen_get_handle (class_group->priv->screen); - _wnck_get_fallback_icons (&icon, + GdkPixbuf *icon_pixbuf, *mini_icon_pixbuf; + + _wnck_get_fallback_icons (&icon_pixbuf, _wnck_handle_get_default_icon_size (handle), - &mini_icon, + &mini_icon_pixbuf, _wnck_handle_get_default_mini_icon_size (handle)); + if (icon_pixbuf) + { + icon = gdk_cairo_surface_create_from_pixbuf (icon_pixbuf, 0, NULL); + g_clear_object (&icon_pixbuf); + } + + if (mini_icon_pixbuf) + { + mini_icon = gdk_cairo_surface_create_from_pixbuf (mini_icon_pixbuf, 0, NULL); + g_clear_object (&mini_icon_pixbuf); + } + icons_reffed = TRUE; } g_assert (icon && mini_icon); - if (class_group->priv->icon) - g_object_unref (class_group->priv->icon); - - if (class_group->priv->mini_icon) - g_object_unref (class_group->priv->mini_icon); + g_clear_pointer (&class_group->priv->icon, cairo_surface_destroy); + g_clear_pointer (&class_group->priv->mini_icon, cairo_surface_destroy); class_group->priv->icon = icon; class_group->priv->mini_icon = mini_icon; if (!icons_reffed) { - g_object_ref (class_group->priv->icon); - g_object_ref (class_group->priv->mini_icon); + cairo_surface_reference (class_group->priv->icon); + cairo_surface_reference (class_group->priv->mini_icon); } g_signal_emit (G_OBJECT (class_group), signals[ICON_CHANGED], 0); @@ -702,9 +706,39 @@ wnck_class_group_get_name (WnckClassGroup *class_group) GdkPixbuf * wnck_class_group_get_icon (WnckClassGroup *class_group) { + static const cairo_user_data_key_t class_group_icon_pixbuf_key; + g_return_val_if_fail (class_group != NULL, NULL); - return class_group->priv->icon; + if (class_group->priv->icon) + { + GdkPixbuf *pixbuf; + + pixbuf = cairo_surface_get_user_data (class_group->priv->icon, &class_group_icon_pixbuf_key); + + if (pixbuf == NULL) + { + int scaling_factor; + + pixbuf = gdk_pixbuf_get_from_surface (class_group->priv->icon, + 0, + 0, + cairo_image_surface_get_width (class_group->priv->icon), + cairo_image_surface_get_height (class_group->priv->icon)); + + scaling_factor = _wnck_get_window_scaling_factor (); + pixbuf = gdk_pixbuf_scale_simple (pixbuf, + gdk_pixbuf_get_width (pixbuf) / scaling_factor, + gdk_pixbuf_get_height (pixbuf) / scaling_factor, + GDK_INTERP_BILINEAR); + + cairo_surface_set_user_data (class_group->priv->icon, &class_group_icon_pixbuf_key, pixbuf, g_object_unref); + } + + return pixbuf; + } + + return NULL; } /** @@ -723,8 +757,76 @@ wnck_class_group_get_icon (WnckClassGroup *class_group) **/ GdkPixbuf * wnck_class_group_get_mini_icon (WnckClassGroup *class_group) +{ + static const cairo_user_data_key_t class_group_mini_icon_pixbuf_key; + + g_return_val_if_fail (class_group != NULL, NULL); + + if (class_group->priv->mini_icon) + { + GdkPixbuf *pixbuf; + + pixbuf = cairo_surface_get_user_data (class_group->priv->mini_icon, &class_group_mini_icon_pixbuf_key); + + if (pixbuf == NULL) + { + int scaling_factor; + + pixbuf = gdk_pixbuf_get_from_surface (class_group->priv->mini_icon, + 0, + 0, + cairo_image_surface_get_width (class_group->priv->mini_icon), + cairo_image_surface_get_height (class_group->priv->mini_icon)); + + scaling_factor = _wnck_get_window_scaling_factor (); + pixbuf = gdk_pixbuf_scale_simple (pixbuf, + gdk_pixbuf_get_width (pixbuf) / scaling_factor, + gdk_pixbuf_get_height (pixbuf) / scaling_factor, + GDK_INTERP_BILINEAR); + + cairo_surface_set_user_data (class_group->priv->mini_icon, &class_group_mini_icon_pixbuf_key, pixbuf, g_object_unref); + } + + return pixbuf; + } + + return NULL; +} + +/** + * wnck_class_group_get_icon_surface: + * @class_group: a #WnckClassGroup. + * + * Gets the icon-surface to be used for @class_group. Since there is no way to + * properly find the icon-surface, the same suboptimal heuristic as the one for + * wnck_class_group_get_icon() is used to find it. + * + * Return value: (transfer full): the icon-surface for @class_group. The caller should + * unreference the returned cairo_surface_t once done with it. + **/ +cairo_surface_t * +wnck_class_group_get_icon_surface (WnckClassGroup *class_group) +{ + g_return_val_if_fail (class_group != NULL, NULL); + + return cairo_surface_reference (class_group->priv->icon); +} + +/** + * wnck_class_group_get_mini_icon_surface: + * @class_group: a #WnckClassGroup. + * + * Gets the mini-icon-surface to be used for @class_group. Since there is no way to + * properly find the mini-icon-surface, the same suboptimal heuristic as the one for + * wnck_class_group_get_icon() is used to find it. + * + * Return value: (transfer full): the mini-icon-surface for @class_group. The caller should + * unreference the returned cairo_surface_t once done with it. + **/ +cairo_surface_t * +wnck_class_group_get_mini_icon_surface (WnckClassGroup *class_group) { g_return_val_if_fail (class_group != NULL, NULL); - return class_group->priv->mini_icon; + return cairo_surface_reference (class_group->priv->mini_icon); } diff --git a/libwnck/class-group.h b/libwnck/class-group.h index 581cd22..122e0ed 100644 --- a/libwnck/class-group.h +++ b/libwnck/class-group.h @@ -30,6 +30,7 @@ #include #include #include +#include G_BEGIN_DECLS @@ -82,6 +83,8 @@ const char * wnck_class_group_get_name (WnckClassGroup *class_group); GdkPixbuf *wnck_class_group_get_icon (WnckClassGroup *class_group); GdkPixbuf *wnck_class_group_get_mini_icon (WnckClassGroup *class_group); +cairo_surface_t *wnck_class_group_get_icon_surface (WnckClassGroup *class_group); +cairo_surface_t *wnck_class_group_get_mini_icon_surface (WnckClassGroup *class_group); #ifndef WNCK_DISABLE_DEPRECATED G_DEPRECATED_FOR(wnck_class_group_get_id) diff --git a/libwnck/pager.c b/libwnck/pager.c index 4df766c..9b09928 100644 --- a/libwnck/pager.c +++ b/libwnck/pager.c @@ -984,8 +984,9 @@ draw_window (cairo_t *cr, gboolean translucent) { GtkStyleContext *context; - GdkPixbuf *icon; + cairo_surface_t *icon; int icon_x, icon_y, icon_w, icon_h; + int scaling_factor; gboolean is_active; GdkRGBA fg; gdouble translucency; @@ -1015,14 +1016,15 @@ draw_window (cairo_t *cr, cairo_pop_group_to_source (cr); cairo_paint_with_alpha (cr, translucency); - icon = wnck_window_get_icon (win); + icon = wnck_window_get_icon_surface (win); icon_w = icon_h = 0; + scaling_factor = gtk_widget_get_scale_factor (widget); if (icon) { - icon_w = gdk_pixbuf_get_width (icon); - icon_h = gdk_pixbuf_get_height (icon); + icon_w = cairo_image_surface_get_width (icon) / scaling_factor; + icon_h = cairo_image_surface_get_height (icon) / scaling_factor; /* If the icon is too big, fall back to mini icon. * We don't arbitrarily scale the icon, because it's @@ -1031,11 +1033,12 @@ draw_window (cairo_t *cr, if (icon_w > (winrect->width - 2) || icon_h > (winrect->height - 2)) { - icon = wnck_window_get_mini_icon (win); + cairo_surface_destroy (icon); + icon = wnck_window_get_mini_icon_surface (win); if (icon) { - icon_w = gdk_pixbuf_get_width (icon); - icon_h = gdk_pixbuf_get_height (icon); + icon_w = cairo_image_surface_get_width (icon) / scaling_factor; + icon_h = cairo_image_surface_get_height (icon) / scaling_factor; /* Give up. */ if (icon_w > (winrect->width - 2) || @@ -1051,7 +1054,7 @@ draw_window (cairo_t *cr, icon_y = winrect->y + (winrect->height - icon_h) / 2; cairo_push_group (cr); - gtk_render_icon (context, cr, icon, icon_x, icon_y); + gtk_render_icon_surface (context, cr, icon, icon_x, icon_y); cairo_pop_group_to_source (cr); cairo_paint_with_alpha (cr, translucency); } @@ -1072,6 +1075,7 @@ draw_window (cairo_t *cr, cairo_stroke (cr); gtk_style_context_restore (context); + cairo_surface_destroy (icon); } static WnckWindow * diff --git a/libwnck/selector.c b/libwnck/selector.c index 4cf6189..fcc3322 100644 --- a/libwnck/selector.c +++ b/libwnck/selector.c @@ -135,103 +135,96 @@ wnck_selector_get_screen (WnckSelector *selector) gdk_x11_screen_get_screen_number (screen)); } -static GdkPixbuf * +static cairo_surface_t * wnck_selector_get_default_window_icon (void) { - static GdkPixbuf *retval = NULL; + static cairo_surface_t *retval = NULL; + GdkPixbuf *pixbuf; if (retval) return retval; - retval = gdk_pixbuf_new_from_resource ("/org/gnome/libwnck/default_icon.png", NULL); + pixbuf = gdk_pixbuf_new_from_resource ("/org/gnome/libwnck/default_icon.png", NULL); - g_assert (retval); + g_assert (pixbuf); + + retval = gdk_cairo_surface_create_from_pixbuf (pixbuf, 0, NULL); + + g_object_unref (pixbuf); return retval; } -static GdkPixbuf * -wnck_selector_dimm_icon (GdkPixbuf *pixbuf) +static void +wnck_selector_dimm_icon (cairo_t *cr, cairo_surface_t *surface) { - int x, y, pixel_stride, row_stride; - guchar *row, *pixels; - int w, h; - GdkPixbuf *dimmed; + cairo_surface_t *temp; + cairo_t *temp_cr; - w = gdk_pixbuf_get_width (pixbuf); - h = gdk_pixbuf_get_height (pixbuf); + g_assert (surface != NULL); + g_assert (cairo_surface_get_content (surface) != CAIRO_CONTENT_COLOR); - if (gdk_pixbuf_get_has_alpha (pixbuf)) - dimmed = gdk_pixbuf_copy (pixbuf); - else - dimmed = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0); + temp = cairo_surface_create_similar (surface, + cairo_surface_get_content (surface), + cairo_image_surface_get_width (surface), + cairo_image_surface_get_height (surface)); - pixel_stride = 4; + temp_cr = cairo_create (temp); - row = gdk_pixbuf_get_pixels (dimmed); - row_stride = gdk_pixbuf_get_rowstride (dimmed); + cairo_set_source_surface (temp_cr, surface, 0, 0); + cairo_paint_with_alpha (temp_cr, 0.5); - for (y = 0; y < h; y++) - { - pixels = row; - for (x = 0; x < w; x++) - { - pixels[3] /= 2; - pixels += pixel_stride; - } - row += row_stride; - } + cairo_set_operator (cr, CAIRO_OPERATOR_IN); + cairo_set_source_surface (cr, temp, 0, 0); + cairo_paint (cr); - return dimmed; + cairo_destroy (temp_cr); + cairo_surface_destroy (temp); } void _wnck_selector_set_window_icon (GtkWidget *image, WnckWindow *window) { - GdkPixbuf *pixbuf, *freeme, *freeme2; - int width, height; + cairo_surface_t *orig, *surface; + cairo_t *cr; + int scaling_factor; int icon_size = -1; - pixbuf = NULL; - freeme = NULL; - freeme2 = NULL; + orig = NULL; + surface = NULL; if (window) - pixbuf = wnck_window_get_mini_icon (window); + orig = wnck_window_get_mini_icon_surface (window); - if (!pixbuf) - pixbuf = wnck_selector_get_default_window_icon (); + if (!orig) + orig = wnck_selector_get_default_window_icon (); if (icon_size == -1) gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, NULL, &icon_size); - width = gdk_pixbuf_get_width (pixbuf); - height = gdk_pixbuf_get_height (pixbuf); + surface = cairo_surface_create_similar_image (orig, + cairo_image_surface_get_format (orig), + cairo_image_surface_get_width (orig), + cairo_image_surface_get_height (orig)); - if (icon_size != -1 && (width > icon_size || height > icon_size)) - { - double scale; + scaling_factor = _wnck_get_window_scaling_factor (); + cairo_surface_set_device_scale (surface, (double)scaling_factor, (double)scaling_factor); - scale = ((double) icon_size) / MAX (width, height); + cr = cairo_create (surface); - pixbuf = gdk_pixbuf_scale_simple (pixbuf, width * scale, - height * scale, GDK_INTERP_BILINEAR); - freeme = pixbuf; - } + cairo_set_source_surface (cr, orig, 0, 0); + cairo_paint (cr); if (window && wnck_window_is_minimized (window)) { - pixbuf = wnck_selector_dimm_icon (pixbuf); - freeme2 = pixbuf; + wnck_selector_dimm_icon (cr, surface); } - gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf); + gtk_image_set_from_surface (GTK_IMAGE (image), surface); - if (freeme) - g_object_unref (freeme); - if (freeme2) - g_object_unref (freeme2); + cairo_destroy (cr); + cairo_surface_destroy (surface); } static void diff --git a/libwnck/tasklist.c b/libwnck/tasklist.c index b773247..10c6cc8 100644 --- a/libwnck/tasklist.c +++ b/libwnck/tasklist.c @@ -289,11 +289,11 @@ static WnckTask *wnck_task_new_from_startup_sequence (WnckTasklist *tasklis #endif static gboolean wnck_task_get_needs_attention (WnckTask *task); +static cairo_surface_t *wnck_task_get_icon (WnckTask *task); static char *wnck_task_get_text (WnckTask *task, gboolean icon_text, gboolean include_state); -static GdkPixbuf *wnck_task_get_icon (WnckTask *task); static gint wnck_task_compare_alphabetically (gconstpointer a, gconstpointer b); static gint wnck_task_compare (gconstpointer a, @@ -637,10 +637,10 @@ wnck_button_set_handle (WnckButton *self, } static void -wnck_button_set_image_from_pixbuf (WnckButton *self, - GdkPixbuf *pixbuf) +wnck_button_set_image_from_surface (WnckButton *self, + cairo_surface_t *surface) { - gtk_image_set_from_pixbuf (GTK_IMAGE (self->image), pixbuf); + gtk_image_set_from_surface (GTK_IMAGE (self->image), surface); } static void @@ -3533,7 +3533,7 @@ wnck_task_popup_menu (WnckTask *task, GtkWidget *menu; WnckTask *win_task; char *text; - GdkPixbuf *pixbuf; + cairo_surface_t *surface; GtkWidget *menu_item; GList *l, *list; @@ -3577,15 +3577,15 @@ wnck_task_popup_menu (WnckTask *task, gtk_widget_set_tooltip_text (menu_item, text); g_free (text); - pixbuf = wnck_task_get_icon (win_task); - if (pixbuf) + surface = wnck_task_get_icon (win_task); + if (surface) { WnckImageMenuItem *item; item = WNCK_IMAGE_MENU_ITEM (menu_item); - wnck_image_menu_item_set_image_from_icon_pixbuf (item, pixbuf); - g_object_unref (pixbuf); + wnck_image_menu_item_set_image_from_icon_surface (item, surface); + cairo_surface_destroy (surface); } gtk_widget_show (menu_item); @@ -3771,102 +3771,78 @@ wnck_task_get_text (WnckTask *task, } static void -wnck_dimm_icon (GdkPixbuf *pixbuf) +wnck_dimm_icon (cairo_t *cr, cairo_surface_t *surface) { - int x, y, pixel_stride, row_stride; - guchar *row, *pixels; - int w, h; + cairo_surface_t *temp; + cairo_t *temp_cr; - g_assert (pixbuf != NULL); + g_assert (surface != NULL); + g_assert (cairo_surface_get_content (surface) != CAIRO_CONTENT_COLOR); - w = gdk_pixbuf_get_width (pixbuf); - h = gdk_pixbuf_get_height (pixbuf); + temp = cairo_surface_create_similar (surface, + cairo_surface_get_content (surface), + cairo_image_surface_get_width (surface), + cairo_image_surface_get_height (surface)); - g_assert (gdk_pixbuf_get_has_alpha (pixbuf)); + temp_cr = cairo_create (temp); - pixel_stride = 4; + cairo_set_source_surface (temp_cr, surface, 0, 0); + cairo_paint_with_alpha (temp_cr, 0.5); - row = gdk_pixbuf_get_pixels (pixbuf); - row_stride = gdk_pixbuf_get_rowstride (pixbuf); + cairo_set_operator (cr, CAIRO_OPERATOR_IN); + cairo_set_source_surface (cr, temp, 0, 0); + cairo_paint (cr); - for (y = 0; y < h; y++) - { - pixels = row; - - for (x = 0; x < w; x++) - { - pixels[3] /= 2; - - pixels += pixel_stride; - } - - row += row_stride; - } + cairo_destroy (temp_cr); + cairo_surface_destroy (temp); } -static GdkPixbuf * +static cairo_surface_t * wnck_task_scale_icon (gsize mini_icon_size, - GdkPixbuf *orig, + cairo_surface_t *orig, gboolean minimized) { - int w, h; - GdkPixbuf *pixbuf; + int scaling_factor; + cairo_surface_t *surface; + cairo_t *cr; if (!orig) return NULL; - w = gdk_pixbuf_get_width (orig); - h = gdk_pixbuf_get_height (orig); + surface = cairo_surface_create_similar_image (orig, + cairo_image_surface_get_format (orig), + cairo_image_surface_get_width (orig), + cairo_image_surface_get_height (orig)); - if (h != (int) mini_icon_size || - !gdk_pixbuf_get_has_alpha (orig)) - { - double scale; - - pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, - TRUE, - 8, - mini_icon_size * w / (double) h, - mini_icon_size); - - scale = mini_icon_size / (double) gdk_pixbuf_get_height (orig); - - gdk_pixbuf_scale (orig, - pixbuf, - 0, 0, - gdk_pixbuf_get_width (pixbuf), - gdk_pixbuf_get_height (pixbuf), - 0, 0, - scale, scale, - GDK_INTERP_HYPER); - } - else - pixbuf = orig; + scaling_factor = _wnck_get_window_scaling_factor (); + cairo_surface_set_device_scale (surface, (double)scaling_factor, (double)scaling_factor); + + cr = cairo_create (surface); + + cairo_set_source_surface (cr, orig, 0, 0); + cairo_paint (cr); if (minimized) { - if (orig == pixbuf) - pixbuf = gdk_pixbuf_copy (orig); - - wnck_dimm_icon (pixbuf); + wnck_dimm_icon (cr, surface); } - if (orig == pixbuf) - g_object_ref (pixbuf); + cairo_destroy (cr); - return pixbuf; + return surface; } -static GdkPixbuf * +static cairo_surface_t * wnck_task_get_icon (WnckTask *task) { WnckWindowState state; - GdkPixbuf *pixbuf; WnckHandle *handle; gsize mini_icon_size; + cairo_surface_t *surface; + cairo_surface_t *mini_icon; - pixbuf = NULL; + surface = NULL; handle = task->tasklist->priv->handle; mini_icon_size = _wnck_handle_get_default_mini_icon_size (handle); @@ -3874,17 +3850,21 @@ wnck_task_get_icon (WnckTask *task) switch (task->type) { case WNCK_TASK_CLASS_GROUP: - pixbuf = wnck_task_scale_icon (mini_icon_size, - wnck_class_group_get_mini_icon (task->class_group), - FALSE); + mini_icon = wnck_class_group_get_mini_icon_surface (task->class_group); + surface = wnck_task_scale_icon (mini_icon_size, mini_icon, FALSE); + + cairo_surface_destroy (mini_icon); break; case WNCK_TASK_WINDOW: state = wnck_window_get_state (task->window); - pixbuf = wnck_task_scale_icon (mini_icon_size, - wnck_window_get_mini_icon (task->window), - state & WNCK_WINDOW_STATE_MINIMIZED); + mini_icon = wnck_window_get_mini_icon_surface (task->window); + surface = wnck_task_scale_icon (mini_icon_size, + mini_icon, + state & WNCK_WINDOW_STATE_MINIMIZED); + + cairo_surface_destroy (mini_icon); break; case WNCK_TASK_STARTUP_SEQUENCE: @@ -3905,16 +3885,28 @@ wnck_task_get_icon (WnckTask *task) if (loaded != NULL) { - pixbuf = wnck_task_scale_icon (mini_icon_size, loaded, FALSE); + cairo_surface_t *temp; + + temp = gdk_cairo_surface_create_from_pixbuf (loaded, 0, NULL); + surface = wnck_task_scale_icon (mini_icon_size, temp, FALSE); + + cairo_surface_destroy (temp); g_object_unref (G_OBJECT (loaded)); } } } - if (pixbuf == NULL) + if (surface == NULL) { + GdkPixbuf *pixbuf; _wnck_get_fallback_icons (NULL, 0, &pixbuf, mini_icon_size); + + if (pixbuf != NULL) + { + surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, 0, NULL); + g_object_unref (pixbuf); + } } #endif break; @@ -3923,7 +3915,7 @@ wnck_task_get_icon (WnckTask *task) break; } - return pixbuf; + return surface; } static gboolean @@ -3972,12 +3964,13 @@ wnck_task_get_needs_attention (WnckTask *task) static void wnck_task_update_visible_state (WnckTask *task) { - GdkPixbuf *pixbuf; + cairo_surface_t *surface; char *text; - pixbuf = wnck_task_get_icon (task); - wnck_button_set_image_from_pixbuf (WNCK_BUTTON (task->button), pixbuf); - g_clear_object (&pixbuf); + surface = wnck_task_get_icon (task); + wnck_button_set_image_from_surface (WNCK_BUTTON (task->button), surface); + if (surface) + cairo_surface_destroy (surface); text = wnck_task_get_text (task, TRUE, TRUE); if (text != NULL) @@ -4448,7 +4441,7 @@ wnck_task_draw (GtkWidget *widget, static void wnck_task_create_widgets (WnckTask *task, GtkReliefStyle relief) { - GdkPixbuf *pixbuf; + cairo_surface_t *surface; char *text; static const GtkTargetEntry targets[] = { { (gchar *) "application/x-wnck-window-id", 0, 0 } @@ -4477,9 +4470,9 @@ wnck_task_create_widgets (WnckTask *task, GtkReliefStyle relief) gtk_drag_dest_set (GTK_WIDGET (task->button), 0, NULL, 0, GDK_ACTION_DEFAULT); - pixbuf = wnck_task_get_icon (task); - wnck_button_set_image_from_pixbuf (WNCK_BUTTON (task->button), pixbuf); - g_clear_object (&pixbuf); + surface = wnck_task_get_icon (task); + wnck_button_set_image_from_surface (WNCK_BUTTON (task->button), surface); + cairo_surface_destroy (surface); text = wnck_task_get_text (task, TRUE, TRUE); wnck_button_set_text (WNCK_BUTTON (task->button), text); diff --git a/libwnck/window.c b/libwnck/window.c index 35bb37c..f01b4c2 100644 --- a/libwnck/window.c +++ b/libwnck/window.c @@ -87,8 +87,8 @@ struct _WnckWindowPrivate WnckWindowType wintype; - GdkPixbuf *icon; - GdkPixbuf *mini_icon; + cairo_surface_t *icon; + cairo_surface_t *mini_icon; WnckIconCache *icon_cache; @@ -411,13 +411,8 @@ wnck_window_finalize (GObject *object) g_free (window->priv->session_id_utf8); window->priv->session_id_utf8 = NULL; - if (window->priv->icon) - g_object_unref (G_OBJECT (window->priv->icon)); - window->priv->icon = NULL; - - if (window->priv->mini_icon) - g_object_unref (G_OBJECT (window->priv->mini_icon)); - window->priv->mini_icon = NULL; + g_clear_pointer (&window->priv->icon, cairo_surface_destroy); + g_clear_pointer (&window->priv->mini_icon, cairo_surface_destroy); _wnck_icon_cache_free (window->priv->icon_cache); window->priv->icon_cache = NULL; @@ -2132,14 +2127,20 @@ get_icons (WnckWindow *window) { window->priv->need_emit_icon_changed = TRUE; - if (window->priv->icon) - g_object_unref (G_OBJECT (window->priv->icon)); + g_clear_pointer (&window->priv->icon, cairo_surface_destroy); + g_clear_pointer (&window->priv->mini_icon, cairo_surface_destroy); - if (window->priv->mini_icon) - g_object_unref (G_OBJECT (window->priv->mini_icon)); + if (icon) + { + window->priv->icon = gdk_cairo_surface_create_from_pixbuf (icon, 0, NULL); + g_clear_object (&icon); + } - window->priv->icon = icon; - window->priv->mini_icon = mini_icon; + if (mini_icon) + { + window->priv->mini_icon = gdk_cairo_surface_create_from_pixbuf (mini_icon, 0, NULL); + g_clear_object (&mini_icon); + } } g_assert ((window->priv->icon && window->priv->mini_icon) || @@ -2173,11 +2174,41 @@ _wnck_window_load_icons (WnckWindow *window) GdkPixbuf* wnck_window_get_icon (WnckWindow *window) { + static const cairo_user_data_key_t window_icon_pixbuf_key; + g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL); _wnck_window_load_icons (window); - return window->priv->icon; + if (window->priv->icon) + { + GdkPixbuf *pixbuf; + + pixbuf = cairo_surface_get_user_data (window->priv->icon, &window_icon_pixbuf_key); + + if (pixbuf == NULL) + { + int scaling_factor; + + pixbuf = gdk_pixbuf_get_from_surface (window->priv->icon, + 0, + 0, + cairo_image_surface_get_width (window->priv->icon), + cairo_image_surface_get_height (window->priv->icon)); + + scaling_factor = _wnck_get_window_scaling_factor (); + pixbuf = gdk_pixbuf_scale_simple (pixbuf, + gdk_pixbuf_get_width (pixbuf) / scaling_factor, + gdk_pixbuf_get_height (pixbuf) / scaling_factor, + GDK_INTERP_BILINEAR); + + cairo_surface_set_user_data (window->priv->icon, &window_icon_pixbuf_key, pixbuf, g_object_unref); + } + + return pixbuf; + } + + return NULL; } /** @@ -2194,12 +2225,86 @@ wnck_window_get_icon (WnckWindow *window) **/ GdkPixbuf* wnck_window_get_mini_icon (WnckWindow *window) +{ + static const cairo_user_data_key_t window_mini_icon_pixbuf_key; + + g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL); + + _wnck_window_load_icons (window); + + if (window->priv->mini_icon) + { + GdkPixbuf *pixbuf; + + pixbuf = cairo_surface_get_user_data (window->priv->mini_icon, &window_mini_icon_pixbuf_key); + + if (pixbuf == NULL) + { + int scaling_factor; + + pixbuf = gdk_pixbuf_get_from_surface (window->priv->mini_icon, + 0, + 0, + cairo_image_surface_get_width (window->priv->mini_icon), + cairo_image_surface_get_height (window->priv->mini_icon)); + + scaling_factor = _wnck_get_window_scaling_factor (); + pixbuf = gdk_pixbuf_scale_simple (pixbuf, + gdk_pixbuf_get_width (pixbuf) / scaling_factor, + gdk_pixbuf_get_height (pixbuf) / scaling_factor, + GDK_INTERP_BILINEAR); + + cairo_surface_set_user_data (window->priv->mini_icon, &window_mini_icon_pixbuf_key, pixbuf, g_object_unref); + } + + return pixbuf; + } + + return NULL; +} + +/** + * wnck_window_get_icon_surface: + * @window: a #WnckWindow. + * + * Gets the icon-surface to be used for @window. If no icon-surface was found, a + * fallback icon-surface is used. wnck_window_get_icon_is_fallback() can be used + * to tell if the icon-surface is the fallback icon-surface. + * + * Return value: (transfer full): a reference to the icon-surface for @window. + * The caller should unreference the returned cairo_surface_t + * once done with it. + **/ +cairo_surface_t* +wnck_window_get_icon_surface (WnckWindow *window) +{ + g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL); + + _wnck_window_load_icons (window); + + return cairo_surface_reference (window->priv->icon); +} + +/** + * wnck_window_get_mini_icon_surface: + * @window: a #WnckWindow. + * + * Gets the mini-icon-surface to be used for @window. If no mini-icon-surface + * was found, a fallback mini-icon-surface is used. wnck_window_get_icon_is_fallback() + * can be used to tell if the mini-icon-surface is the fallback mini-icon-surface. + * + * Return value: (transfer full): a reference to the mini-icon-surface for @window. + * The caller should unreference the returned cairo_surface_t + * once done with it. + **/ +cairo_surface_t* +wnck_window_get_mini_icon_surface (WnckWindow *window) { g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL); _wnck_window_load_icons (window); - return window->priv->mini_icon; + return cairo_surface_reference (window->priv->mini_icon); } /** diff --git a/libwnck/window.h b/libwnck/window.h index 47c6543..2bec086 100644 --- a/libwnck/window.h +++ b/libwnck/window.h @@ -33,6 +33,7 @@ #include #include #include +#include G_BEGIN_DECLS @@ -382,6 +383,8 @@ gboolean wnck_window_transient_is_most_recently_activated (WnckWindow *window); GdkPixbuf* wnck_window_get_icon (WnckWindow *window); GdkPixbuf* wnck_window_get_mini_icon (WnckWindow *window); +cairo_surface_t* wnck_window_get_icon_surface (WnckWindow *window); +cairo_surface_t* wnck_window_get_mini_icon_surface (WnckWindow *window); gboolean wnck_window_get_icon_is_fallback (WnckWindow *window); diff --git a/libwnck/wnck-image-menu-item-private.h b/libwnck/wnck-image-menu-item-private.h index 265289d..5c47517 100644 --- a/libwnck/wnck-image-menu-item-private.h +++ b/libwnck/wnck-image-menu-item-private.h @@ -34,6 +34,9 @@ GtkWidget *wnck_image_menu_item_new_with_label (const gchar *l void wnck_image_menu_item_set_image_from_icon_pixbuf (WnckImageMenuItem *item, GdkPixbuf *pixbuf); +void wnck_image_menu_item_set_image_from_icon_surface (WnckImageMenuItem *item, + cairo_surface_t *surface); + void wnck_image_menu_item_set_image_from_window (WnckImageMenuItem *item, WnckWindow *window); diff --git a/libwnck/wnck-image-menu-item.c b/libwnck/wnck-image-menu-item.c index e8e6d87..7f5efdc 100644 --- a/libwnck/wnck-image-menu-item.c +++ b/libwnck/wnck-image-menu-item.c @@ -219,6 +219,14 @@ wnck_image_menu_item_set_image_from_icon_pixbuf (WnckImageMenuItem *item, gtk_widget_show (item->image); } +void +wnck_image_menu_item_set_image_from_icon_surface (WnckImageMenuItem *item, + cairo_surface_t *surface) +{ + gtk_image_set_from_surface (GTK_IMAGE (item->image), surface); + gtk_widget_show (item->image); +} + void wnck_image_menu_item_set_image_from_window (WnckImageMenuItem *item, WnckWindow *window) -- 2.37.2