From 45577d548d3abfe504d3f936b3c9b997d0cf7f9c Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 24 Mar 2016 16:46:28 -0700 Subject: [PATCH] Avoid GTK 3 crash with icons and masks MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Problem reported by Mosè Giordano (Bug#18997). * src/gtkutil.c (xg_get_pixbuf_from_pixmap): Remove. (xg_get_pixbuf_from_pix_and_mask): Do not use xg_get_pixbuf_from_pixmap, as it is poorly documented. Instead, invoke XGetPixel directly. This is slow but speed is not important here. Also, fail for unusual situations (not TrueColor, or images that are not 8 bits per sample) instead of displaying junk or crashing. --- src/gtkutil.c | 100 +++++++++++++++++--------------------------------- 1 file changed, 34 insertions(+), 66 deletions(-) diff --git a/src/gtkutil.c b/src/gtkutil.c index ba059b73a70..e791e6ac317 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -252,35 +252,6 @@ xg_create_default_cursor (Display *dpy) return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR); } -static GdkPixbuf * -xg_get_pixbuf_from_pixmap (struct frame *f, Pixmap pix) -{ - int iunused; - GdkPixbuf *tmp_buf; - Window wunused; - unsigned int width, height, uunused; - XImage *xim; - - XGetGeometry (FRAME_X_DISPLAY (f), pix, &wunused, &iunused, &iunused, - &width, &height, &uunused, &uunused); - - xim = XGetImage (FRAME_X_DISPLAY (f), pix, 0, 0, width, height, - ~0, XYPixmap); - if (!xim) return 0; - - tmp_buf = gdk_pixbuf_new_from_data ((guchar *) xim->data, - GDK_COLORSPACE_RGB, - FALSE, - xim->bitmap_unit, - width, - height, - xim->bytes_per_line, - NULL, - NULL); - XDestroyImage (xim); - return tmp_buf; -} - /* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel. */ static GdkPixbuf * @@ -288,46 +259,43 @@ xg_get_pixbuf_from_pix_and_mask (struct frame *f, Pixmap pix, Pixmap mask) { - int width, height; - GdkPixbuf *icon_buf, *tmp_buf; - - tmp_buf = xg_get_pixbuf_from_pixmap (f, pix); - icon_buf = gdk_pixbuf_add_alpha (tmp_buf, FALSE, 0, 0, 0); - g_object_unref (G_OBJECT (tmp_buf)); - - width = gdk_pixbuf_get_width (icon_buf); - height = gdk_pixbuf_get_height (icon_buf); + GdkPixbuf *icon_buf = 0; + int iunused; + Window wunused; + unsigned int width, height, depth, uunused; - if (mask) + if (FRAME_DISPLAY_INFO (f)->red_bits != 8) + return 0; + XGetGeometry (FRAME_X_DISPLAY (f), pix, &wunused, &iunused, &iunused, + &width, &height, &uunused, &depth); + if (depth != 24) + return 0; + XImage *xim = XGetImage (FRAME_X_DISPLAY (f), pix, 0, 0, width, height, + ~0, XYPixmap); + if (xim) { - GdkPixbuf *mask_buf = xg_get_pixbuf_from_pixmap (f, mask); - guchar *pixels = gdk_pixbuf_get_pixels (icon_buf); - guchar *mask_pixels = gdk_pixbuf_get_pixels (mask_buf); - int rowstride = gdk_pixbuf_get_rowstride (icon_buf); - int mask_rowstride = gdk_pixbuf_get_rowstride (mask_buf); - int y; - - for (y = 0; y < height; ++y) - { - guchar *iconptr, *maskptr; - int x; - - iconptr = pixels + y * rowstride; - maskptr = mask_pixels + y * mask_rowstride; - - for (x = 0; x < width; ++x) - { - /* In a bitmap, RGB is either 255/255/255 or 0/0/0. Checking - just R is sufficient. */ - if (maskptr[0] == 0) - iconptr[3] = 0; /* 0, 1, 2 is R, G, B. 3 is alpha. */ - - iconptr += rowstride/width; - maskptr += mask_rowstride/width; - } - } + XImage *xmm = (! mask ? 0 + : XGetImage (FRAME_X_DISPLAY (f), mask, 0, 0, + width, height, ~0, XYPixmap)); + icon_buf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height); + if (icon_buf) + { + guchar *pixels = gdk_pixbuf_get_pixels (icon_buf); + int rowjunkwidth = gdk_pixbuf_get_rowstride (icon_buf) - width * 4; + for (int y = 0; y < height; y++, pixels += rowjunkwidth) + for (int x = 0; x < width; x++) + { + unsigned long rgb = XGetPixel (xim, x, y); + *pixels++ = (rgb >> 16) & 255; + *pixels++ = (rgb >> 8) & 255; + *pixels++ = rgb & 255; + *pixels++ = xmm && !XGetPixel (xmm, x, y) ? 0 : 255; + } + } - g_object_unref (G_OBJECT (mask_buf)); + if (xmm) + XDestroyImage (xmm); + XDestroyImage (xim); } return icon_buf; -- 2.39.2