]> git.eshelyaron.com Git - emacs.git/commitdiff
Avoid GTK 3 crash with icons and masks
authorPaul Eggert <eggert@cs.ucla.edu>
Thu, 24 Mar 2016 23:46:28 +0000 (16:46 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Thu, 24 Mar 2016 23:53:23 +0000 (16:53 -0700)
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

index ba059b73a70750a7df5107293b0647ab37f04008..e791e6ac317152804cdfa48c6db36e20ae0f4adc 100644 (file)
@@ -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;