From a74cd3d8406e54a68868bc69f4117c3c25cf1f40 Mon Sep 17 00:00:00 2001 From: Victor Kareh Date: Mon, 14 Oct 2019 10:49:07 -0400 Subject: tabpopup: Render window thumbnail as cairo surface Instead of converting from surface to a GdkPixbuf and then back to a surface, we keep it as a surface for the entire manipulation flow. This improves rendering speed a bit and sets the ground for a higher resolution thumbnail in the future. --- src/core/screen.c | 178 +++++++++++++++++++++++++++---------------------- src/include/tabpopup.h | 1 + src/ui/tabpopup.c | 30 ++++++--- 3 files changed, 118 insertions(+), 91 deletions(-) diff --git a/src/core/screen.c b/src/core/screen.c index 49e50506..78a41d83 100644 --- a/src/core/screen.c +++ b/src/core/screen.c @@ -48,6 +48,7 @@ #endif #include +#include #include #include #include @@ -1212,60 +1213,60 @@ meta_screen_update_cursor (MetaScreen *screen) XFreeCursor (screen->display->xdisplay, xcursor); } -#define MAX_PREVIEW_SCALE 10.0 +#define MAX_PREVIEW_SCREEN_FRACTION 0.33 +#define MAX_PREVIEW_SIZE 300 -static GdkPixbuf * -get_window_pixbuf (MetaWindow *window, - int *width, - int *height) +static cairo_surface_t * +get_window_surface (MetaWindow *window) { - MetaDisplay *display; - cairo_surface_t *surface; - GdkPixbuf *pixbuf, *scaled; + cairo_surface_t *surface, *scaled; + cairo_t *cr; const MetaXineramaScreenInfo *current; + int width, height, max_columns, max_size; double ratio; - display = window->display; - surface = meta_compositor_get_window_surface (window->display->compositor, - window); - if (surface == None) - return NULL; - - meta_error_trap_push (display); - - pixbuf = meta_ui_get_pixbuf_from_surface (surface); - cairo_surface_destroy (surface); - - if (meta_error_trap_pop_with_return (display, FALSE) != Success) - g_clear_object (&pixbuf); + surface = meta_compositor_get_window_surface (window->display->compositor, window); - if (pixbuf == NULL) + if (surface == NULL) return NULL; - *width = gdk_pixbuf_get_width (pixbuf); - *height = gdk_pixbuf_get_height (pixbuf); + width = cairo_xlib_surface_get_width (surface); + height = cairo_xlib_surface_get_height (surface); current = meta_screen_get_current_xinerama (window->screen); + max_columns = meta_prefs_get_alt_tab_max_columns (); - /* Scale pixbuf to max dimension based on monitor size */ - if (*width > *height) + /* Scale surface to fit current screen */ + if (width > height) { - int max_preview_width = current->rect.width / MAX_PREVIEW_SCALE; - ratio = ((double) *width) / max_preview_width; - *width = (int) max_preview_width; - *height = (int) (((double) *height) / ratio); + max_size = MIN (MAX_PREVIEW_SIZE, current->rect.width / max_columns * MAX_PREVIEW_SCREEN_FRACTION); + ratio = ((double) width) / max_size; + width = (int) max_size; + height = (int) (((double) height) / ratio); } else { - int max_preview_height = current->rect.height / MAX_PREVIEW_SCALE; - ratio = ((double) *height) / max_preview_height; - *height = (int) max_preview_height; - *width = (int) (((double) *width) / ratio); + max_size = MIN (MAX_PREVIEW_SIZE, current->rect.height / max_columns * MAX_PREVIEW_SCREEN_FRACTION); + ratio = ((double) height) / max_size; + height = (int) max_size; + width = (int) (((double) width) / ratio); } - scaled = gdk_pixbuf_scale_simple (pixbuf, *width, *height, - GDK_INTERP_BILINEAR); - g_object_unref (pixbuf); + meta_error_trap_push (window->display); + scaled = cairo_surface_create_similar (surface, + cairo_surface_get_content (surface), + width, height); + if (meta_error_trap_pop_with_return (window->display, FALSE) != Success) + return NULL; + + cr = cairo_create (scaled); + cairo_scale (cr, 1/ratio, 1/ratio); + cairo_set_source_surface (cr, surface, 0, 0); + cairo_paint (cr); + + cairo_destroy (cr); + cairo_surface_destroy (surface); + return scaled; } @@ -1280,6 +1281,7 @@ meta_screen_ensure_tab_popup (MetaScreen *screen, int len; int i; gint border; + int scale; if (screen->tab_popup) return; @@ -1290,11 +1292,13 @@ meta_screen_ensure_tab_popup (MetaScreen *screen, screen->active_workspace); len = g_list_length (tab_list); + scale = gdk_window_get_scale_factor (gdk_get_default_root_window ()); entries = g_new (MetaTabEntry, len + 1); entries[len].key = NULL; entries[len].title = NULL; entries[len].icon = NULL; + entries[len].win_surface = NULL; border = meta_prefs_show_tab_border() ? BORDER_OUTLINE_TAB | BORDER_OUTLINE_WINDOW : BORDER_OUTLINE_TAB; @@ -1304,59 +1308,64 @@ meta_screen_ensure_tab_popup (MetaScreen *screen, { MetaWindow *window; MetaRectangle r; - GdkPixbuf *win_pixbuf = NULL; - int width = 0, height = 0; window = tmp->data; entries[i].key = (MetaTabEntryKey) window->xwindow; entries[i].title = window->title; + entries[i].win_surface = NULL; - /* Only get the pixbuf if the user does NOT have - compositing-fast-alt-tab-set to true - in GSettings. There is an obvious lag when the pixbuf is - retrieved. */ - if (!meta_prefs_get_compositing_fast_alt_tab()) - win_pixbuf = get_window_pixbuf (window, &width, &height); + entries[i].icon = g_object_ref (window->icon); - if (win_pixbuf == NULL) - entries[i].icon = g_object_ref (window->icon); - else + /* Only get the window thumbnail surface if the user has a compositor + * enabled and does NOT have compositing-fast-alt-tab-set to true in + * GSettings. There is an obvious lag when the surface is retrieved. */ + if (meta_prefs_get_compositing_manager() && !meta_prefs_get_compositing_fast_alt_tab()) { - int icon_width, icon_height, t_width, t_height; + cairo_surface_t *win_surface; + + /* Get window thumbnail */ + win_surface = get_window_surface (window); + + if (win_surface != NULL) + { + cairo_surface_t *surface, *icon; + cairo_t *cr; + int width, height, icon_width, icon_height; + #define ICON_OFFSET 6 - icon_width = gdk_pixbuf_get_width (window->icon); - icon_height = gdk_pixbuf_get_height (window->icon); - - t_width = width + ICON_OFFSET; - t_height = height + ICON_OFFSET; - - entries[i].icon = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, - t_width, t_height); - gdk_pixbuf_fill (entries[i].icon, 0x00000000); - gdk_pixbuf_copy_area (win_pixbuf, 0, 0, width, height, - entries[i].icon, 0, 0); - g_object_unref (win_pixbuf); - - double icon_scale = 1.0; - double max_coverage = 0.9; - - if (icon_width > t_width * max_coverage) - icon_scale = (t_width * max_coverage) / icon_width; - - if (icon_height * icon_scale > t_height * max_coverage) - icon_scale = (t_height * max_coverage) / icon_height; - - int t_icon_width = (int)(icon_width * icon_scale); - int t_icon_height = (int)(icon_height * icon_scale); - - gdk_pixbuf_composite (window->icon, entries[i].icon, - t_width - t_icon_width, - t_height - t_icon_height, - t_icon_width, t_icon_height, - t_width - t_icon_width, t_height - t_icon_height, - icon_scale, icon_scale, GDK_INTERP_BILINEAR, 255); + width = cairo_xlib_surface_get_width (win_surface); + height = cairo_xlib_surface_get_height (win_surface); + + /* Create a new surface to overlap the window icon into */ + surface = cairo_surface_create_similar (win_surface, + cairo_surface_get_content (win_surface), + width, height); + + cr = cairo_create (surface); + cairo_set_source_surface (cr, win_surface, 0, 0); + cairo_paint (cr); + + /* Get the window icon as a surface */ + icon = gdk_cairo_surface_create_from_pixbuf (window->icon, scale, NULL); + + icon_width = cairo_image_surface_get_width (icon) / scale; + icon_height = cairo_image_surface_get_height (icon) / scale; + + /* Overlap the window icon surface over the window thumbnail */ + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_set_source_surface (cr, icon, + width - icon_width - ICON_OFFSET, + height - icon_height - ICON_OFFSET); + cairo_paint (cr); + + entries[i].win_surface = surface; + + cairo_destroy (cr); + cairo_surface_destroy (icon); + cairo_surface_destroy (win_surface); + } } entries[i].blank = FALSE; @@ -1403,7 +1412,11 @@ meta_screen_ensure_tab_popup (MetaScreen *screen, border); for (i = 0; i < len; i++) - g_object_unref (entries[i].icon); + { + g_object_unref (entries[i].icon); + if (entries[i].win_surface) + cairo_surface_destroy (entries[i].win_surface); + } g_free (entries); @@ -1437,6 +1450,7 @@ meta_screen_ensure_workspace_popup (MetaScreen *screen) entries[len].key = NULL; entries[len].title = NULL; entries[len].icon = NULL; + entries[len].win_surface = NULL; i = 0; while (i < len) @@ -1451,6 +1465,7 @@ meta_screen_ensure_workspace_popup (MetaScreen *screen) entries[i].key = (MetaTabEntryKey) workspace; entries[i].title = meta_workspace_get_name (workspace); entries[i].icon = NULL; + entries[i].win_surface = NULL; entries[i].blank = FALSE; g_assert (entries[i].title != NULL); @@ -1460,6 +1475,7 @@ meta_screen_ensure_workspace_popup (MetaScreen *screen) entries[i].key = NULL; entries[i].title = NULL; entries[i].icon = NULL; + entries[i].win_surface = NULL; entries[i].blank = TRUE; } entries[i].hidden = FALSE; diff --git a/src/include/tabpopup.h b/src/include/tabpopup.h index 447264e1..19e8621b 100644 --- a/src/include/tabpopup.h +++ b/src/include/tabpopup.h @@ -49,6 +49,7 @@ struct _MetaTabEntry MetaTabEntryKey key; const char *title; GdkPixbuf *icon; + cairo_surface_t *win_surface; MetaRectangle rect; MetaRectangle inner_rect; guint blank : 1; diff --git a/src/ui/tabpopup.c b/src/ui/tabpopup.c index 013700ed..06ee84e9 100644 --- a/src/ui/tabpopup.c +++ b/src/ui/tabpopup.c @@ -50,6 +50,7 @@ struct _TabEntry gint grid_left; gint grid_top; GdkPixbuf *icon, *dimmed_icon; + cairo_surface_t *win_surface; GtkWidget *widget; GdkRectangle rect; GdkRectangle inner_rect; @@ -68,7 +69,7 @@ struct _MetaTabPopup gint border; }; -static GtkWidget* selectable_image_new (GdkPixbuf *pixbuf); +static GtkWidget* selectable_image_new (GdkPixbuf *pixbuf, cairo_surface_t *win_surface); static void select_image (GtkWidget *widget); static void unselect_image (GtkWidget *widget); @@ -203,6 +204,7 @@ tab_entry_new (const MetaTabEntry *entry, } te->widget = NULL; te->icon = entry->icon; + te->win_surface = entry->win_surface; te->blank = entry->blank; te->dimmed_icon = NULL; if (te->icon) @@ -373,11 +375,11 @@ meta_ui_tab_popup_new (const MetaTabEntry *entries, { if (te->dimmed_icon) { - image = selectable_image_new (te->dimmed_icon); + image = selectable_image_new (te->dimmed_icon, NULL); } else { - image = selectable_image_new (te->icon); + image = selectable_image_new (te->icon, te->win_surface); } gtk_misc_set_padding (GTK_MISC (image), @@ -751,19 +753,27 @@ struct _MetaSelectImageClass static GType meta_select_image_get_type (void) G_GNUC_CONST; static GtkWidget* -selectable_image_new (GdkPixbuf *pixbuf) +selectable_image_new (GdkPixbuf *pixbuf, cairo_surface_t *win_surface) { GtkWidget *widget; - int scale; - cairo_surface_t *surface; widget = g_object_new (meta_select_image_get_type (), NULL); - scale = gtk_widget_get_scale_factor (widget); - surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL); - gtk_image_set_from_surface (GTK_IMAGE (widget), surface); + if (win_surface == NULL) + { + int scale; + cairo_surface_t *surface; + + scale = gtk_widget_get_scale_factor (widget); + surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL); + + gtk_image_set_from_surface (GTK_IMAGE (widget), surface); + + cairo_surface_destroy (surface); + } + else + gtk_image_set_from_surface (GTK_IMAGE (widget), win_surface); - cairo_surface_destroy (surface); return widget; } -- cgit v1.2.1