diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/iconcache.c | 417 | ||||
-rw-r--r-- | src/core/iconcache.h | 21 | ||||
-rw-r--r-- | src/core/window.c | 22 |
3 files changed, 202 insertions, 258 deletions
diff --git a/src/core/iconcache.c b/src/core/iconcache.c index 45452544..1810e782 100644 --- a/src/core/iconcache.c +++ b/src/core/iconcache.c @@ -27,14 +27,15 @@ #include "errors.h" #include "window-private.h" +#include <cairo-xlib.h> #include <X11/Xatom.h> /* The icon-reading code is also in libwnck, please sync bugfixes */ static void -get_fallback_icons (MetaScreen *screen, - GdkPixbuf **iconp, - GdkPixbuf **mini_iconp) +get_fallback_icons (MetaScreen *screen, + cairo_surface_t **iconp, + cairo_surface_t **mini_iconp) { /* we don't scale, should be fixed if we ever un-hardcode the icon * size @@ -128,49 +129,65 @@ find_best_size (gulong *data, return FALSE; } -static void -argbdata_to_pixdata (gulong *argb_data, int len, guchar **pixdata) +static cairo_surface_t * +argbdata_to_surface (gulong *argb_data, + int w, + int h, + int ideal_w, + int ideal_h, + int scaling_factor) { - guchar *p; - int i; + cairo_surface_t *surface, *icon; + cairo_t *cr; + int x, y, stride; + uint32_t *data; - *pixdata = g_new (guchar, len * 4); - p = *pixdata; + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); + cairo_surface_set_device_scale (surface, (double)scaling_factor, (double)scaling_factor); + stride = cairo_image_surface_get_stride (surface) / sizeof (uint32_t); + data = (uint32_t *) cairo_image_surface_get_data (surface); /* One could speed this up a lot. */ - i = 0; - while (i < len) + for (y = 0; y < h; y++) { - guint argb; - guint rgba; - - argb = argb_data[i]; - rgba = (argb << 8) | (argb >> 24); - - *p = rgba >> 24; - ++p; - *p = (rgba >> 16) & 0xff; - ++p; - *p = (rgba >> 8) & 0xff; - ++p; - *p = rgba & 0xff; - ++p; - - ++i; + for (x = 0; x < w; x++) + { + uint32_t *p = &data[y * stride + x]; + gulong *d = &argb_data[y * w + x]; + *p = *d; + } } + + cairo_surface_mark_dirty (surface); + + icon = cairo_surface_create_similar_image (surface, + cairo_image_surface_get_format (surface), + ideal_w, ideal_h); + + cairo_surface_set_device_scale (icon, (double)scaling_factor, (double)scaling_factor); + + cr = cairo_create (icon); + cairo_scale (cr, ideal_w / (double)w, ideal_h / (double)h); + cairo_set_source_surface (cr, surface, 0, 0); + cairo_paint (cr); + + cairo_set_operator (cr, CAIRO_OPERATOR_IN); + cairo_paint (cr); + + cairo_destroy (cr); + cairo_surface_destroy (surface); + + return icon; } static gboolean -read_rgb_icon (MetaDisplay *display, - Window xwindow, - int ideal_size, - int ideal_mini_size, - int *width, - int *height, - guchar **pixdata, - int *mini_width, - int *mini_height, - guchar **mini_pixdata) +read_rgb_icon (MetaDisplay *display, + Window xwindow, + int ideal_size, + int ideal_mini_size, + cairo_surface_t **iconp, + cairo_surface_t **mini_iconp, + int scaling_factor) { Atom type; int format; @@ -221,14 +238,8 @@ read_rgb_icon (MetaDisplay *display, return FALSE; } - *width = w; - *height = h; - - *mini_width = mini_w; - *mini_height = mini_h; - - argbdata_to_pixdata (best, w * h, pixdata); - argbdata_to_pixdata (best_mini, mini_w * mini_h, mini_pixdata); + *iconp = argbdata_to_surface (best, w, h, ideal_size, ideal_size, scaling_factor); + *mini_iconp = argbdata_to_surface (best_mini, mini_w, mini_h, ideal_mini_size, ideal_mini_size, scaling_factor); XFree (data); @@ -236,12 +247,6 @@ read_rgb_icon (MetaDisplay *display, } static void -free_pixels (guchar *pixels, gpointer data) -{ - g_free (pixels); -} - -static void get_pixmap_geometry (MetaDisplay *display, Pixmap pixmap, int *w, @@ -273,128 +278,121 @@ get_pixmap_geometry (MetaDisplay *display, *d = depth; } -static GdkPixbuf* -apply_mask (GdkPixbuf *pixbuf, - GdkPixbuf *mask) +static gboolean +try_pixmap_and_mask (MetaDisplay *display, + Pixmap src_pixmap, + Pixmap src_mask, + cairo_surface_t **iconp, + int ideal_size, + cairo_surface_t **mini_iconp, + int ideal_mini_size, + int scaling_factor) { - int w, h; - int i, j; - GdkPixbuf *with_alpha; - guchar *src; - guchar *dest; - int src_stride; - int dest_stride; + cairo_surface_t *surface = NULL; + cairo_surface_t *mask_surface = NULL; + cairo_surface_t *image = NULL; + int width, height; + cairo_t *cr; - w = MIN (gdk_pixbuf_get_width (mask), gdk_pixbuf_get_width (pixbuf)); - h = MIN (gdk_pixbuf_get_height (mask), gdk_pixbuf_get_height (pixbuf)); + if (src_pixmap == None) + return FALSE; - with_alpha = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0); + meta_error_trap_push (display); - dest = gdk_pixbuf_get_pixels (with_alpha); - src = gdk_pixbuf_get_pixels (mask); + get_pixmap_geometry (display, src_pixmap, &width, &height, NULL); - dest_stride = gdk_pixbuf_get_rowstride (with_alpha); - src_stride = gdk_pixbuf_get_rowstride (mask); + surface = meta_cairo_surface_get_from_pixmap (display->xdisplay, + src_pixmap, + scaling_factor); - i = 0; - while (i < h) + if (surface && src_mask != None) { - j = 0; - while (j < w) - { - guchar *s = src + i * src_stride + j * 3; - guchar *d = dest + i * dest_stride + j * 4; + get_pixmap_geometry (display, src_mask, &width, &height, NULL); + mask_surface = meta_cairo_surface_get_from_pixmap (display->xdisplay, + src_mask, + scaling_factor); + } - /* s[0] == s[1] == s[2], they are 255 if the bit was set, 0 - * otherwise - */ - if (s[0] == 0) - d[3] = 0; /* transparent */ - else - d[3] = 255; /* opaque */ + width = cairo_xlib_surface_get_width (surface); + height = cairo_xlib_surface_get_height (surface); - ++j; - } + image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + width, height); + cr = cairo_create (image); - ++i; + /* Need special code for alpha-only surfaces. We only get those + * for bitmaps. And in that case, it's a differentiation between + * foreground (white) and background (black). + */ + if (cairo_surface_get_content (surface) & CAIRO_CONTENT_ALPHA) + { + cairo_push_group (cr); + + /* black background */ + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_paint (cr); + /* mask with white foreground */ + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_mask_surface (cr, surface, 0, 0); + + cairo_pop_group_to_source (cr); } + else + cairo_set_source_surface (cr, surface, 0, 0); - return with_alpha; -} + if (mask_surface) + { + cairo_mask_surface (cr, mask_surface, 0, 0); + cairo_surface_destroy (mask_surface); + } + else + cairo_paint (cr); -static gboolean -try_pixmap_and_mask (MetaDisplay *display, - Pixmap src_pixmap, - Pixmap src_mask, - GdkPixbuf **iconp, - int ideal_size, - GdkPixbuf **mini_iconp, - int ideal_mini_size) -{ - GdkPixbuf *unscaled = NULL; - GdkPixbuf *mask = NULL; - int w, h; + cairo_surface_destroy (surface); + cairo_destroy (cr); - if (src_pixmap == None) - return FALSE; + if (meta_error_trap_pop_with_return (display, FALSE) != Success) + { + cairo_surface_destroy (image); + return FALSE; + } - meta_error_trap_push (display); + if (image) + { + int image_w, image_h; - get_pixmap_geometry (display, src_pixmap, &w, &h, NULL); + image_w = cairo_image_surface_get_width (image); + image_h = cairo_image_surface_get_height (image); - unscaled = meta_gdk_pixbuf_get_from_pixmap (NULL, - src_pixmap, - 0, 0, 0, 0, - w, h); + *iconp = cairo_surface_create_similar (image, + cairo_surface_get_content (image), + ideal_size, + ideal_size); - if (unscaled && src_mask != None) - { - get_pixmap_geometry (display, src_mask, &w, &h, NULL); - mask = meta_gdk_pixbuf_get_from_pixmap (NULL, - src_mask, - 0, 0, 0, 0, - w, h); - } + cairo_surface_set_device_scale (*iconp, (double)scaling_factor, (double)scaling_factor); - meta_error_trap_pop (display, FALSE); + cr = cairo_create (*iconp); + cairo_scale (cr, ideal_size / (double)image_w, ideal_size / (double)image_h); + cairo_set_source_surface (cr, image, 0, 0); + cairo_paint (cr); + cairo_destroy (cr); - if (mask) - { - GdkPixbuf *masked; + *mini_iconp = cairo_surface_create_similar (image, + cairo_surface_get_content (image), + ideal_mini_size, + ideal_mini_size); - masked = apply_mask (unscaled, mask); - g_object_unref (G_OBJECT (unscaled)); - unscaled = masked; + cairo_surface_set_device_scale (*mini_iconp, (double)scaling_factor, (double)scaling_factor); - g_object_unref (G_OBJECT (mask)); - mask = NULL; - } + cr = cairo_create (*mini_iconp); + cairo_scale (cr, ideal_mini_size / (double)image_w, ideal_mini_size / (double)image_h); + cairo_set_source_surface (cr, image, 0, 0); + cairo_paint (cr); + cairo_destroy (cr); - if (unscaled) - { - *iconp = - gdk_pixbuf_scale_simple (unscaled, - ideal_size, - ideal_size, - GDK_INTERP_BILINEAR); - *mini_iconp = - gdk_pixbuf_scale_simple (unscaled, - ideal_mini_size, - ideal_mini_size, - GDK_INTERP_BILINEAR); - - g_object_unref (G_OBJECT (unscaled)); - - if (*iconp && *mini_iconp) - return TRUE; - else - { - if (*iconp) - g_object_unref (G_OBJECT (*iconp)); - if (*mini_iconp) - g_object_unref (G_OBJECT (*mini_iconp)); - return FALSE; - } + cairo_surface_destroy (image); + + return TRUE; } else return FALSE; @@ -551,10 +549,10 @@ meta_icon_cache_get_icon_invalidated (MetaIconCache *icon_cache) } static void -replace_cache (MetaIconCache *icon_cache, - IconOrigin origin, - GdkPixbuf *new_icon, - GdkPixbuf *new_mini_icon) +replace_cache (MetaIconCache *icon_cache, + IconOrigin origin, + cairo_surface_t *new_icon, + cairo_surface_t *new_mini_icon) { clear_icon_cache (icon_cache, FALSE); @@ -573,81 +571,19 @@ replace_cache (MetaIconCache *icon_cache, #endif } -static GdkPixbuf* -scaled_from_pixdata (guchar *pixdata, - int w, - int h, - int new_w, - int new_h) -{ - GdkPixbuf *src; - GdkPixbuf *dest; - - src = gdk_pixbuf_new_from_data (pixdata, - GDK_COLORSPACE_RGB, - TRUE, - 8, - w, h, w * 4, - free_pixels, - NULL); - - if (src == NULL) - return NULL; - - if (w != h) - { - GdkPixbuf *tmp; - int size; - - size = MAX (w, h); - - tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, size, size); - - if (tmp) - { - gdk_pixbuf_fill (tmp, 0); - gdk_pixbuf_copy_area (src, 0, 0, w, h, - tmp, - (size - w) / 2, (size - h) / 2); - - g_object_unref (src); - src = tmp; - } - } - - if (w != new_w || h != new_h) - { - dest = gdk_pixbuf_scale_simple (src, new_w, new_h, GDK_INTERP_BILINEAR); - - g_object_unref (G_OBJECT (src)); - } - else - { - dest = src; - } - - return dest; -} - gboolean -meta_read_icons (MetaScreen *screen, - Window xwindow, - char *res_name, - MetaIconCache *icon_cache, - Pixmap wm_hints_pixmap, - Pixmap wm_hints_mask, - GdkPixbuf **iconp, - int ideal_size, - GdkPixbuf **mini_iconp, - int ideal_mini_size) +meta_read_icons (MetaScreen *screen, + Window xwindow, + char *res_name, + MetaIconCache *icon_cache, + Pixmap wm_hints_pixmap, + Pixmap wm_hints_mask, + cairo_surface_t **iconp, + int ideal_size, + cairo_surface_t **mini_iconp, + int ideal_mini_size, + int scaling_factor) { - guchar *pixdata; - int w, h; - guchar *mini_pixdata; - int mini_w, mini_h; - Pixmap pixmap; - Pixmap mask; - /* Return value is whether the icon changed */ g_return_val_if_fail (icon_cache != NULL, FALSE); @@ -658,7 +594,6 @@ meta_read_icons (MetaScreen *screen, if (!meta_icon_cache_get_icon_invalidated (icon_cache)) return FALSE; /* we have no new info to use */ - pixdata = NULL; /* Our algorithm here assumes that we can't have for example origin * < USING_NET_WM_ICON and icon_cache->net_wm_icon_dirty == FALSE @@ -671,21 +606,15 @@ meta_read_icons (MetaScreen *screen, if (icon_cache->origin <= USING_NET_WM_ICON && icon_cache->net_wm_icon_dirty) - { icon_cache->net_wm_icon_dirty = FALSE; if (read_rgb_icon (screen->display, xwindow, ideal_size, ideal_mini_size, - &w, &h, &pixdata, - &mini_w, &mini_h, &mini_pixdata)) + iconp, mini_iconp, + scaling_factor)) { - *iconp = scaled_from_pixdata (pixdata, w, h, ideal_size, ideal_size); - - *mini_iconp = scaled_from_pixdata (mini_pixdata, mini_w, mini_h, - ideal_mini_size, ideal_mini_size); - if (*iconp && *mini_iconp) { replace_cache (icon_cache, USING_NET_WM_ICON, @@ -696,9 +625,9 @@ meta_read_icons (MetaScreen *screen, else { if (*iconp) - g_object_unref (G_OBJECT (*iconp)); + cairo_surface_destroy (*iconp); if (*mini_iconp) - g_object_unref (G_OBJECT (*mini_iconp)); + cairo_surface_destroy (*mini_iconp); } } } @@ -706,6 +635,9 @@ meta_read_icons (MetaScreen *screen, if (icon_cache->origin <= USING_WM_HINTS && icon_cache->wm_hints_dirty) { + Pixmap pixmap; + Pixmap mask; + icon_cache->wm_hints_dirty = FALSE; pixmap = wm_hints_pixmap; @@ -724,7 +656,8 @@ meta_read_icons (MetaScreen *screen, if (try_pixmap_and_mask (screen->display, pixmap, mask, iconp, ideal_size, - mini_iconp, ideal_mini_size)) + mini_iconp, ideal_mini_size, + scaling_factor)) { icon_cache->prev_pixmap = pixmap; icon_cache->prev_mask = mask; @@ -740,6 +673,9 @@ meta_read_icons (MetaScreen *screen, if (icon_cache->origin <= USING_KWM_WIN_ICON && icon_cache->kwm_win_icon_dirty) { + Pixmap pixmap; + Pixmap mask; + icon_cache->kwm_win_icon_dirty = FALSE; get_kwm_win_icon (screen->display, xwindow, &pixmap, &mask); @@ -753,7 +689,8 @@ meta_read_icons (MetaScreen *screen, if (try_pixmap_and_mask (screen->display, pixmap, mask, iconp, ideal_size, - mini_iconp, ideal_mini_size)) + mini_iconp, ideal_mini_size, + scaling_factor)) { icon_cache->prev_pixmap = pixmap; icon_cache->prev_mask = mask; diff --git a/src/core/iconcache.h b/src/core/iconcache.h index a558a945..11dc6617 100644 --- a/src/core/iconcache.h +++ b/src/core/iconcache.h @@ -65,16 +65,17 @@ void meta_icon_cache_property_changed (MetaIconCache *icon_cache, Atom atom); gboolean meta_icon_cache_get_icon_invalidated (MetaIconCache *icon_cache); -gboolean meta_read_icons (MetaScreen *screen, - Window xwindow, - char *res_name, - MetaIconCache *icon_cache, - Pixmap wm_hints_pixmap, - Pixmap wm_hints_mask, - GdkPixbuf **iconp, - int ideal_size, - GdkPixbuf **mini_iconp, - int ideal_mini_size); +gboolean meta_read_icons (MetaScreen *screen, + Window xwindow, + char *res_name, + MetaIconCache *icon_cache, + Pixmap wm_hints_pixmap, + Pixmap wm_hints_mask, + cairo_surface_t **iconp, + int ideal_size, + cairo_surface_t **mini_iconp, + int ideal_mini_size, + int scaling_factor); #endif diff --git a/src/core/window.c b/src/core/window.c index 4c90fee4..08cd99c7 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -6111,13 +6111,11 @@ redraw_icon (MetaWindow *window) void meta_window_update_icon_now (MetaWindow *window) { - GdkPixbuf *icon; - GdkPixbuf *mini_icon; - - icon = NULL; - mini_icon = NULL; + cairo_surface_t *icon = NULL; + cairo_surface_t *mini_icon = NULL; int icon_size = meta_prefs_get_icon_size(); + int scale = gdk_window_get_scale_factor (gdk_get_default_root_window ()); if (meta_read_icons (window->screen, window->xwindow, @@ -6128,7 +6126,8 @@ meta_window_update_icon_now (MetaWindow *window) &icon, icon_size, &mini_icon, - META_MINI_ICON_SIZE)) + META_MINI_ICON_SIZE, + scale)) { if (window->icon) g_object_unref (G_OBJECT (window->icon)); @@ -6136,8 +6135,15 @@ meta_window_update_icon_now (MetaWindow *window) if (window->mini_icon) g_object_unref (G_OBJECT (window->mini_icon)); - window->icon = icon; - window->mini_icon = mini_icon; + window->icon = gdk_pixbuf_get_from_surface (icon, + 0, 0, + icon_size, icon_size); + cairo_surface_destroy (icon); + + window->mini_icon = gdk_pixbuf_get_from_surface (mini_icon, + 0, 0, + META_MINI_ICON_SIZE, META_MINI_ICON_SIZE); + cairo_surface_destroy (mini_icon); redraw_icon (window); } |