diff src/image.c @ 54341:80dfe07a1d65

New file (image.c) for Image consolidation: (COLOR_TABLE_SUPPORT): New define to control whether color table support is available (X only). (Bitmap_Record): Common name for x_bitmap_record, w32_bitmap_record, and mac_bitmap_record. (XImagePtr): Common name for pointer to XImage or equivalent. (XImagePtr_or_DC): New type to simplify code sharing; equivalent to XImagePtr on X+MAC, and to HDC on W32. (GET_PIXEL): Wrapper for XGetPixel or equivalent. (NO_PIXMAP): Common name for "None" or equivalent. (PNG_BG_COLOR_SHIFT): Bits to shift PNG background colors. (RGB_PIXEL_COLOR): Common type for an integer "pixel color" value. (PIX_MASK_RETAIN, PIX_MASK_DRAW): Portability macros (from macfns.c). (FRAME_X_VISUAL, x_defined_color, DefaultDepthOfScreen): Define with suitable equivalents on W32 and MAC for code sharing. (XDrawLine): Define on MAC for code sharing. (Destroy_Image, Free_Pixmap): Wrappers for code sharing. (IF_LIB_AVAILABLE): Macro to simplify code sharing. (Vx_bitmap_file_path, Vimage_cache_eviction_delay) (x_bitmap_height, x_bitmap_width, x_bitmap_pixmap) (x_reference_bitmap, x_create_bitmap_from_data) (x_create_bitmap_from_file, x_destroy_bitmap) (x_destroy_all_bitmaps, x_create_bitmap_mask) (XGetImage, XPutPixel, XGetPixel, XDestroyImage) (QCascent, QCmargin, QCrelief, QCconversion, QCcolor_symbols) (QCheuristic_mask, QCindex, QCmatrix, QCcolor_adjustment, QCmask) (Qlaplace, Qemboss, Qedge_detection, Qheuristic, Qcenter) (define_image_type, lookup_image_type, valid_image_p) (image_error, enum image_value_type, struct image_keyword) (parse_image_spec, image_spec_value, Fimage_size, Fimage_mask_p) (make_image, free_image, prepare_image_for_display, image_ascent) (four_corners_best, image_background, image_background_transparent) (x_clear_image_1, x_clear_image, x_alloc_image_color) (make_image_cache, free_image_cache, clear_image_cache) (Fclear_image_cache, postprocess_image, lookup_image, cache_image) (forall_images_in_image_cache, x_create_x_image_and_pixmap) (x_destroy_x_image, x_put_x_image, x_find_image_file, slurp_file) (find_image_fsspec, image_load_qt_1, image_load_quicktime) (init_image_func_pointer, image_load_quartz2d) (struct ct_color, init_color_table, free_color_table) (lookup_rgb_color, lookup_pixel_color, colors_in_color_table) (cross_disabled_images, x_to_xcolors, x_from_xcolors) (x_detect_edges, x_emboss, x_laplace, x_edge_detection) (x_disable_image, x_build_heuristic_mask) (XBM support, XPM support, PBM support, PNG support, JPEG support) (TIFF support, GIF support, Ghostscript support): Consolidate image code from xfns.c, w32fns.c, and macfns.c. (syms_of_image): Consolidate image related symbol setup here. (init_image): Consolidate image related initializations here.
author Kim F. Storm <storm@cua.dk>
date Thu, 11 Mar 2004 00:28:24 +0000
parents
children c0422921b537
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/image.c	Thu Mar 11 00:28:24 2004 +0000
@@ -0,0 +1,7539 @@
+/* Functions for image support on window system.
+   Copyright (C) 1989, 92, 93, 94, 95, 96, 97, 98, 99, 2000,01,02,03,04
+     Free Software Foundation.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#include <config.h>
+#include <signal.h>
+#include <stdio.h>
+#include <math.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* This makes the fields of a Display accessible, in Xlib header files.  */
+
+#define XLIB_ILLEGAL_ACCESS
+
+#include "lisp.h"
+#include "frame.h"
+#include "window.h"
+#include "dispextern.h"
+#include "blockinput.h"
+#include "systime.h"
+#include <epaths.h>
+
+
+#ifdef HAVE_X_WINDOWS
+#include "xterm.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define COLOR_TABLE_SUPPORT 1
+
+typedef struct x_bitmap_record Bitmap_Record;
+typedef XImage * XImagePtr;
+typedef XImagePtr XImagePtr_or_DC;
+#define GET_PIXEL(ximg, x, y) XGetPixel(ximg, x, y)
+#define NO_PIXMAP None
+#define PNG_BG_COLOR_SHIFT 0
+
+#define RGB_PIXEL_COLOR unsigned long
+
+#define PIX_MASK_RETAIN(f) 0
+#define PIX_MASK_DRAW(f) 1
+#endif /* HAVE_X_WINDOWS */
+
+
+#ifdef HAVE_NTGUI
+#include "w32term.h"
+
+/* W32_TODO : Color tables on W32.  */
+#undef COLOR_TABLE_SUPPORT
+
+typedef struct w32_bitmap_record Bitmap_Record;
+typedef XImage *XImagePtr;
+typedef HDC XImagePtr_or_DC;
+#define GET_PIXEL(ximg, x, y) GetPixel(ximg, x, y)
+#define NO_PIXMAP 0
+#define PNG_BG_COLOR_SHIFT 0
+
+#define RGB_PIXEL_COLOR COLORREF
+
+#define PIX_MASK_RETAIN(f) 0
+#define PIX_MASK_DRAW(f) 1
+
+#define FRAME_X_VISUAL(f) FRAME_X_DISPLAY_INFO (f)->visual
+#define x_defined_color w32_defined_color
+#define DefaultDepthOfScreen(screen) (one_w32_display_info.n_cbits)
+#endif /* HAVE_NTGUI */
+
+
+#ifdef MAC_OS
+#include "macterm.h"
+#ifndef MAC_OSX
+#include <alloca.h>
+#endif
+#ifdef MAC_OSX
+#include <QuickTime/QuickTime.h>
+#else /* not MAC_OSX */
+#include <Windows.h>
+#include <Gestalt.h>
+#include <TextUtils.h>
+#endif /* not MAC_OSX */
+
+/* MAC_TODO : Color tables on Mac.  */
+#undef COLOR_TABLE_SUPPORT
+
+/* Mac equivalent of XImage.  */
+typedef Pixmap XImagePtr;
+#define ZPixmap 0 		/* arbitrary */
+typedef struct mac_bitmap_record Bitmap_Record;
+
+typedef XImagePtr XImagePtr_or_DC;
+#define GET_PIXEL(ximg, x, y) XGetPixel(ximg, x, y)
+#define NO_PIXMAP 0
+#define PNG_BG_COLOR_SHIFT 8
+
+#define RGB_PIXEL_COLOR unsigned long
+
+#define FRAME_X_VISUAL(f) FRAME_X_DISPLAY_INFO (f)->visual
+#define x_defined_color mac_defined_color
+#define DefaultDepthOfScreen(screen) (one_mac_display_info.n_planes)
+#define XDrawLine(display, w, gc, x1, y1, x2, y2) \
+	mac_draw_line_to_pixmap(display, w, gc, x1, y1, x2, y2)
+
+#endif /* MAC_OS */
+
+
+/* Search path for bitmap files.  */
+
+Lisp_Object Vx_bitmap_file_path;
+
+
+static void x_disable_image P_ ((struct frame *, struct image *));
+static void x_edge_detection P_ ((struct frame *, struct image *, Lisp_Object,
+				  Lisp_Object));
+
+static void init_color_table P_ ((void));
+static unsigned long lookup_rgb_color P_ ((struct frame *f, int r, int g, int b));
+#ifdef COLOR_TABLE_SUPPORT
+static void free_color_table P_ ((void));
+static unsigned long *colors_in_color_table P_ ((int *n));
+static unsigned long lookup_pixel_color P_ ((struct frame *f, unsigned long p));
+#endif
+
+/* Code to deal with bitmaps.  Bitmaps are referenced by their bitmap
+   id, which is just an int that this section returns.  Bitmaps are
+   reference counted so they can be shared among frames.
+
+   Bitmap indices are guaranteed to be > 0, so a negative number can
+   be used to indicate no bitmap.
+
+   If you use x_create_bitmap_from_data, then you must keep track of
+   the bitmaps yourself.  That is, creating a bitmap from the same
+   data more than once will not be caught.  */
+
+#ifdef MAC_OS
+
+static XImagePtr
+XGetImage (display, pixmap, x, y, width, height, plane_mask, format)
+     Display *display;		/* not used */
+     Pixmap pixmap;
+     int x, y;			/* not used */
+     unsigned int width, height; /* not used */
+     unsigned long plane_mask; 	/* not used */
+     int format;		/* not used */
+{
+#if GLYPH_DEBUG
+  xassert (x == 0 && y == 0);
+  {
+    Rect ri, rp;
+    SetRect (&ri, 0, 0, width, height);
+    xassert (EqualRect (&ri, GetPixBounds (GetGWorldPixMap (pixmap), &rp)));
+  }
+  xassert (! (pixelsLocked & GetPixelsState (GetGWorldPixMap (pixmap))));
+#endif
+
+  LockPixels (GetGWorldPixMap (pixmap));
+
+  return pixmap;
+}
+
+static void
+XPutPixel (ximage, x, y, pixel)
+     XImagePtr ximage;
+     int x, y;
+     unsigned long pixel;
+{
+  RGBColor color;
+
+  SetGWorld (ximage, NULL);
+
+  color.red = RED16_FROM_ULONG (pixel);
+  color.green = GREEN16_FROM_ULONG (pixel);
+  color.blue = BLUE16_FROM_ULONG (pixel);
+  SetCPixel (x, y, &color);
+}
+
+static unsigned long
+XGetPixel (ximage, x, y)
+     XImagePtr ximage;
+     int x, y;
+{
+  RGBColor color;
+
+  SetGWorld (ximage, NULL);
+
+  GetCPixel (x, y, &color);
+  return RGB_TO_ULONG (color.red >> 8, color.green >> 8, color.blue >> 8);
+}
+
+static void
+XDestroyImage (ximg)
+     XImagePtr ximg;
+{
+  UnlockPixels (GetGWorldPixMap (ximg));
+}
+#endif
+
+
+/* Functions to access the contents of a bitmap, given an id.  */
+
+int
+x_bitmap_height (f, id)
+     FRAME_PTR f;
+     int id;
+{
+  return FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].height;
+}
+
+int
+x_bitmap_width (f, id)
+     FRAME_PTR f;
+     int id;
+{
+  return FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].width;
+}
+
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI)
+int
+x_bitmap_pixmap (f, id)
+     FRAME_PTR f;
+     int id;
+{
+  return (int) FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].pixmap;
+}
+#endif
+
+#ifdef HAVE_X_WINDOWS
+int
+x_bitmap_mask (f, id)
+     FRAME_PTR f;
+     int id;
+{
+  return FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].mask;
+}
+#endif
+
+/* Allocate a new bitmap record.  Returns index of new record.  */
+
+static int
+x_allocate_bitmap_record (f)
+     FRAME_PTR f;
+{
+  Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+  int i;
+
+  if (dpyinfo->bitmaps == NULL)
+    {
+      dpyinfo->bitmaps_size = 10;
+      dpyinfo->bitmaps
+	= (Bitmap_Record *) xmalloc (dpyinfo->bitmaps_size * sizeof (Bitmap_Record));
+      dpyinfo->bitmaps_last = 1;
+      return 1;
+    }
+
+  if (dpyinfo->bitmaps_last < dpyinfo->bitmaps_size)
+    return ++dpyinfo->bitmaps_last;
+
+  for (i = 0; i < dpyinfo->bitmaps_size; ++i)
+    if (dpyinfo->bitmaps[i].refcount == 0)
+      return i + 1;
+
+  dpyinfo->bitmaps_size *= 2;
+  dpyinfo->bitmaps
+    = (Bitmap_Record *) xrealloc (dpyinfo->bitmaps,
+				  dpyinfo->bitmaps_size * sizeof (Bitmap_Record));
+  return ++dpyinfo->bitmaps_last;
+}
+
+/* Add one reference to the reference count of the bitmap with id ID.  */
+
+void
+x_reference_bitmap (f, id)
+     FRAME_PTR f;
+     int id;
+{
+  ++FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].refcount;
+}
+
+/* Create a bitmap for frame F from a HEIGHT x WIDTH array of bits at BITS.  */
+
+int
+x_create_bitmap_from_data (f, bits, width, height)
+     struct frame *f;
+     char *bits;
+     unsigned int width, height;
+{
+  Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+  int id;
+
+#ifdef HAVE_X_WINDOWS
+  Pixmap bitmap;
+  bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+				  bits, width, height);
+  if (! bitmap)
+    return -1;
+#endif /* HAVE_X_WINDOWS */
+
+#ifdef HAVE_NTGUI
+  Pixmap bitmap;
+  bitmap = CreateBitmap (width, height,
+			 FRAME_X_DISPLAY_INFO (XFRAME (frame))->n_planes,
+			 FRAME_X_DISPLAY_INFO (XFRAME (frame))->n_cbits,
+			 bits);
+  if (! bitmap)
+    return -1;
+#endif /* HAVE_NTGUI */
+
+#ifdef MAC_OS
+  /* MAC_TODO: for now fail if width is not mod 16 (toolbox requires it) */
+  if (width % 16 != 0)
+    return -1;
+#endif
+
+  id = x_allocate_bitmap_record (f);
+#ifdef MAC_OS
+  dpyinfo->bitmaps[id - 1].bitmap_data = (char *) xmalloc (height * width);
+  if (! dpyinfo->bitmaps[id - 1].bitmap_data)
+    return -1;
+  bcopy (bits, dpyinfo->bitmaps[id - 1].bitmap_data, height * width);
+#endif  /* MAC_OS */
+
+  dpyinfo->bitmaps[id - 1].file = NULL;
+  dpyinfo->bitmaps[id - 1].height = height;
+  dpyinfo->bitmaps[id - 1].width = width;
+  dpyinfo->bitmaps[id - 1].refcount = 1;
+
+#ifdef HAVE_X_WINDOWS
+  dpyinfo->bitmaps[id - 1].pixmap = bitmap;
+  dpyinfo->bitmaps[id - 1].have_mask = 0;
+  dpyinfo->bitmaps[id - 1].depth = 1;
+#endif /* HAVE_X_WINDOWS */
+
+#ifdef HAVE_NTGUI
+  dpyinfo->bitmaps[id - 1].pixmap = bitmap;
+  dpyinfo->bitmaps[id - 1].hinst = NULL;
+  dpyinfo->bitmaps[id - 1].depth = 1;
+#endif /* HAVE_NTGUI */
+
+  return id;
+}
+
+/* Create bitmap from file FILE for frame F.  */
+
+int
+x_create_bitmap_from_file (f, file)
+     struct frame *f;
+     Lisp_Object file;
+{
+#ifdef MAC_OS
+  return -1;  /* MAC_TODO : bitmap support */
+#endif  /* MAC_OS */
+
+#ifdef HAVE_NTGUI
+  return -1;  /* W32_TODO : bitmap support */
+#endif /* HAVE_NTGUI */
+
+#ifdef HAVE_X_WINDOWS
+  Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+  unsigned int width, height;
+  Pixmap bitmap;
+  int xhot, yhot, result, id;
+  Lisp_Object found;
+  int fd;
+  char *filename;
+
+  /* Look for an existing bitmap with the same name.  */
+  for (id = 0; id < dpyinfo->bitmaps_last; ++id)
+    {
+      if (dpyinfo->bitmaps[id].refcount
+	  && dpyinfo->bitmaps[id].file
+	  && !strcmp (dpyinfo->bitmaps[id].file, (char *) SDATA (file)))
+	{
+	  ++dpyinfo->bitmaps[id].refcount;
+	  return id + 1;
+	}
+    }
+
+  /* Search bitmap-file-path for the file, if appropriate.  */
+  fd = openp (Vx_bitmap_file_path, file, Qnil, &found, Qnil);
+  if (fd < 0)
+    return -1;
+  emacs_close (fd);
+
+  filename = (char *) SDATA (found);
+
+  result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+			    filename, &width, &height, &bitmap, &xhot, &yhot);
+  if (result != BitmapSuccess)
+    return -1;
+
+  id = x_allocate_bitmap_record (f);
+  dpyinfo->bitmaps[id - 1].pixmap = bitmap;
+  dpyinfo->bitmaps[id - 1].have_mask = 0;
+  dpyinfo->bitmaps[id - 1].refcount = 1;
+  dpyinfo->bitmaps[id - 1].file = (char *) xmalloc (SBYTES (file) + 1);
+  dpyinfo->bitmaps[id - 1].depth = 1;
+  dpyinfo->bitmaps[id - 1].height = height;
+  dpyinfo->bitmaps[id - 1].width = width;
+  strcpy (dpyinfo->bitmaps[id - 1].file, SDATA (file));
+
+  return id;
+#endif /* HAVE_X_WINDOWS */
+}
+
+/* Free bitmap B.  */
+
+static void
+Free_Bitmap_Record (dpyinfo, bm)
+     Display_Info *dpyinfo;
+     Bitmap_Record *bm;
+{
+#ifdef HAVE_X_WINDOWS
+  XFreePixmap (dpyinfo->display, bm->pixmap);
+  if (bm->have_mask)
+    XFreePixmap (dpyinfo->display, bm->mask);
+#endif /* HAVE_X_WINDOWS */
+
+#ifdef HAVE_NTGUI
+  DeleteObject (bm->pixmap);
+#endif /* HAVE_NTGUI */
+
+#ifdef MAC_OS
+  xfree (bm->bitmap_data);  /* Added ++kfs */
+  bm->bitmap_data = NULL;
+#endif  /* MAC_OS */
+
+  if (bm->file)
+    {
+      xfree (bm->file);
+      bm->file = NULL;
+    }
+}
+
+/* Remove reference to bitmap with id number ID.  */
+
+void
+x_destroy_bitmap (f, id)
+     FRAME_PTR f;
+     int id;
+{
+  Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+
+  if (id > 0)
+    {
+      Bitmap_Record *bm = &dpyinfo->bitmaps[id - 1];
+
+      if (--bm->refcount == 0)
+	{
+	  BLOCK_INPUT;
+	  Free_Bitmap_Record (dpyinfo, bm);
+	  UNBLOCK_INPUT;
+	}
+    }
+}
+
+/* Free all the bitmaps for the display specified by DPYINFO.  */
+
+void
+x_destroy_all_bitmaps (dpyinfo)
+     Display_Info *dpyinfo;
+{
+  int i;
+  Bitmap_Record *bm = dpyinfo->bitmaps;
+
+  for (i = 0; i < dpyinfo->bitmaps_last; i++, bm++)
+    if (bm->refcount > 0)
+      Free_Bitmap_Record (dpyinfo, bm);
+
+  dpyinfo->bitmaps_last = 0;
+}
+
+
+#ifdef HAVE_X_WINDOWS
+
+/* Useful functions defined in the section
+   `Image type independent image structures' below. */
+
+static unsigned long four_corners_best P_ ((XImagePtr ximg, unsigned long width,
+					    unsigned long height));
+
+static int x_create_x_image_and_pixmap P_ ((struct frame *f, int width, int height,
+					    int depth, XImagePtr *ximg,
+					    Pixmap *pixmap));
+
+static void x_destroy_x_image P_ ((XImagePtr ximg));
+
+
+/* Create a mask of a bitmap. Note is this not a perfect mask.
+   It's nicer with some borders in this context */
+
+int
+x_create_bitmap_mask (f, id)
+     struct frame *f;
+     int id;
+{
+  Pixmap pixmap, mask;
+  XImagePtr ximg, mask_img;
+  unsigned long width, height;
+  int result;
+  unsigned long bg;
+  unsigned long x, y, xp, xm, yp, ym;
+  GC gc;
+
+  Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+
+  if (!(id > 0))
+    return -1;
+
+  pixmap = x_bitmap_pixmap (f, id);
+  width = x_bitmap_width (f, id);
+  height = x_bitmap_height (f, id);
+
+  BLOCK_INPUT;
+  ximg = XGetImage (FRAME_X_DISPLAY (f), pixmap, 0, 0, width, height,
+		    ~0, ZPixmap);
+
+  if (!ximg)
+    {
+      UNBLOCK_INPUT;
+      return -1;
+    }
+
+  result = x_create_x_image_and_pixmap (f, width, height, 1, &mask_img, &mask);
+
+  UNBLOCK_INPUT;
+  if (!result)
+    {
+      XDestroyImage (ximg);
+      return -1;
+    }
+
+  bg = four_corners_best (ximg, width, height);
+
+  for (y = 0; y < ximg->height; ++y)
+    {
+      for (x = 0; x < ximg->width; ++x)
+	{
+	  xp = x != ximg->width - 1 ? x + 1 : 0;
+	  xm = x != 0 ? x - 1 : ximg->width - 1;
+	  yp = y != ximg->height - 1 ? y + 1 : 0;
+	  ym = y != 0 ? y - 1 : ximg->height - 1;
+	  if (XGetPixel (ximg, x, y) == bg
+	      && XGetPixel (ximg, x, yp) == bg
+	      && XGetPixel (ximg, x, ym) == bg
+	      && XGetPixel (ximg, xp, y) == bg
+	      && XGetPixel (ximg, xp, yp) == bg
+	      && XGetPixel (ximg, xp, ym) == bg
+	      && XGetPixel (ximg, xm, y) == bg
+	      && XGetPixel (ximg, xm, yp) == bg
+	      && XGetPixel (ximg, xm, ym) == bg)
+	    XPutPixel (mask_img, x, y, 0);
+	  else
+	    XPutPixel (mask_img, x, y, 1);
+	}
+    }
+
+  xassert (interrupt_input_blocked);
+  gc = XCreateGC (FRAME_X_DISPLAY (f), mask, 0, NULL);
+  XPutImage (FRAME_X_DISPLAY (f), mask, gc, mask_img, 0, 0, 0, 0,
+	     width, height);
+  XFreeGC (FRAME_X_DISPLAY (f), gc);
+
+  dpyinfo->bitmaps[id - 1].have_mask = 1;
+  dpyinfo->bitmaps[id - 1].mask = mask;
+
+  XDestroyImage (ximg);
+  x_destroy_x_image (mask_img);
+
+  return 0;
+}
+
+#endif /* HAVE_X_WINDOWS */
+
+
+/***********************************************************************
+			    Image types
+ ***********************************************************************/
+
+/* Value is the number of elements of vector VECTOR.  */
+
+#define DIM(VECTOR)	(sizeof (VECTOR) / sizeof *(VECTOR))
+
+/* List of supported image types.  Use define_image_type to add new
+   types.  Use lookup_image_type to find a type for a given symbol.  */
+
+static struct image_type *image_types;
+
+/* The symbol `xbm' which is used as the type symbol for XBM images.  */
+
+Lisp_Object Qxbm;
+
+/* Keywords.  */
+
+extern Lisp_Object QCwidth, QCheight, QCforeground, QCbackground, QCfile;
+extern Lisp_Object QCdata, QCtype;
+Lisp_Object QCascent, QCmargin, QCrelief;
+Lisp_Object QCconversion, QCcolor_symbols, QCheuristic_mask;
+Lisp_Object QCindex, QCmatrix, QCcolor_adjustment, QCmask;
+
+/* Other symbols.  */
+
+Lisp_Object Qlaplace, Qemboss, Qedge_detection, Qheuristic;
+Lisp_Object Qcenter;
+
+/* Time in seconds after which images should be removed from the cache
+   if not displayed.  */
+
+Lisp_Object Vimage_cache_eviction_delay;
+
+/* Function prototypes.  */
+
+static void define_image_type P_ ((struct image_type *type));
+static struct image_type *lookup_image_type P_ ((Lisp_Object symbol));
+static void image_error P_ ((char *format, Lisp_Object, Lisp_Object));
+static void x_laplace P_ ((struct frame *, struct image *));
+static void x_emboss P_ ((struct frame *, struct image *));
+static int x_build_heuristic_mask P_ ((struct frame *, struct image *,
+				       Lisp_Object));
+
+
+/* Define a new image type from TYPE.  This adds a copy of TYPE to
+   image_types and adds the symbol *TYPE->type to Vimage_types.  */
+
+static void
+define_image_type (type)
+     struct image_type *type;
+{
+  /* Make a copy of TYPE to avoid a bus error in a dumped Emacs.
+     The initialized data segment is read-only.  */
+  struct image_type *p = (struct image_type *) xmalloc (sizeof *p);
+  bcopy (type, p, sizeof *p);
+  p->next = image_types;
+  image_types = p;
+  Vimage_types = Fcons (*p->type, Vimage_types);
+}
+
+
+/* Look up image type SYMBOL, and return a pointer to its image_type
+   structure.  Value is null if SYMBOL is not a known image type.  */
+
+static INLINE struct image_type *
+lookup_image_type (symbol)
+     Lisp_Object symbol;
+{
+  struct image_type *type;
+
+  for (type = image_types; type; type = type->next)
+    if (EQ (symbol, *type->type))
+      break;
+
+  return type;
+}
+
+
+/* Value is non-zero if OBJECT is a valid Lisp image specification.  A
+   valid image specification is a list whose car is the symbol
+   `image', and whose rest is a property list.  The property list must
+   contain a value for key `:type'.  That value must be the name of a
+   supported image type.  The rest of the property list depends on the
+   image type.  */
+
+int
+valid_image_p (object)
+     Lisp_Object object;
+{
+  int valid_p = 0;
+
+  if (IMAGEP (object))
+    {
+      Lisp_Object tem;
+
+      for (tem = XCDR (object); CONSP (tem); tem = XCDR (tem))
+	if (EQ (XCAR (tem), QCtype))
+	  {
+	    tem = XCDR (tem);
+	    if (CONSP (tem) && SYMBOLP (XCAR (tem)))
+	      {
+		struct image_type *type;
+		type = lookup_image_type (XCAR (tem));
+		if (type)
+		  valid_p = type->valid_p (object);
+	      }
+
+	    break;
+	  }
+    }
+
+  return valid_p;
+}
+
+
+/* Log error message with format string FORMAT and argument ARG.
+   Signaling an error, e.g. when an image cannot be loaded, is not a
+   good idea because this would interrupt redisplay, and the error
+   message display would lead to another redisplay.  This function
+   therefore simply displays a message.  */
+
+static void
+image_error (format, arg1, arg2)
+     char *format;
+     Lisp_Object arg1, arg2;
+{
+  add_to_log (format, arg1, arg2);
+}
+
+
+
+/***********************************************************************
+			 Image specifications
+ ***********************************************************************/
+
+enum image_value_type
+{
+  IMAGE_DONT_CHECK_VALUE_TYPE,
+  IMAGE_STRING_VALUE,
+  IMAGE_STRING_OR_NIL_VALUE,
+  IMAGE_SYMBOL_VALUE,
+  IMAGE_POSITIVE_INTEGER_VALUE,
+  IMAGE_POSITIVE_INTEGER_VALUE_OR_PAIR,
+  IMAGE_NON_NEGATIVE_INTEGER_VALUE,
+  IMAGE_ASCENT_VALUE,
+  IMAGE_INTEGER_VALUE,
+  IMAGE_FUNCTION_VALUE,
+  IMAGE_NUMBER_VALUE,
+  IMAGE_BOOL_VALUE
+};
+
+/* Structure used when parsing image specifications.  */
+
+struct image_keyword
+{
+  /* Name of keyword.  */
+  char *name;
+
+  /* The type of value allowed.  */
+  enum image_value_type type;
+
+  /* Non-zero means key must be present.  */
+  int mandatory_p;
+
+  /* Used to recognize duplicate keywords in a property list.  */
+  int count;
+
+  /* The value that was found.  */
+  Lisp_Object value;
+};
+
+
+static int parse_image_spec P_ ((Lisp_Object, struct image_keyword *,
+				 int, Lisp_Object));
+static Lisp_Object image_spec_value P_ ((Lisp_Object, Lisp_Object, int *));
+
+
+/* Parse image spec SPEC according to KEYWORDS.  A valid image spec
+   has the format (image KEYWORD VALUE ...).  One of the keyword/
+   value pairs must be `:type TYPE'.  KEYWORDS is a vector of
+   image_keywords structures of size NKEYWORDS describing other
+   allowed keyword/value pairs.  Value is non-zero if SPEC is valid.  */
+
+static int
+parse_image_spec (spec, keywords, nkeywords, type)
+     Lisp_Object spec;
+     struct image_keyword *keywords;
+     int nkeywords;
+     Lisp_Object type;
+{
+  int i;
+  Lisp_Object plist;
+
+  if (!IMAGEP (spec))
+    return 0;
+
+  plist = XCDR (spec);
+  while (CONSP (plist))
+    {
+      Lisp_Object key, value;
+
+      /* First element of a pair must be a symbol.  */
+      key = XCAR (plist);
+      plist = XCDR (plist);
+      if (!SYMBOLP (key))
+	return 0;
+
+      /* There must follow a value.  */
+      if (!CONSP (plist))
+	return 0;
+      value = XCAR (plist);
+      plist = XCDR (plist);
+
+      /* Find key in KEYWORDS.  Error if not found.  */
+      for (i = 0; i < nkeywords; ++i)
+	if (strcmp (keywords[i].name, SDATA (SYMBOL_NAME (key))) == 0)
+	  break;
+
+      if (i == nkeywords)
+	continue;
+
+      /* Record that we recognized the keyword.  If a keywords
+	 was found more than once, it's an error.  */
+      keywords[i].value = value;
+      ++keywords[i].count;
+
+      if (keywords[i].count > 1)
+	return 0;
+
+      /* Check type of value against allowed type.  */
+      switch (keywords[i].type)
+	{
+	case IMAGE_STRING_VALUE:
+	  if (!STRINGP (value))
+	    return 0;
+	  break;
+
+	case IMAGE_STRING_OR_NIL_VALUE:
+	  if (!STRINGP (value) && !NILP (value))
+	    return 0;
+	  break;
+
+	case IMAGE_SYMBOL_VALUE:
+	  if (!SYMBOLP (value))
+	    return 0;
+	  break;
+
+	case IMAGE_POSITIVE_INTEGER_VALUE:
+	  if (!INTEGERP (value) || XINT (value) <= 0)
+	    return 0;
+	  break;
+
+	case IMAGE_POSITIVE_INTEGER_VALUE_OR_PAIR:
+	  if (INTEGERP (value) && XINT (value) >= 0)
+	    break;
+	  if (CONSP (value)
+	      && INTEGERP (XCAR (value)) && INTEGERP (XCDR (value))
+	      && XINT (XCAR (value)) >= 0 && XINT (XCDR (value)) >= 0)
+	    break;
+	  return 0;
+
+	case IMAGE_ASCENT_VALUE:
+	  if (SYMBOLP (value) && EQ (value, Qcenter))
+	    break;
+	  else if (INTEGERP (value)
+		   && XINT (value) >= 0
+		   && XINT (value) <= 100)
+	    break;
+	  return 0;
+
+	case IMAGE_NON_NEGATIVE_INTEGER_VALUE:
+	  if (!INTEGERP (value) || XINT (value) < 0)
+	    return 0;
+	  break;
+
+	case IMAGE_DONT_CHECK_VALUE_TYPE:
+	  break;
+
+	case IMAGE_FUNCTION_VALUE:
+	  value = indirect_function (value);
+	  if (SUBRP (value)
+	      || COMPILEDP (value)
+	      || (CONSP (value) && EQ (XCAR (value), Qlambda)))
+	    break;
+	  return 0;
+
+	case IMAGE_NUMBER_VALUE:
+	  if (!INTEGERP (value) && !FLOATP (value))
+	    return 0;
+	  break;
+
+	case IMAGE_INTEGER_VALUE:
+	  if (!INTEGERP (value))
+	    return 0;
+	  break;
+
+	case IMAGE_BOOL_VALUE:
+	  if (!NILP (value) && !EQ (value, Qt))
+	    return 0;
+	  break;
+
+	default:
+	  abort ();
+	  break;
+	}
+
+      if (EQ (key, QCtype) && !EQ (type, value))
+	return 0;
+    }
+
+  /* Check that all mandatory fields are present.  */
+  for (i = 0; i < nkeywords; ++i)
+    if (keywords[i].mandatory_p && keywords[i].count == 0)
+      return 0;
+
+  return NILP (plist);
+}
+
+
+/* Return the value of KEY in image specification SPEC.  Value is nil
+   if KEY is not present in SPEC.  if FOUND is not null, set *FOUND
+   to 1 if KEY was found in SPEC, set it to 0 otherwise.  */
+
+static Lisp_Object
+image_spec_value (spec, key, found)
+     Lisp_Object spec, key;
+     int *found;
+{
+  Lisp_Object tail;
+
+  xassert (valid_image_p (spec));
+
+  for (tail = XCDR (spec);
+       CONSP (tail) && CONSP (XCDR (tail));
+       tail = XCDR (XCDR (tail)))
+    {
+      if (EQ (XCAR (tail), key))
+	{
+	  if (found)
+	    *found = 1;
+	  return XCAR (XCDR (tail));
+	}
+    }
+
+  if (found)
+    *found = 0;
+  return Qnil;
+}
+
+
+DEFUN ("image-size", Fimage_size, Simage_size, 1, 3, 0,
+       doc: /* Return the size of image SPEC as pair (WIDTH . HEIGHT).
+PIXELS non-nil means return the size in pixels, otherwise return the
+size in canonical character units.
+FRAME is the frame on which the image will be displayed.  FRAME nil
+or omitted means use the selected frame.  */)
+     (spec, pixels, frame)
+     Lisp_Object spec, pixels, frame;
+{
+  Lisp_Object size;
+
+  size = Qnil;
+  if (valid_image_p (spec))
+    {
+      struct frame *f = check_x_frame (frame);
+      int id = lookup_image (f, spec);
+      struct image *img = IMAGE_FROM_ID (f, id);
+      int width = img->width + 2 * img->hmargin;
+      int height = img->height + 2 * img->vmargin;
+
+      if (NILP (pixels))
+	size = Fcons (make_float ((double) width / FRAME_COLUMN_WIDTH (f)),
+		      make_float ((double) height / FRAME_LINE_HEIGHT (f)));
+      else
+	size = Fcons (make_number (width), make_number (height));
+    }
+  else
+    error ("Invalid image specification");
+
+  return size;
+}
+
+
+DEFUN ("image-mask-p", Fimage_mask_p, Simage_mask_p, 1, 2, 0,
+       doc: /* Return t if image SPEC has a mask bitmap.
+FRAME is the frame on which the image will be displayed.  FRAME nil
+or omitted means use the selected frame.  */)
+     (spec, frame)
+     Lisp_Object spec, frame;
+{
+  Lisp_Object mask;
+
+  mask = Qnil;
+  if (valid_image_p (spec))
+    {
+      struct frame *f = check_x_frame (frame);
+      int id = lookup_image (f, spec);
+      struct image *img = IMAGE_FROM_ID (f, id);
+      if (img->mask)
+	mask = Qt;
+    }
+  else
+    error ("Invalid image specification");
+
+  return mask;
+}
+
+
+/***********************************************************************
+		 Image type independent image structures
+ ***********************************************************************/
+
+static struct image *make_image P_ ((Lisp_Object spec, unsigned hash));
+static void free_image P_ ((struct frame *f, struct image *img));
+
+
+/* Allocate and return a new image structure for image specification
+   SPEC.  SPEC has a hash value of HASH.  */
+
+static struct image *
+make_image (spec, hash)
+     Lisp_Object spec;
+     unsigned hash;
+{
+  struct image *img = (struct image *) xmalloc (sizeof *img);
+
+  xassert (valid_image_p (spec));
+  bzero (img, sizeof *img);
+  img->type = lookup_image_type (image_spec_value (spec, QCtype, NULL));
+  xassert (img->type != NULL);
+  img->spec = spec;
+  img->data.lisp_val = Qnil;
+  img->ascent = DEFAULT_IMAGE_ASCENT;
+  img->hash = hash;
+  return img;
+}
+
+
+/* Free image IMG which was used on frame F, including its resources.  */
+
+static void
+free_image (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  if (img)
+    {
+      struct image_cache *c = FRAME_X_IMAGE_CACHE (f);
+
+      /* Remove IMG from the hash table of its cache.  */
+      if (img->prev)
+	img->prev->next = img->next;
+      else
+	c->buckets[img->hash % IMAGE_CACHE_BUCKETS_SIZE] = img->next;
+
+      if (img->next)
+	img->next->prev = img->prev;
+
+      c->images[img->id] = NULL;
+
+      /* Free resources, then free IMG.  */
+      img->type->free (f, img);
+      xfree (img);
+    }
+}
+
+
+/* Prepare image IMG for display on frame F.  Must be called before
+   drawing an image.  */
+
+void
+prepare_image_for_display (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  EMACS_TIME t;
+
+  /* We're about to display IMG, so set its timestamp to `now'.  */
+  EMACS_GET_TIME (t);
+  img->timestamp = EMACS_SECS (t);
+
+  /* If IMG doesn't have a pixmap yet, load it now, using the image
+     type dependent loader function.  */
+  if (img->pixmap == NO_PIXMAP && !img->load_failed_p)
+    img->load_failed_p = img->type->load (f, img) == 0;
+}
+
+
+/* Value is the number of pixels for the ascent of image IMG when
+   drawn in face FACE.  */
+
+int
+image_ascent (img, face)
+     struct image *img;
+     struct face *face;
+{
+  int height = img->height + img->vmargin;
+  int ascent;
+
+  if (img->ascent == CENTERED_IMAGE_ASCENT)
+    {
+      if (face->font)
+	{
+#ifdef HAVE_NTGUI
+	  /* W32 specific version.  Why?. ++kfs  */
+	  ascent = height / 2 - (FONT_DESCENT(face->font)
+				 - FONT_BASE(face->font)) / 2;
+#else
+	  /* This expression is arranged so that if the image can't be
+	     exactly centered, it will be moved slightly up.  This is
+	     because a typical font is `top-heavy' (due to the presence
+	     uppercase letters), so the image placement should err towards
+	     being top-heavy too.  It also just generally looks better.  */
+	  ascent = (height + face->font->ascent - face->font->descent + 1) / 2;
+#endif /* HAVE_NTGUI */
+	}
+      else
+	ascent = height / 2;
+    }
+  else
+    ascent = (int) (height * img->ascent / 100.0);
+
+  return ascent;
+}
+
+
+/* Image background colors.  */
+
+/* Find the "best" corner color of a bitmap.
+   On W32, XIMG is assumed to a device context with the bitmap selected.  */
+
+static RGB_PIXEL_COLOR
+four_corners_best (ximg, width, height)
+     XImagePtr_or_DC ximg;
+     unsigned long width, height;
+{
+  RGB_PIXEL_COLOR corners[4], best;
+  int i, best_count;
+
+  /* Get the colors at the corners of ximg.  */
+  corners[0] = GET_PIXEL (ximg, 0, 0);
+  corners[1] = GET_PIXEL (ximg, width - 1, 0);
+  corners[2] = GET_PIXEL (ximg, width - 1, height - 1);
+  corners[3] = GET_PIXEL (ximg, 0, height - 1);
+
+  /* Choose the most frequently found color as background.  */
+  for (i = best_count = 0; i < 4; ++i)
+    {
+      int j, n;
+
+      for (j = n = 0; j < 4; ++j)
+	if (corners[i] == corners[j])
+	  ++n;
+
+      if (n > best_count)
+	best = corners[i], best_count = n;
+    }
+
+  return best;
+}
+
+/* Portability macros */
+
+#ifdef HAVE_NTGUI
+
+#define Destroy_Image(img_dc, prev) \
+  do { SelectObject (img_dc, prev); DeleteDC (img_dc); } while (0)
+
+#define Free_Pixmap(display, pixmap) \
+  DeleteObject (pixmap)
+
+#else
+
+#define Destroy_Image(ximg, dummy) \
+  XDestroyImage (ximg)
+
+#define Free_Pixmap(display, pixmap) \
+  XFreePixmap (display, pixmap)
+
+#endif /* HAVE_NTGUI */
+
+
+/* Return the `background' field of IMG.  If IMG doesn't have one yet,
+   it is guessed heuristically.  If non-zero, XIMG is an existing
+   XImage object (or device context with the image selected on W32) to
+   use for the heuristic.  */ 
+
+RGB_PIXEL_COLOR
+image_background (img, f, ximg)
+     struct image *img;
+     struct frame *f;
+     XImagePtr_or_DC ximg;
+{
+  if (! img->background_valid)
+    /* IMG doesn't have a background yet, try to guess a reasonable value.  */
+    {
+      int free_ximg = !ximg;
+#ifdef HAVE_NTGUI
+      HGDIOBJ prev;
+#endif /* HAVE_NTGUI */
+
+      if (free_ximg)
+	{
+#ifndef HAVE_NTGUI
+	  ximg = XGetImage (FRAME_X_DISPLAY (f), img->pixmap,
+			    0, 0, img->width, img->height, ~0, ZPixmap);
+#else
+	  HDC frame_dc = get_frame_dc (f);
+	  ximg = CreateCompatibleDC (frame_dc);
+	  release_frame_dc (f, frame_dc);
+	  prev = SelectObject (ximg, img->pixmap);
+#endif /* !HAVE_NTGUI */
+	}
+
+      img->background = four_corners_best (ximg, img->width, img->height);
+
+      if (free_ximg)
+	Destroy_Image (ximg, prev);
+      
+      img->background_valid = 1;
+    }
+
+  return img->background;
+}
+
+/* Return the `background_transparent' field of IMG.  If IMG doesn't
+   have one yet, it is guessed heuristically.  If non-zero, MASK is an
+   existing XImage object to use for the heuristic.  */
+
+int
+image_background_transparent (img, f, mask)
+     struct image *img;
+     struct frame *f;
+     XImagePtr_or_DC mask;
+{
+  if (! img->background_transparent_valid)
+    /* IMG doesn't have a background yet, try to guess a reasonable value.  */
+    {
+      if (img->mask)
+	{
+	  int free_mask = !mask;
+#ifdef HAVE_NTGUI
+	  HGDIOBJ prev;
+#endif /* HAVE_NTGUI */
+
+	  if (free_mask)
+	    {
+#ifndef HAVE_NTGUI
+	      mask = XGetImage (FRAME_X_DISPLAY (f), img->mask,
+				0, 0, img->width, img->height, ~0, ZPixmap);
+#else
+	      HDC frame_dc = get_frame_dc (f);
+	      mask = CreateCompatibleDC (frame_dc);
+	      release_frame_dc (f, frame_dc);
+	      prev = SelectObject (mask, img->mask);
+#endif /* HAVE_NTGUI */
+	    }
+
+	  img->background_transparent
+	    = (four_corners_best (mask, img->width, img->height) == PIX_MASK_RETAIN (f));
+
+	  if (free_mask)
+	    Destroy_Image (mask, prev);
+	}
+      else
+	img->background_transparent = 0;
+
+      img->background_transparent_valid = 1;
+    }
+
+  return img->background_transparent;
+}
+
+
+/***********************************************************************
+		  Helper functions for X image types
+ ***********************************************************************/
+
+static void x_clear_image_1 P_ ((struct frame *, struct image *, int,
+				 int, int));
+static void x_clear_image P_ ((struct frame *f, struct image *img));
+static unsigned long x_alloc_image_color P_ ((struct frame *f,
+					      struct image *img,
+					      Lisp_Object color_name,
+					      unsigned long dflt));
+
+
+/* Clear X resources of image IMG on frame F.  PIXMAP_P non-zero means
+   free the pixmap if any.  MASK_P non-zero means clear the mask
+   pixmap if any.  COLORS_P non-zero means free colors allocated for
+   the image, if any.  */
+
+static void
+x_clear_image_1 (f, img, pixmap_p, mask_p, colors_p)
+     struct frame *f;
+     struct image *img;
+     int pixmap_p, mask_p, colors_p;
+{
+  if (pixmap_p && img->pixmap)
+    {
+      Free_Pixmap (FRAME_X_DISPLAY (f), img->pixmap);
+      img->pixmap = NO_PIXMAP;
+      img->background_valid = 0;
+    }
+
+  if (mask_p && img->mask)
+    {
+      Free_Pixmap (FRAME_X_DISPLAY (f), img->mask);
+      img->mask = NO_PIXMAP;
+      img->background_transparent_valid = 0;
+    }
+
+  if (colors_p && img->ncolors)
+    {
+      /* MAC_TODO: color table support.  */
+      /* W32_TODO: color table support.  */
+#ifdef HAVE_X_WINDOWS
+      x_free_colors (f, img->colors, img->ncolors);
+#endif /* HAVE_X_WINDOWS */
+      xfree (img->colors);
+      img->colors = NULL;
+      img->ncolors = 0;
+    }
+}
+
+/* Free X resources of image IMG which is used on frame F.  */
+
+static void
+x_clear_image (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  BLOCK_INPUT;
+  x_clear_image_1 (f, img, 1, 1, 1);
+  UNBLOCK_INPUT;
+}
+
+
+/* Allocate color COLOR_NAME for image IMG on frame F.  If color
+   cannot be allocated, use DFLT.  Add a newly allocated color to
+   IMG->colors, so that it can be freed again.  Value is the pixel
+   color.  */
+
+static unsigned long
+x_alloc_image_color (f, img, color_name, dflt)
+     struct frame *f;
+     struct image *img;
+     Lisp_Object color_name;
+     unsigned long dflt;
+{
+  XColor color;
+  unsigned long result;
+
+  xassert (STRINGP (color_name));
+
+  if (x_defined_color (f, SDATA (color_name), &color, 1))
+    {
+      /* This isn't called frequently so we get away with simply
+	 reallocating the color vector to the needed size, here.  */
+      ++img->ncolors;
+      img->colors =
+	(unsigned long *) xrealloc (img->colors,
+				    img->ncolors * sizeof *img->colors);
+      img->colors[img->ncolors - 1] = color.pixel;
+      result = color.pixel;
+    }
+  else
+    result = dflt;
+
+  return result;
+}
+
+
+
+/***********************************************************************
+			     Image Cache
+ ***********************************************************************/
+
+static void cache_image P_ ((struct frame *f, struct image *img));
+static void postprocess_image P_ ((struct frame *, struct image *));
+
+/* Return a new, initialized image cache that is allocated from the
+   heap.  Call free_image_cache to free an image cache.  */
+
+struct image_cache *
+make_image_cache ()
+{
+  struct image_cache *c = (struct image_cache *) xmalloc (sizeof *c);
+  int size;
+
+  bzero (c, sizeof *c);
+  c->size = 50;
+  c->images = (struct image **) xmalloc (c->size * sizeof *c->images);
+  size = IMAGE_CACHE_BUCKETS_SIZE * sizeof *c->buckets;
+  c->buckets = (struct image **) xmalloc (size);
+  bzero (c->buckets, size);
+  return c;
+}
+
+
+/* Free image cache of frame F.  Be aware that X frames share images
+   caches.  */
+
+void
+free_image_cache (f)
+     struct frame *f;
+{
+  struct image_cache *c = FRAME_X_IMAGE_CACHE (f);
+  if (c)
+    {
+      int i;
+
+      /* Cache should not be referenced by any frame when freed.  */
+      xassert (c->refcount == 0);
+
+      for (i = 0; i < c->used; ++i)
+	free_image (f, c->images[i]);
+      xfree (c->images);
+      xfree (c->buckets);
+      xfree (c);
+      FRAME_X_IMAGE_CACHE (f) = NULL;
+    }
+}
+
+
+/* Clear image cache of frame F.  FORCE_P non-zero means free all
+   images.  FORCE_P zero means clear only images that haven't been
+   displayed for some time.  Should be called from time to time to
+   reduce the number of loaded images.  If image-eviction-seconds is
+   non-nil, this frees images in the cache which weren't displayed for
+   at least that many seconds.  */
+
+void
+clear_image_cache (f, force_p)
+     struct frame *f;
+     int force_p;
+{
+  struct image_cache *c = FRAME_X_IMAGE_CACHE (f);
+
+  if (c && INTEGERP (Vimage_cache_eviction_delay))
+    {
+      EMACS_TIME t;
+      unsigned long old;
+      int i, nfreed;
+
+      EMACS_GET_TIME (t);
+      old = EMACS_SECS (t) - XFASTINT (Vimage_cache_eviction_delay);
+
+      /* Block input so that we won't be interrupted by a SIGIO
+	 while being in an inconsistent state.  */
+      BLOCK_INPUT;
+
+      for (i = nfreed = 0; i < c->used; ++i)
+	{
+	  struct image *img = c->images[i];
+	  if (img != NULL
+	      && (force_p || img->timestamp < old))
+	    {
+	      free_image (f, img);
+	      ++nfreed;
+	    }
+	}
+
+      /* We may be clearing the image cache because, for example,
+	 Emacs was iconified for a longer period of time.  In that
+	 case, current matrices may still contain references to
+	 images freed above.  So, clear these matrices.  */
+      if (nfreed)
+	{
+	  Lisp_Object tail, frame;
+
+	  FOR_EACH_FRAME (tail, frame)
+	    {
+	      struct frame *f = XFRAME (frame);
+	      if (FRAME_WINDOW_P (f)
+		  && FRAME_X_IMAGE_CACHE (f) == c)
+		clear_current_matrices (f);
+	    }
+
+	  ++windows_or_buffers_changed;
+	}
+
+      UNBLOCK_INPUT;
+    }
+}
+
+
+DEFUN ("clear-image-cache", Fclear_image_cache, Sclear_image_cache,
+       0, 1, 0,
+       doc: /* Clear the image cache of FRAME.
+FRAME nil or omitted means use the selected frame.
+FRAME t means clear the image caches of all frames.  */)
+     (frame)
+     Lisp_Object frame;
+{
+  if (EQ (frame, Qt))
+    {
+      Lisp_Object tail;
+
+      FOR_EACH_FRAME (tail, frame)
+	if (FRAME_WINDOW_P (XFRAME (frame)))
+	  clear_image_cache (XFRAME (frame), 1);
+    }
+  else
+    clear_image_cache (check_x_frame (frame), 1);
+
+  return Qnil;
+}
+
+
+/* Compute masks and transform image IMG on frame F, as specified
+   by the image's specification,  */
+
+static void
+postprocess_image (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  /* Manipulation of the image's mask.  */
+  if (img->pixmap)
+    {
+      Lisp_Object conversion, spec;
+      Lisp_Object mask;
+
+      spec = img->spec;
+
+      /* `:heuristic-mask t'
+	 `:mask heuristic'
+	 means build a mask heuristically.
+	 `:heuristic-mask (R G B)'
+	 `:mask (heuristic (R G B))'
+	 means build a mask from color (R G B) in the
+	 image.
+	 `:mask nil'
+	 means remove a mask, if any.  */
+
+      mask = image_spec_value (spec, QCheuristic_mask, NULL);
+      if (!NILP (mask))
+	x_build_heuristic_mask (f, img, mask);
+      else
+	{
+	  int found_p;
+
+	  mask = image_spec_value (spec, QCmask, &found_p);
+
+	  if (EQ (mask, Qheuristic))
+	    x_build_heuristic_mask (f, img, Qt);
+	  else if (CONSP (mask)
+		   && EQ (XCAR (mask), Qheuristic))
+	    {
+	      if (CONSP (XCDR (mask)))
+		x_build_heuristic_mask (f, img, XCAR (XCDR (mask)));
+	      else
+		x_build_heuristic_mask (f, img, XCDR (mask));
+	    }
+	  else if (NILP (mask) && found_p && img->mask)
+	    {
+	      Free_Pixmap (FRAME_X_DISPLAY (f), img->mask);
+	      img->mask = NO_PIXMAP;
+	    }
+	}
+
+
+      /* Should we apply an image transformation algorithm?  */
+      conversion = image_spec_value (spec, QCconversion, NULL);
+      if (EQ (conversion, Qdisabled))
+	x_disable_image (f, img);
+      else if (EQ (conversion, Qlaplace))
+	x_laplace (f, img);
+      else if (EQ (conversion, Qemboss))
+	x_emboss (f, img);
+      else if (CONSP (conversion)
+	       && EQ (XCAR (conversion), Qedge_detection))
+	{
+	  Lisp_Object tem;
+	  tem = XCDR (conversion);
+	  if (CONSP (tem))
+	    x_edge_detection (f, img,
+			      Fplist_get (tem, QCmatrix),
+			      Fplist_get (tem, QCcolor_adjustment));
+	}
+    }
+}
+
+
+/* Return the id of image with Lisp specification SPEC on frame F.
+   SPEC must be a valid Lisp image specification (see valid_image_p).  */
+
+int
+lookup_image (f, spec)
+     struct frame *f;
+     Lisp_Object spec;
+{
+  struct image_cache *c = FRAME_X_IMAGE_CACHE (f);
+  struct image *img;
+  int i;
+  unsigned hash;
+  struct gcpro gcpro1;
+  EMACS_TIME now;
+
+  /* F must be a window-system frame, and SPEC must be a valid image
+     specification.  */
+  xassert (FRAME_WINDOW_P (f));
+  xassert (valid_image_p (spec));
+
+  GCPRO1 (spec);
+
+  /* Look up SPEC in the hash table of the image cache.  */
+  hash = sxhash (spec, 0);
+  i = hash % IMAGE_CACHE_BUCKETS_SIZE;
+
+  for (img = c->buckets[i]; img; img = img->next)
+    if (img->hash == hash && !NILP (Fequal (img->spec, spec)))
+      break;
+
+  /* If not found, create a new image and cache it.  */
+  if (img == NULL)
+    {
+      extern Lisp_Object Qpostscript;
+
+      BLOCK_INPUT;
+      img = make_image (spec, hash);
+      cache_image (f, img);
+      img->load_failed_p = img->type->load (f, img) == 0;
+
+      /* If we can't load the image, and we don't have a width and
+	 height, use some arbitrary width and height so that we can
+	 draw a rectangle for it.  */
+      if (img->load_failed_p)
+	{
+	  Lisp_Object value;
+
+	  value = image_spec_value (spec, QCwidth, NULL);
+	  img->width = (INTEGERP (value)
+			? XFASTINT (value) : DEFAULT_IMAGE_WIDTH);
+	  value = image_spec_value (spec, QCheight, NULL);
+	  img->height = (INTEGERP (value)
+			 ? XFASTINT (value) : DEFAULT_IMAGE_HEIGHT);
+	}
+      else
+	{
+	  /* Handle image type independent image attributes
+	     `:ascent ASCENT', `:margin MARGIN', `:relief RELIEF',
+	     `:background COLOR'.  */
+	  Lisp_Object ascent, margin, relief, bg;
+
+	  ascent = image_spec_value (spec, QCascent, NULL);
+	  if (INTEGERP (ascent))
+	    img->ascent = XFASTINT (ascent);
+	  else if (EQ (ascent, Qcenter))
+	    img->ascent = CENTERED_IMAGE_ASCENT;
+
+	  margin = image_spec_value (spec, QCmargin, NULL);
+	  if (INTEGERP (margin) && XINT (margin) >= 0)
+	    img->vmargin = img->hmargin = XFASTINT (margin);
+	  else if (CONSP (margin) && INTEGERP (XCAR (margin))
+		   && INTEGERP (XCDR (margin)))
+	    {
+	      if (XINT (XCAR (margin)) > 0)
+		img->hmargin = XFASTINT (XCAR (margin));
+	      if (XINT (XCDR (margin)) > 0)
+		img->vmargin = XFASTINT (XCDR (margin));
+	    }
+
+	  relief = image_spec_value (spec, QCrelief, NULL);
+	  if (INTEGERP (relief))
+	    {
+	      img->relief = XINT (relief);
+	      img->hmargin += abs (img->relief);
+	      img->vmargin += abs (img->relief);
+	    }
+
+	  if (! img->background_valid)
+	    {
+	      bg = image_spec_value (img->spec, QCbackground, NULL);
+	      if (!NILP (bg))
+		{
+		  img->background
+		    = x_alloc_image_color (f, img, bg,
+					   FRAME_BACKGROUND_PIXEL (f));
+		  img->background_valid = 1;
+		}
+	    }
+
+	  /* Do image transformations and compute masks, unless we
+	     don't have the image yet.  */
+	  if (!EQ (*img->type->type, Qpostscript))
+	    postprocess_image (f, img);
+	}
+
+      UNBLOCK_INPUT;
+    }
+
+  /* We're using IMG, so set its timestamp to `now'.  */
+  EMACS_GET_TIME (now);
+  img->timestamp = EMACS_SECS (now);
+
+  UNGCPRO;
+
+  /* Value is the image id.  */
+  return img->id;
+}
+
+
+/* Cache image IMG in the image cache of frame F.  */
+
+static void
+cache_image (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  struct image_cache *c = FRAME_X_IMAGE_CACHE (f);
+  int i;
+
+  /* Find a free slot in c->images.  */
+  for (i = 0; i < c->used; ++i)
+    if (c->images[i] == NULL)
+      break;
+
+  /* If no free slot found, maybe enlarge c->images.  */
+  if (i == c->used && c->used == c->size)
+    {
+      c->size *= 2;
+      c->images = (struct image **) xrealloc (c->images,
+					      c->size * sizeof *c->images);
+    }
+
+  /* Add IMG to c->images, and assign IMG an id.  */
+  c->images[i] = img;
+  img->id = i;
+  if (i == c->used)
+    ++c->used;
+
+  /* Add IMG to the cache's hash table.  */
+  i = img->hash % IMAGE_CACHE_BUCKETS_SIZE;
+  img->next = c->buckets[i];
+  if (img->next)
+    img->next->prev = img;
+  img->prev = NULL;
+  c->buckets[i] = img;
+}
+
+
+/* Call FN on every image in the image cache of frame F.  Used to mark
+   Lisp Objects in the image cache.  */
+
+void
+forall_images_in_image_cache (f, fn)
+     struct frame *f;
+     void (*fn) P_ ((struct image *img));
+{
+  if (FRAME_LIVE_P (f) && FRAME_WINDOW_P (f))
+    {
+      struct image_cache *c = FRAME_X_IMAGE_CACHE (f);
+      if (c)
+	{
+	  int i;
+	  for (i = 0; i < c->used; ++i)
+	    if (c->images[i])
+	      fn (c->images[i]);
+	}
+    }
+}
+
+
+
+/***********************************************************************
+			  X / MAC / W32 support code
+ ***********************************************************************/
+
+#ifdef HAVE_NTGUI
+
+/* Macro for defining functions that will be loaded from image DLLs.  */
+#define DEF_IMGLIB_FN(func) FARPROC fn_##func
+
+/* Macro for loading those image functions from the library.  */
+#define LOAD_IMGLIB_FN(lib,func) {					\
+    fn_##func = (void *) GetProcAddress (lib, #func);			\
+    if (!fn_##func) return 0;						\
+  }
+
+#endif /* HAVE_NTGUI */
+
+static int x_create_x_image_and_pixmap P_ ((struct frame *, int, int, int,
+					    XImagePtr *, Pixmap *));
+static void x_destroy_x_image P_ ((XImagePtr));
+static void x_put_x_image P_ ((struct frame *, XImagePtr, Pixmap, int, int));
+
+
+/* Create an XImage and a pixmap of size WIDTH x HEIGHT for use on
+   frame F.  Set *XIMG and *PIXMAP to the XImage and Pixmap created.
+   Set (*XIMG)->data to a raster of WIDTH x HEIGHT pixels allocated
+   via xmalloc.  Print error messages via image_error if an error
+   occurs.  Value is non-zero if successful.
+
+   On W32, a DEPTH of zero signifies a 24 bit image, otherwise DEPTH
+   should indicate the bit depth of the image.  */
+
+static int
+x_create_x_image_and_pixmap (f, width, height, depth, ximg, pixmap)
+     struct frame *f;
+     int width, height, depth;
+     XImagePtr *ximg;
+     Pixmap *pixmap;
+{
+#ifdef HAVE_X_WINDOWS
+  Display *display = FRAME_X_DISPLAY (f);
+  Window window = FRAME_X_WINDOW (f);
+  Screen *screen = FRAME_X_SCREEN (f);
+
+  xassert (interrupt_input_blocked);
+
+  if (depth <= 0)
+    depth = DefaultDepthOfScreen (screen);
+  *ximg = XCreateImage (display, DefaultVisualOfScreen (screen),
+			depth, ZPixmap, 0, NULL, width, height,
+			depth > 16 ? 32 : depth > 8 ? 16 : 8, 0);
+  if (*ximg == NULL)
+    {
+      image_error ("Unable to allocate X image", Qnil, Qnil);
+      return 0;
+    }
+
+  /* Allocate image raster.  */
+  (*ximg)->data = (char *) xmalloc ((*ximg)->bytes_per_line * height);
+
+  /* Allocate a pixmap of the same size.  */
+  *pixmap = XCreatePixmap (display, window, width, height, depth);
+  if (*pixmap == NO_PIXMAP)
+    {
+      x_destroy_x_image (*ximg);
+      *ximg = NULL;
+      image_error ("Unable to create X pixmap", Qnil, Qnil);
+      return 0;
+    }
+
+  return 1;
+#endif /* HAVE_X_WINDOWS */
+
+#ifdef HAVE_NTGUI
+
+  BITMAPINFOHEADER *header;
+  HDC hdc;
+  int scanline_width_bits;
+  int remainder;
+  int palette_colors = 0;
+
+  if (depth == 0)
+    depth = 24;
+
+  if (depth != 1 && depth != 4 && depth != 8
+      && depth != 16 && depth != 24 && depth != 32)
+    {
+      image_error ("Invalid image bit depth specified", Qnil, Qnil);
+      return 0;
+    }
+
+  scanline_width_bits = width * depth;
+  remainder = scanline_width_bits % 32;
+
+  if (remainder)
+    scanline_width_bits += 32 - remainder;
+
+  /* Bitmaps with a depth less than 16 need a palette.  */
+  /* BITMAPINFO structure already contains the first RGBQUAD.  */
+  if (depth < 16)
+    palette_colors = 1 << depth - 1;
+
+  *ximg = xmalloc (sizeof (XImage) + palette_colors * sizeof (RGBQUAD));
+  if (*ximg == NULL)
+    {
+      image_error ("Unable to allocate memory for XImage", Qnil, Qnil);
+      return 0;
+    }
+
+  header = &((*ximg)->info.bmiHeader);
+  bzero (&((*ximg)->info), sizeof (BITMAPINFO));
+  header->biSize = sizeof (*header);
+  header->biWidth = width;
+  header->biHeight = -height;  /* negative indicates a top-down bitmap.  */
+  header->biPlanes = 1;
+  header->biBitCount = depth;
+  header->biCompression = BI_RGB;
+  header->biClrUsed = palette_colors;
+
+  /* TODO: fill in palette.  */
+  if (depth == 1)
+    {
+      (*ximg)->info.bmiColors[0].rgbBlue = 0;
+      (*ximg)->info.bmiColors[0].rgbGreen = 0;
+      (*ximg)->info.bmiColors[0].rgbRed = 0;
+      (*ximg)->info.bmiColors[0].rgbReserved = 0;
+      (*ximg)->info.bmiColors[1].rgbBlue = 255;
+      (*ximg)->info.bmiColors[1].rgbGreen = 255;
+      (*ximg)->info.bmiColors[1].rgbRed = 255;
+      (*ximg)->info.bmiColors[1].rgbReserved = 0;
+    }
+
+  hdc = get_frame_dc (f);
+
+  /* Create a DIBSection and raster array for the bitmap,
+     and store its handle in *pixmap.  */
+  *pixmap = CreateDIBSection (hdc, &((*ximg)->info),
+			      (depth < 16) ? DIB_PAL_COLORS : DIB_RGB_COLORS,
+			      &((*ximg)->data), NULL, 0);
+
+  /* Realize display palette and garbage all frames. */
+  release_frame_dc (f, hdc);
+
+  if (*pixmap == NULL)
+    {
+      DWORD err = GetLastError();
+      Lisp_Object errcode;
+      /* All system errors are < 10000, so the following is safe.  */
+      XSETINT (errcode, (int) err);
+      image_error ("Unable to create bitmap, error code %d", errcode, Qnil);
+      x_destroy_x_image (*ximg);
+      return 0;
+    }
+
+  return 1;
+
+#endif /* HAVE_NTGUI */
+
+#ifdef MAC_OS
+  Display *display = FRAME_X_DISPLAY (f);
+  Window window = FRAME_X_WINDOW (f);
+
+  xassert (interrupt_input_blocked);
+
+  /* Allocate a pixmap of the same size.  */
+  *pixmap = XCreatePixmap (display, window, width, height, depth);
+  if (*pixmap == NO_PIXMAP)
+    {
+      x_destroy_x_image (*ximg);
+      *ximg = NULL;
+      image_error ("Unable to create X pixmap", Qnil, Qnil);
+      return 0;
+    }
+
+  LockPixels (GetGWorldPixMap (*pixmap));
+  *ximg = *pixmap;
+  return 1;
+
+#endif  /* MAC_OS */
+}
+
+
+/* Destroy XImage XIMG.  Free XIMG->data.  */
+
+static void
+x_destroy_x_image (ximg)
+     XImagePtr ximg;
+{
+  xassert (interrupt_input_blocked);
+  if (ximg)
+    {
+#ifdef HAVE_X_WINDOWS
+      xfree (ximg->data);
+      ximg->data = NULL;
+      XDestroyImage (ximg);
+#endif /* HAVE_X_WINDOWS */
+#ifdef HAVE_NTGUI
+      /* Data will be freed by DestroyObject.  */
+      ximg->data = NULL;
+      xfree (ximg);
+#endif /* HAVE_NTGUI */
+#ifdef MAC_OS
+      XDestroyImage (ximg);
+#endif /* MAC_OS */
+    }
+}
+
+
+/* Put XImage XIMG into pixmap PIXMAP on frame F.  WIDTH and HEIGHT
+   are width and height of both the image and pixmap.  */
+
+static void
+x_put_x_image (f, ximg, pixmap, width, height)
+     struct frame *f;
+     XImagePtr ximg;
+     Pixmap pixmap;
+     int width, height;
+{
+#ifdef HAVE_X_WINDOWS
+  GC gc;
+
+  xassert (interrupt_input_blocked);
+  gc = XCreateGC (FRAME_X_DISPLAY (f), pixmap, 0, NULL);
+  XPutImage (FRAME_X_DISPLAY (f), pixmap, gc, ximg, 0, 0, 0, 0, width, height);
+  XFreeGC (FRAME_X_DISPLAY (f), gc);
+#endif /* HAVE_X_WINDOWS */
+
+#ifdef HAVE_NTGUI
+#if 0  /* I don't think this is necessary looking at where it is used.  */
+  HDC hdc = get_frame_dc (f);
+  SetDIBits (hdc, pixmap, 0, height, ximg->data, &(ximg->info), DIB_RGB_COLORS);
+  release_frame_dc (f, hdc);
+#endif
+#endif /* HAVE_NTGUI */
+
+#ifdef MAC_OS
+  xassert (ximg == pixmap);
+#endif  /* MAC_OS */
+}
+
+
+/***********************************************************************
+			      File Handling
+ ***********************************************************************/
+
+static Lisp_Object x_find_image_file P_ ((Lisp_Object));
+static unsigned char *slurp_file P_ ((char *, int *));
+
+
+/* Find image file FILE.  Look in data-directory, then
+   x-bitmap-file-path.  Value is the full name of the file found, or
+   nil if not found.  */
+
+static Lisp_Object
+x_find_image_file (file)
+     Lisp_Object file;
+{
+  Lisp_Object file_found, search_path;
+  struct gcpro gcpro1, gcpro2;
+  int fd;
+
+  file_found = Qnil;
+  search_path = Fcons (Vdata_directory, Vx_bitmap_file_path);
+  GCPRO2 (file_found, search_path);
+
+  /* Try to find FILE in data-directory, then x-bitmap-file-path.  */
+  fd = openp (search_path, file, Qnil, &file_found, Qnil);
+
+  if (fd == -1)
+    file_found = Qnil;
+  else
+    close (fd);
+
+  UNGCPRO;
+  return file_found;
+}
+
+
+/* Read FILE into memory.  Value is a pointer to a buffer allocated
+   with xmalloc holding FILE's contents.  Value is null if an error
+   occurred.  *SIZE is set to the size of the file.  */
+
+static unsigned char *
+slurp_file (file, size)
+     char *file;
+     int *size;
+{
+  FILE *fp = NULL;
+  unsigned char *buf = NULL;
+  struct stat st;
+
+  if (stat (file, &st) == 0
+      && (fp = fopen (file, "rb")) != NULL
+      && (buf = (char *) xmalloc (st.st_size),
+	  fread (buf, 1, st.st_size, fp) == st.st_size))
+    {
+      *size = st.st_size;
+      fclose (fp);
+    }
+  else
+    {
+      if (fp)
+	fclose (fp);
+      if (buf)
+	{
+	  xfree (buf);
+	  buf = NULL;
+	}
+    }
+
+  return buf;
+}
+
+
+
+#ifdef MAC_OS
+
+/***********************************************************************
+			MAC Image Load Functions
+ ***********************************************************************/
+
+static int image_load_quicktime P_ ((struct frame *, struct image *img,
+				     OSType));
+#ifdef MAC_OSX
+static int image_load_quartz2d P_ ((struct frame *, struct image *img, int));
+#endif
+
+static OSErr
+find_image_fsspec (specified_file, file, fss)
+     Lisp_Object specified_file, *file;
+     FSSpec *fss;
+{
+#if TARGET_API_MAC_CARBON
+  FSRef fsr;
+#else
+  Str255 mac_pathname;
+#endif
+  OSErr err;
+
+  *file = x_find_image_file (specified_file);
+  if (!STRINGP (*file))
+    return fnfErr;		/* file or directory not found;
+				   incomplete pathname */
+  /* Try to open the image file.  */
+#if TARGET_API_MAC_CARBON
+  err = FSPathMakeRef (SDATA (*file), &fsr, NULL);
+  if (err == noErr)
+    err = FSGetCatalogInfo (&fsr, kFSCatInfoNone, NULL, NULL, fss, NULL);
+#else
+  if (posix_to_mac_pathname (SDATA (*file), mac_pathname, MAXPATHLEN+1) == 0)
+    return fnfErr;
+  c2pstr (mac_pathname);
+  err = FSMakeFSSpec (0, 0, mac_pathname, fss);
+#endif
+  return err;
+}
+
+static int
+image_load_qt_1 (f, img, type, fss, dh)
+     struct frame *f;
+     struct image *img;
+     OSType type;
+     FSSpec *fss;
+     Handle dh;
+{
+  OSErr err;
+  GraphicsImportComponent gi;
+  Rect rect;
+  int width, height;
+  short draw_all_pixels;
+  Lisp_Object specified_bg;
+  XColor color;
+  XImagePtr ximg;
+  RGBColor bg_color;
+
+  err = OpenADefaultComponent (GraphicsImporterComponentType,
+			       type, &gi);
+  if (err != noErr)
+    {
+      image_error ("Cannot get importer component for `%s'", img->spec, Qnil);
+      return 0;
+    }
+  if (dh == NULL)
+    {
+      /* read from file system spec */
+      err = GraphicsImportSetDataFile (gi, fss);
+      if (err != noErr)
+	{
+	  image_error ("Cannot set fsspec to graphics importer for '%s'",
+		       img->spec, Qnil);
+	  goto error;
+	}
+    }
+  else
+    {
+      /* read from data handle */
+      err = GraphicsImportSetDataHandle (gi, dh);
+      if (err != noErr)
+	{
+	  image_error ("Cannot set data handle to graphics importer for `%s'",
+		       img->spec, Qnil);
+	  goto error;
+	}
+    }
+  err = GraphicsImportGetNaturalBounds (gi, &rect);
+  if (err != noErr)
+    {
+      image_error ("Error reading `%s'", img->spec, Qnil);
+      goto error;
+    }
+  width = img->width = rect.right - rect.left;
+  height = img->height = rect.bottom - rect.top;
+  err = GraphicsImportDoesDrawAllPixels (gi, &draw_all_pixels);
+#if 0
+  /* Don't check the error code here.  It may have an undocumented
+     value -32766. */
+  if (err != noErr)
+    {
+      image_error ("Error reading `%s'", img->spec, Qnil);
+      goto error;
+    }
+#endif
+  if (draw_all_pixels != graphicsImporterDrawsAllPixels)
+    {
+      specified_bg = image_spec_value (img->spec, QCbackground, NULL);
+      if (!STRINGP (specified_bg) ||
+	  !mac_defined_color (f, SDATA (specified_bg), &color, 0))
+	{
+	  color.pixel = FRAME_BACKGROUND_PIXEL (f);
+	  color.red = RED16_FROM_ULONG (color.pixel);
+	  color.green = GREEN16_FROM_ULONG (color.pixel);
+	  color.blue = BLUE16_FROM_ULONG (color.pixel);
+	}
+    }
+
+  if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap))
+    goto error;
+  if (draw_all_pixels != graphicsImporterDrawsAllPixels)
+    {
+      SetGWorld (ximg, NULL);
+      bg_color.red = color.red;
+      bg_color.green = color.green;
+      bg_color.blue = color.blue;
+      RGBBackColor (&bg_color);
+#if TARGET_API_MAC_CARBON
+      GetPortBounds (ximg, &rect);
+      EraseRect (&rect);
+#else
+      EraseRect (&(ximg->portRect));
+#endif
+    }
+  GraphicsImportSetGWorld (gi, ximg, NULL);
+  GraphicsImportDraw (gi);
+  CloseComponent (gi);
+
+  /* Maybe fill in the background field while we have ximg handy. */
+  if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
+    IMAGE_BACKGROUND (img, f, ximg);
+
+  /* Put the image into the pixmap.  */
+  x_put_x_image (f, ximg, img->pixmap, width, height);
+  x_destroy_x_image (ximg);
+  return 1;
+
+ error:
+  CloseComponent (gi);
+  return 0;
+}
+
+
+/* Load an image using the QuickTime Graphics Importer.
+   Note: The alpha channel does not work for PNG images. */
+static int
+image_load_quicktime (f, img, type)
+     struct frame *f;
+     struct image *img;
+     OSType type;
+{
+  Lisp_Object specified_file;
+  Lisp_Object specified_data;
+  OSErr err;
+
+  specified_file = image_spec_value (img->spec, QCfile, NULL);
+  specified_data = image_spec_value (img->spec, QCdata, NULL);
+
+  if (NILP (specified_data))
+    {
+      /* Read from a file */
+      Lisp_Object file;
+      FSSpec fss;
+
+      err = find_image_fsspec (specified_file, &file, &fss);
+      if (err != noErr)
+	{
+	  if (err == fnfErr)
+	    image_error ("Cannot find image file `%s'", specified_file, Qnil);
+	  else
+	    image_error ("Cannot open `%s'", file, Qnil);
+	  return 0;
+	}
+      return image_load_qt_1 (f, img, type, &fss, NULL);
+    }
+  else
+    {
+      /* Memory source! */
+      int success_p;
+      Handle dh;
+
+      err = PtrToHand (SDATA (specified_data), &dh, SBYTES (specified_data));
+      if (err != noErr)
+	{
+	  image_error ("Cannot allocate data handle for `%s'",
+		       img->spec, Qnil);
+	  return 0;
+	}
+      success_p = image_load_qt_1 (f, img, type, NULL, dh);
+      DisposeHandle (dh);
+      return success_p;
+    }
+}
+
+
+#ifdef MAC_OSX
+/* Load a PNG/JPEG image using Quartz 2D decoding routines.
+   CGImageCreateWithPNGDataProvider is provided after Mac OS X 10.2.
+   So don't use this function directly but determine at runtime
+   whether it exists. */
+typedef CGImageRef (*CGImageCreateWithPNGDataProviderProcType)
+  (CGDataProviderRef, const float [], bool, CGColorRenderingIntent);
+static CGImageCreateWithPNGDataProviderProcType MyCGImageCreateWithPNGDataProvider;
+
+
+static void
+init_image_func_pointer ()
+{
+  if (NSIsSymbolNameDefined ("_CGImageCreateWithPNGDataProvider"))
+    {
+      MyCGImageCreateWithPNGDataProvider
+	= (CGImageCreateWithPNGDataProviderProcType)
+	NSAddressOfSymbol (NSLookupAndBindSymbol
+			   ("_CGImageCreateWithPNGDataProvider"));
+    }
+  else
+    MyCGImageCreateWithPNGDataProvider = NULL;
+}
+
+
+static int
+image_load_quartz2d (f, img, png_p)
+     struct frame *f;
+     struct image *img;
+     int png_p;
+{
+  Lisp_Object file, specified_file;
+  Lisp_Object specified_data, specified_bg;
+  struct gcpro gcpro1;
+  CGDataProviderRef source;
+  CGImageRef image;
+  int width, height;
+  XColor color;
+  XImagePtr ximg = NULL;
+  CGContextRef context;
+  CGRect rectangle;
+
+  /* Open the file.  */
+  specified_file = image_spec_value (img->spec, QCfile, NULL);
+  specified_data = image_spec_value (img->spec, QCdata, NULL);
+
+  file = Qnil;
+  GCPRO1 (file);
+
+  if (NILP (specified_data))
+    {
+      CFStringRef path;
+      CFURLRef url;
+
+      file = x_find_image_file (specified_file);
+      if (!STRINGP (file))
+	{
+	  image_error ("Cannot find image file `%s'", specified_file, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+      path = CFStringCreateWithCString (NULL, SDATA (file),
+					kCFStringEncodingUTF8);
+      url = CFURLCreateWithFileSystemPath (NULL, path,
+					   kCFURLPOSIXPathStyle, 0);
+      CFRelease (path);
+      source = CGDataProviderCreateWithURL (url);
+      CFRelease (url);
+    }
+  else
+    source = CGDataProviderCreateWithData (NULL, SDATA (specified_data),
+					   SBYTES (specified_data), NULL);
+
+  if (png_p)
+    image = (*MyCGImageCreateWithPNGDataProvider) (source, NULL, FALSE,
+						   kCGRenderingIntentDefault);
+  else
+    image = CGImageCreateWithJPEGDataProvider (source, NULL, FALSE,
+					       kCGRenderingIntentDefault);
+
+  CGDataProviderRelease (source);
+  if (image == NULL)
+    {
+      UNGCPRO;
+      image_error ("Error reading image `%s'", img->spec, Qnil);
+      return 0;
+    }
+
+  if (png_p)
+    {
+      specified_bg = image_spec_value (img->spec, QCbackground, NULL);
+      if (!STRINGP (specified_bg) ||
+	  !mac_defined_color (f, SDATA (specified_bg), &color, 0))
+	{
+	  color.pixel = FRAME_BACKGROUND_PIXEL (f);
+	  color.red = RED16_FROM_ULONG (color.pixel);
+	  color.green = GREEN16_FROM_ULONG (color.pixel);
+	  color.blue = BLUE16_FROM_ULONG (color.pixel);
+	}
+    }
+  width = img->width = CGImageGetWidth (image);
+  height = img->height = CGImageGetHeight (image);
+  if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap))
+    {
+      CGImageRelease (image);
+      UNGCPRO;
+      return 0;
+    }
+  rectangle = CGRectMake (0, 0, width, height);
+  QDBeginCGContext (ximg, &context);
+  if (png_p)
+    {
+      CGContextSetRGBFillColor (context, color.red / 65535.0,
+				color.green / 65535.0,
+				color.blue / 65535.0, 1.0);
+      CGContextFillRect (context, rectangle);
+    }
+  CGContextDrawImage (context, rectangle, image);
+  QDEndCGContext (ximg, &context);
+  CGImageRelease (image);
+
+  /* Maybe fill in the background field while we have ximg handy. */
+  if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
+    IMAGE_BACKGROUND (img, f, ximg);
+
+  /* Put the image into the pixmap.  */
+  x_put_x_image (f, ximg, img->pixmap, width, height);
+  x_destroy_x_image (ximg);
+  UNGCPRO;
+  return 1;
+}
+#endif
+
+#endif  /* MAC_OS */
+
+
+/***********************************************************************
+			      XBM images
+ ***********************************************************************/
+
+static int xbm_scan P_ ((unsigned char **, unsigned char *, char *, int *));
+static int xbm_load P_ ((struct frame *f, struct image *img));
+static int xbm_load_image P_ ((struct frame *f, struct image *img,
+			       unsigned char *, unsigned char *));
+static int xbm_image_p P_ ((Lisp_Object object));
+static int xbm_read_bitmap_data P_ ((unsigned char *, unsigned char *,
+				     int *, int *, unsigned char **));
+static int xbm_file_p P_ ((Lisp_Object));
+
+
+/* Indices of image specification fields in xbm_format, below.  */
+
+enum xbm_keyword_index
+{
+  XBM_TYPE,
+  XBM_FILE,
+  XBM_WIDTH,
+  XBM_HEIGHT,
+  XBM_DATA,
+  XBM_FOREGROUND,
+  XBM_BACKGROUND,
+  XBM_ASCENT,
+  XBM_MARGIN,
+  XBM_RELIEF,
+  XBM_ALGORITHM,
+  XBM_HEURISTIC_MASK,
+  XBM_MASK,
+  XBM_LAST
+};
+
+/* Vector of image_keyword structures describing the format
+   of valid XBM image specifications.  */
+
+static struct image_keyword xbm_format[XBM_LAST] =
+{
+  {":type",		IMAGE_SYMBOL_VALUE,			1},
+  {":file",		IMAGE_STRING_VALUE,			0},
+  {":width",		IMAGE_POSITIVE_INTEGER_VALUE,		0},
+  {":height",		IMAGE_POSITIVE_INTEGER_VALUE,		0},
+  {":data",		IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":foreground",	IMAGE_STRING_OR_NIL_VALUE,		0},
+  {":background",	IMAGE_STRING_OR_NIL_VALUE,		0},
+  {":ascent",		IMAGE_ASCENT_VALUE,			0},
+  {":margin",		IMAGE_POSITIVE_INTEGER_VALUE_OR_PAIR,	0},
+  {":relief",		IMAGE_INTEGER_VALUE,			0},
+  {":conversion",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":heuristic-mask",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":mask",		IMAGE_DONT_CHECK_VALUE_TYPE,		0}
+};
+
+/* Structure describing the image type XBM.  */
+
+static struct image_type xbm_type =
+{
+  &Qxbm,
+  xbm_image_p,
+  xbm_load,
+  x_clear_image,
+  NULL
+};
+
+/* Tokens returned from xbm_scan.  */
+
+enum xbm_token
+{
+  XBM_TK_IDENT = 256,
+  XBM_TK_NUMBER
+};
+
+
+/* Return non-zero if OBJECT is a valid XBM-type image specification.
+   A valid specification is a list starting with the symbol `image'
+   The rest of the list is a property list which must contain an
+   entry `:type xbm..
+
+   If the specification specifies a file to load, it must contain
+   an entry `:file FILENAME' where FILENAME is a string.
+
+   If the specification is for a bitmap loaded from memory it must
+   contain `:width WIDTH', `:height HEIGHT', and `:data DATA', where
+   WIDTH and HEIGHT are integers > 0.  DATA may be:
+
+   1. a string large enough to hold the bitmap data, i.e. it must
+   have a size >= (WIDTH + 7) / 8 * HEIGHT
+
+   2. a bool-vector of size >= WIDTH * HEIGHT
+
+   3. a vector of strings or bool-vectors, one for each line of the
+   bitmap.
+
+   4. A string containing an in-memory XBM file.  WIDTH and HEIGHT
+   may not be specified in this case because they are defined in the
+   XBM file.
+
+   Both the file and data forms may contain the additional entries
+   `:background COLOR' and `:foreground COLOR'.  If not present,
+   foreground and background of the frame on which the image is
+   displayed is used.  */
+
+static int
+xbm_image_p (object)
+     Lisp_Object object;
+{
+  struct image_keyword kw[XBM_LAST];
+
+  bcopy (xbm_format, kw, sizeof kw);
+  if (!parse_image_spec (object, kw, XBM_LAST, Qxbm))
+    return 0;
+
+  xassert (EQ (kw[XBM_TYPE].value, Qxbm));
+
+  if (kw[XBM_FILE].count)
+    {
+      if (kw[XBM_WIDTH].count || kw[XBM_HEIGHT].count || kw[XBM_DATA].count)
+	return 0;
+    }
+  else if (kw[XBM_DATA].count && xbm_file_p (kw[XBM_DATA].value))
+    {
+      /* In-memory XBM file.  */
+      if (kw[XBM_WIDTH].count || kw[XBM_HEIGHT].count || kw[XBM_FILE].count)
+	return 0;
+    }
+  else
+    {
+      Lisp_Object data;
+      int width, height;
+
+      /* Entries for `:width', `:height' and `:data' must be present.  */
+      if (!kw[XBM_WIDTH].count
+	  || !kw[XBM_HEIGHT].count
+	  || !kw[XBM_DATA].count)
+	return 0;
+
+      data = kw[XBM_DATA].value;
+      width = XFASTINT (kw[XBM_WIDTH].value);
+      height = XFASTINT (kw[XBM_HEIGHT].value);
+
+      /* Check type of data, and width and height against contents of
+	 data.  */
+      if (VECTORP (data))
+	{
+	  int i;
+
+	  /* Number of elements of the vector must be >= height.  */
+	  if (XVECTOR (data)->size < height)
+	    return 0;
+
+	  /* Each string or bool-vector in data must be large enough
+	     for one line of the image.  */
+	  for (i = 0; i < height; ++i)
+	    {
+	      Lisp_Object elt = XVECTOR (data)->contents[i];
+
+	      if (STRINGP (elt))
+		{
+		  if (SCHARS (elt)
+		      < (width + BITS_PER_CHAR - 1) / BITS_PER_CHAR)
+		    return 0;
+		}
+	      else if (BOOL_VECTOR_P (elt))
+		{
+		  if (XBOOL_VECTOR (elt)->size < width)
+		    return 0;
+		}
+	      else
+		return 0;
+	    }
+	}
+      else if (STRINGP (data))
+	{
+	  if (SCHARS (data)
+	      < (width + BITS_PER_CHAR - 1) / BITS_PER_CHAR * height)
+	    return 0;
+	}
+      else if (BOOL_VECTOR_P (data))
+	{
+	  if (XBOOL_VECTOR (data)->size < width * height)
+	    return 0;
+	}
+      else
+	return 0;
+    }
+
+  return 1;
+}
+
+
+/* Scan a bitmap file.  FP is the stream to read from.  Value is
+   either an enumerator from enum xbm_token, or a character for a
+   single-character token, or 0 at end of file.  If scanning an
+   identifier, store the lexeme of the identifier in SVAL.  If
+   scanning a number, store its value in *IVAL.  */
+
+static int
+xbm_scan (s, end, sval, ival)
+     unsigned char **s, *end;
+     char *sval;
+     int *ival;
+{
+  unsigned int c;
+
+ loop:
+
+  /* Skip white space.  */
+  while (*s < end && (c = *(*s)++, isspace (c)))
+    ;
+
+  if (*s >= end)
+    c = 0;
+  else if (isdigit (c))
+    {
+      int value = 0, digit;
+
+      if (c == '0' && *s < end)
+	{
+	  c = *(*s)++;
+	  if (c == 'x' || c == 'X')
+	    {
+	      while (*s < end)
+		{
+		  c = *(*s)++;
+		  if (isdigit (c))
+		    digit = c - '0';
+		  else if (c >= 'a' && c <= 'f')
+		    digit = c - 'a' + 10;
+		  else if (c >= 'A' && c <= 'F')
+		    digit = c - 'A' + 10;
+		  else
+		    break;
+		  value = 16 * value + digit;
+		}
+	    }
+	  else if (isdigit (c))
+	    {
+	      value = c - '0';
+	      while (*s < end
+		     && (c = *(*s)++, isdigit (c)))
+		value = 8 * value + c - '0';
+	    }
+	}
+      else
+	{
+	  value = c - '0';
+	  while (*s < end
+		 && (c = *(*s)++, isdigit (c)))
+	    value = 10 * value + c - '0';
+	}
+
+      if (*s < end)
+	*s = *s - 1;
+      *ival = value;
+      c = XBM_TK_NUMBER;
+    }
+  else if (isalpha (c) || c == '_')
+    {
+      *sval++ = c;
+      while (*s < end
+	     && (c = *(*s)++, (isalnum (c) || c == '_')))
+	*sval++ = c;
+      *sval = 0;
+      if (*s < end)
+	*s = *s - 1;
+      c = XBM_TK_IDENT;
+    }
+  else if (c == '/' && **s == '*')
+    {
+      /* C-style comment.  */
+      ++*s;
+      while (**s && (**s != '*' || *(*s + 1) != '/'))
+	++*s;
+      if (**s)
+	{
+	  *s += 2;
+	  goto loop;
+	}
+    }
+
+  return c;
+}
+
+#ifdef HAVE_NTGUI
+
+/* Create a Windows bitmap from X bitmap data.  */
+static HBITMAP
+w32_create_pixmap_from_bitmap_data (int width, int height, char *data)
+{
+  static unsigned char swap_nibble[16]
+    = { 0x0, 0x8, 0x4, 0xc,    /* 0000 1000 0100 1100 */
+	0x2, 0xa, 0x6, 0xe,    /* 0010 1010 0110 1110 */
+	0x1, 0x9, 0x5, 0xd,    /* 0001 1001 0101 1101 */
+	0x3, 0xb, 0x7, 0xf };  /* 0011 1011 0111 1111 */
+  int i, j, w1, w2;
+  unsigned char *bits, *p;
+  HBITMAP bmp;
+
+  w1 = (width + 7) / 8;         /* nb of 8bits elt in X bitmap */
+  w2 = ((width + 15) / 16) * 2; /* nb of 16bits elt in W32 bitmap */
+  bits = (unsigned char *) alloca (height * w2);
+  bzero (bits, height * w2);
+  for (i = 0; i < height; i++)
+    {
+      p = bits + i*w2;
+      for (j = 0; j < w1; j++)
+	{
+	  /* Bitswap XBM bytes to match how Windows does things.  */
+	  unsigned char c = *data++;
+	  *p++ = (unsigned char)((swap_nibble[c & 0xf] << 4)
+				 | (swap_nibble[(c>>4) & 0xf]));
+	}
+    }
+  bmp = CreateBitmap (width, height, 1, 1, (char *) bits);
+
+  return bmp;
+}
+
+static void convert_mono_to_color_image (f, img, foreground, background)
+     struct frame *f;
+     struct image *img;
+     COLORREF foreground, background;
+{
+  HDC hdc, old_img_dc, new_img_dc;
+  HGDIOBJ old_prev, new_prev;
+  HBITMAP new_pixmap;
+
+  hdc = get_frame_dc (f);
+  old_img_dc = CreateCompatibleDC (hdc);
+  new_img_dc = CreateCompatibleDC (hdc);
+  new_pixmap = CreateCompatibleBitmap (hdc, img->width, img->height);
+  release_frame_dc (f, hdc);
+  old_prev = SelectObject (old_img_dc, img->pixmap);
+  new_prev = SelectObject (new_img_dc, new_pixmap);
+  SetTextColor (new_img_dc, foreground);
+  SetBkColor (new_img_dc, background);
+
+  BitBlt (new_img_dc, 0, 0, img->width, img->height, old_img_dc,
+	  0, 0, SRCCOPY);
+
+  SelectObject (old_img_dc, old_prev);
+  SelectObject (new_img_dc, new_prev);
+  DeleteDC (old_img_dc);
+  DeleteDC (new_img_dc);
+  DeleteObject (img->pixmap);
+  if (new_pixmap == 0)
+    fprintf (stderr, "Failed to convert image to color.\n");
+  else
+    img->pixmap = new_pixmap;
+}
+
+#define XBM_BIT_SHUFFLE(b) (~(b))
+
+#else
+
+#define XBM_BIT_SHUFFLE(b) (b)
+
+#endif /* HAVE_NTGUI */
+
+
+static void
+Create_Pixmap_From_Bitmap_Data(f, img, data, fg, bg, non_default_colors)
+     struct frame *f;
+     struct image *img;
+     char *data;
+     RGB_PIXEL_COLOR fg, bg;
+     int non_default_colors;
+{
+#ifdef HAVE_NTGUI
+  img->pixmap
+    = w32_create_pixmap_from_bitmap_data (img->width, img->height, data);
+
+  /* If colors were specified, transfer the bitmap to a color one.  */
+  if (non_default_colors)
+    convert_mono_to_color_image (f, img, fg, bg);
+#else
+  img->pixmap
+    = XCreatePixmapFromBitmapData (FRAME_X_DISPLAY (f),
+				   FRAME_X_WINDOW (f),
+				   data,
+				   img->width, img->height,
+				   fg, bg,
+				   DefaultDepthOfScreen (FRAME_X_SCREEN (f)));
+#endif /* HAVE_NTGUI */
+}
+
+
+
+/* Replacement for XReadBitmapFileData which isn't available under old
+   X versions.  CONTENTS is a pointer to a buffer to parse; END is the
+   buffer's end.  Set *WIDTH and *HEIGHT to the width and height of
+   the image.  Return in *DATA the bitmap data allocated with xmalloc.
+   Value is non-zero if successful.  DATA null means just test if
+   CONTENTS looks like an in-memory XBM file.  */
+
+static int
+xbm_read_bitmap_data (contents, end, width, height, data)
+     unsigned char *contents, *end;
+     int *width, *height;
+     unsigned char **data;
+{
+  unsigned char *s = contents;
+  char buffer[BUFSIZ];
+  int padding_p = 0;
+  int v10 = 0;
+  int bytes_per_line, i, nbytes;
+  unsigned char *p;
+  int value;
+  int LA1;
+
+#define match() \
+     LA1 = xbm_scan (&s, end, buffer, &value)
+
+#define expect(TOKEN)		\
+     if (LA1 != (TOKEN)) 	\
+       goto failure;		\
+     else			\
+       match ()
+
+#define expect_ident(IDENT)					\
+     if (LA1 == XBM_TK_IDENT && strcmp (buffer, (IDENT)) == 0)	\
+       match ();						\
+     else							\
+       goto failure
+
+  *width = *height = -1;
+  if (data)
+    *data = NULL;
+  LA1 = xbm_scan (&s, end, buffer, &value);
+
+  /* Parse defines for width, height and hot-spots.  */
+  while (LA1 == '#')
+    {
+      match ();
+      expect_ident ("define");
+      expect (XBM_TK_IDENT);
+
+      if (LA1 == XBM_TK_NUMBER);
+	{
+          char *p = strrchr (buffer, '_');
+	  p = p ? p + 1 : buffer;
+          if (strcmp (p, "width") == 0)
+	    *width = value;
+          else if (strcmp (p, "height") == 0)
+	    *height = value;
+	}
+      expect (XBM_TK_NUMBER);
+    }
+
+  if (*width < 0 || *height < 0)
+    goto failure;
+  else if (data == NULL)
+    goto success;
+
+  /* Parse bits.  Must start with `static'.  */
+  expect_ident ("static");
+  if (LA1 == XBM_TK_IDENT)
+    {
+      if (strcmp (buffer, "unsigned") == 0)
+	{
+	  match ();
+	  expect_ident ("char");
+	}
+      else if (strcmp (buffer, "short") == 0)
+	{
+	  match ();
+	  v10 = 1;
+	  if (*width % 16 && *width % 16 < 9)
+	    padding_p = 1;
+	}
+      else if (strcmp (buffer, "char") == 0)
+	match ();
+      else
+	goto failure;
+    }
+  else
+    goto failure;
+
+  expect (XBM_TK_IDENT);
+  expect ('[');
+  expect (']');
+  expect ('=');
+  expect ('{');
+
+  bytes_per_line = (*width + 7) / 8 + padding_p;
+  nbytes = bytes_per_line * *height;
+  p = *data = (char *) xmalloc (nbytes);
+
+  if (v10)
+    {
+      for (i = 0; i < nbytes; i += 2)
+	{
+	  int val = value;
+	  expect (XBM_TK_NUMBER);
+
+	  *p++ = XBM_BIT_SHUFFLE (val);
+	  if (!padding_p || ((i + 2) % bytes_per_line))
+	    *p++ = XBM_BIT_SHUFFLE (value >> 8);
+
+	  if (LA1 == ',' || LA1 == '}')
+	    match ();
+	  else
+	    goto failure;
+	}
+    }
+  else
+    {
+      for (i = 0; i < nbytes; ++i)
+	{
+	  int val = value;
+	  expect (XBM_TK_NUMBER);
+
+	  *p++ = XBM_BIT_SHUFFLE (val);
+
+	  if (LA1 == ',' || LA1 == '}')
+	    match ();
+	  else
+	    goto failure;
+	}
+    }
+
+ success:
+  return 1;
+
+ failure:
+
+  if (data && *data)
+    {
+      xfree (*data);
+      *data = NULL;
+    }
+  return 0;
+
+#undef match
+#undef expect
+#undef expect_ident
+}
+
+
+/* Load XBM image IMG which will be displayed on frame F from buffer
+   CONTENTS.  END is the end of the buffer.  Value is non-zero if
+   successful.  */
+
+static int
+xbm_load_image (f, img, contents, end)
+     struct frame *f;
+     struct image *img;
+     unsigned char *contents, *end;
+{
+  int rc;
+  unsigned char *data;
+  int success_p = 0;
+
+  rc = xbm_read_bitmap_data (contents, end, &img->width, &img->height, &data);
+  if (rc)
+    {
+      unsigned long foreground = FRAME_FOREGROUND_PIXEL (f);
+      unsigned long background = FRAME_BACKGROUND_PIXEL (f);
+      int non_default_colors = 0;
+      Lisp_Object value;
+
+      xassert (img->width > 0 && img->height > 0);
+
+      /* Get foreground and background colors, maybe allocate colors.  */
+      value = image_spec_value (img->spec, QCforeground, NULL);
+      if (!NILP (value))
+	{
+	  foreground = x_alloc_image_color (f, img, value, foreground);
+	  non_default_colors = 1;
+	}
+      value = image_spec_value (img->spec, QCbackground, NULL);
+      if (!NILP (value))
+	{
+	  background = x_alloc_image_color (f, img, value, background);
+	  img->background = background;
+	  img->background_valid = 1;
+	  non_default_colors = 1;
+	}
+
+      Create_Pixmap_From_Bitmap_Data (f, img, data, 
+				      foreground, background,
+				      non_default_colors);
+      xfree (data);
+
+      if (img->pixmap == NO_PIXMAP)
+	{
+	  x_clear_image (f, img);
+	  image_error ("Unable to create X pixmap for `%s'", img->spec, Qnil);
+	}
+      else
+	success_p = 1;
+    }
+  else
+    image_error ("Error loading XBM image `%s'", img->spec, Qnil);
+
+  return success_p;
+}
+
+
+/* Value is non-zero if DATA looks like an in-memory XBM file.  */
+
+static int
+xbm_file_p (data)
+     Lisp_Object data;
+{
+  int w, h;
+  return (STRINGP (data)
+	  && xbm_read_bitmap_data (SDATA (data),
+				   (SDATA (data)
+				    + SBYTES (data)),
+				   &w, &h, NULL));
+}
+
+
+/* Fill image IMG which is used on frame F with pixmap data.  Value is
+   non-zero if successful.  */
+
+static int
+xbm_load (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  int success_p = 0;
+  Lisp_Object file_name;
+
+  xassert (xbm_image_p (img->spec));
+
+  /* If IMG->spec specifies a file name, create a non-file spec from it.  */
+  file_name = image_spec_value (img->spec, QCfile, NULL);
+  if (STRINGP (file_name))
+    {
+      Lisp_Object file;
+      unsigned char *contents;
+      int size;
+      struct gcpro gcpro1;
+
+      file = x_find_image_file (file_name);
+      GCPRO1 (file);
+      if (!STRINGP (file))
+	{
+	  image_error ("Cannot find image file `%s'", file_name, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+
+      contents = slurp_file (SDATA (file), &size);
+      if (contents == NULL)
+	{
+	  image_error ("Error loading XBM image `%s'", img->spec, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+
+      success_p = xbm_load_image (f, img, contents, contents + size);
+      UNGCPRO;
+    }
+  else
+    {
+      struct image_keyword fmt[XBM_LAST];
+      Lisp_Object data;
+      unsigned long foreground = FRAME_FOREGROUND_PIXEL (f);
+      unsigned long background = FRAME_BACKGROUND_PIXEL (f);
+      int non_default_colors = 0;
+      char *bits;
+      int parsed_p;
+      int in_memory_file_p = 0;
+
+      /* See if data looks like an in-memory XBM file.  */
+      data = image_spec_value (img->spec, QCdata, NULL);
+      in_memory_file_p = xbm_file_p (data);
+
+      /* Parse the image specification.  */
+      bcopy (xbm_format, fmt, sizeof fmt);
+      parsed_p = parse_image_spec (img->spec, fmt, XBM_LAST, Qxbm);
+      xassert (parsed_p);
+
+      /* Get specified width, and height.  */
+      if (!in_memory_file_p)
+	{
+	  img->width = XFASTINT (fmt[XBM_WIDTH].value);
+	  img->height = XFASTINT (fmt[XBM_HEIGHT].value);
+	  xassert (img->width > 0 && img->height > 0);
+	}
+
+      /* Get foreground and background colors, maybe allocate colors.  */
+      if (fmt[XBM_FOREGROUND].count
+	  && STRINGP (fmt[XBM_FOREGROUND].value))
+	{
+	  foreground = x_alloc_image_color (f, img, fmt[XBM_FOREGROUND].value,
+					    foreground);
+	  non_default_colors = 1;
+	}
+
+      if (fmt[XBM_BACKGROUND].count
+	  && STRINGP (fmt[XBM_BACKGROUND].value))
+	{
+	  background = x_alloc_image_color (f, img, fmt[XBM_BACKGROUND].value,
+					    background);
+	  non_default_colors = 1;
+	}
+
+      if (in_memory_file_p)
+	success_p = xbm_load_image (f, img, SDATA (data),
+				    (SDATA (data)
+				     + SBYTES (data)));
+      else
+	{
+	  if (VECTORP (data))
+	    {
+	      int i;
+	      char *p;
+	      int nbytes = (img->width + BITS_PER_CHAR - 1) / BITS_PER_CHAR;
+
+	      p = bits = (char *) alloca (nbytes * img->height);
+	      for (i = 0; i < img->height; ++i, p += nbytes)
+		{
+		  Lisp_Object line = XVECTOR (data)->contents[i];
+		  if (STRINGP (line))
+		    bcopy (SDATA (line), p, nbytes);
+		  else
+		    bcopy (XBOOL_VECTOR (line)->data, p, nbytes);
+		}
+	    }
+	  else if (STRINGP (data))
+	    bits = SDATA (data);
+	  else
+	    bits = XBOOL_VECTOR (data)->data;
+
+	  /* Create the pixmap.  */
+
+	  Create_Pixmap_From_Bitmap_Data (f, img, bits,
+					  foreground, background,
+					  non_default_colors);
+	  if (img->pixmap)
+	    success_p = 1;
+	  else
+	    {
+	      image_error ("Unable to create pixmap for XBM image `%s'",
+			   img->spec, Qnil);
+	      x_clear_image (f, img);
+	    }
+	}
+    }
+
+  return success_p;
+}
+
+
+
+/***********************************************************************
+			      XPM images
+ ***********************************************************************/
+
+#ifdef HAVE_XPM
+
+static int xpm_image_p P_ ((Lisp_Object object));
+static int xpm_load P_ ((struct frame *f, struct image *img));
+static int xpm_valid_color_symbols_p P_ ((Lisp_Object));
+
+#ifdef HAVE_NTGUI
+/* Indicate to xpm.h that we don't have Xlib.  */
+#define FOR_MSW
+/* simx.h in xpm defines XColor and XImage differently than Emacs.  */
+#define XColor xpm_XColor
+#define XImage xpm_XImage
+#define PIXEL_ALREADY_TYPEDEFED
+#include "X11/xpm.h"
+#undef FOR_MSW
+#undef XColor
+#undef XImage
+#undef PIXEL_ALREADY_TYPEDEFED
+#else
+#include "X11/xpm.h"
+#endif /* HAVE_NTGUI */
+
+/* The symbol `xpm' identifying XPM-format images.  */
+
+Lisp_Object Qxpm;
+
+/* Indices of image specification fields in xpm_format, below.  */
+
+enum xpm_keyword_index
+{
+  XPM_TYPE,
+  XPM_FILE,
+  XPM_DATA,
+  XPM_ASCENT,
+  XPM_MARGIN,
+  XPM_RELIEF,
+  XPM_ALGORITHM,
+  XPM_HEURISTIC_MASK,
+  XPM_MASK,
+  XPM_COLOR_SYMBOLS,
+  XPM_BACKGROUND,
+  XPM_LAST
+};
+
+/* Vector of image_keyword structures describing the format
+   of valid XPM image specifications.  */
+
+static struct image_keyword xpm_format[XPM_LAST] =
+{
+  {":type",		IMAGE_SYMBOL_VALUE,			1},
+  {":file",		IMAGE_STRING_VALUE,			0},
+  {":data",		IMAGE_STRING_VALUE,			0},
+  {":ascent",		IMAGE_ASCENT_VALUE,			0},
+  {":margin",		IMAGE_POSITIVE_INTEGER_VALUE_OR_PAIR,	0},
+  {":relief",		IMAGE_INTEGER_VALUE,			0},
+  {":conversion",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":heuristic-mask",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":mask",		IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":color-symbols",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":background",	IMAGE_STRING_OR_NIL_VALUE,		0}
+};
+
+/* Structure describing the image type XPM.  */
+
+static struct image_type xpm_type =
+{
+  &Qxpm,
+  xpm_image_p,
+  xpm_load,
+  x_clear_image,
+  NULL
+};
+
+#ifdef HAVE_X_WINDOWS
+
+/* Define ALLOC_XPM_COLORS if we can use Emacs' own color allocation
+   functions for allocating image colors.  Our own functions handle
+   color allocation failures more gracefully than the ones on the XPM
+   lib.  */
+
+#if defined XpmAllocColor && defined XpmFreeColors && defined XpmColorClosure
+#define ALLOC_XPM_COLORS
+#endif
+#endif /* HAVE_X_WINDOWS */
+
+#ifdef ALLOC_XPM_COLORS
+
+static void xpm_init_color_cache P_ ((struct frame *, XpmAttributes *));
+static void xpm_free_color_cache P_ ((void));
+static int xpm_lookup_color P_ ((struct frame *, char *, XColor *));
+static int xpm_color_bucket P_ ((char *));
+static struct xpm_cached_color *xpm_cache_color P_ ((struct frame *, char *,
+						     XColor *, int));
+
+/* An entry in a hash table used to cache color definitions of named
+   colors.  This cache is necessary to speed up XPM image loading in
+   case we do color allocations ourselves.  Without it, we would need
+   a call to XParseColor per pixel in the image.  */
+
+struct xpm_cached_color
+{
+  /* Next in collision chain.  */
+  struct xpm_cached_color *next;
+
+  /* Color definition (RGB and pixel color).  */
+  XColor color;
+
+  /* Color name.  */
+  char name[1];
+};
+
+/* The hash table used for the color cache, and its bucket vector
+   size.  */
+
+#define XPM_COLOR_CACHE_BUCKETS	1001
+struct xpm_cached_color **xpm_color_cache;
+
+/* Initialize the color cache.  */
+
+static void
+xpm_init_color_cache (f, attrs)
+     struct frame *f;
+     XpmAttributes *attrs;
+{
+  size_t nbytes = XPM_COLOR_CACHE_BUCKETS * sizeof *xpm_color_cache;
+  xpm_color_cache = (struct xpm_cached_color **) xmalloc (nbytes);
+  memset (xpm_color_cache, 0, nbytes);
+  init_color_table ();
+
+  if (attrs->valuemask & XpmColorSymbols)
+    {
+      int i;
+      XColor color;
+
+      for (i = 0; i < attrs->numsymbols; ++i)
+	if (XParseColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f),
+			 attrs->colorsymbols[i].value, &color))
+	  {
+	    color.pixel = lookup_rgb_color (f, color.red, color.green,
+					    color.blue);
+	    xpm_cache_color (f, attrs->colorsymbols[i].name, &color, -1);
+	  }
+    }
+}
+
+/* Free the color cache.  */
+
+static void
+xpm_free_color_cache ()
+{
+  struct xpm_cached_color *p, *next;
+  int i;
+
+  for (i = 0; i < XPM_COLOR_CACHE_BUCKETS; ++i)
+    for (p = xpm_color_cache[i]; p; p = next)
+      {
+	next = p->next;
+	xfree (p);
+      }
+
+  xfree (xpm_color_cache);
+  xpm_color_cache = NULL;
+  free_color_table ();
+}
+
+/* Return the bucket index for color named COLOR_NAME in the color
+   cache.  */
+
+static int
+xpm_color_bucket (color_name)
+     char *color_name;
+{
+  unsigned h = 0;
+  char *s;
+
+  for (s = color_name; *s; ++s)
+    h = (h << 2) ^ *s;
+  return h %= XPM_COLOR_CACHE_BUCKETS;
+}
+
+
+/* On frame F, cache values COLOR for color with name COLOR_NAME.
+   BUCKET, if >= 0, is a precomputed bucket index.  Value is the cache
+   entry added.  */
+
+static struct xpm_cached_color *
+xpm_cache_color (f, color_name, color, bucket)
+     struct frame *f;
+     char *color_name;
+     XColor *color;
+     int bucket;
+{
+  size_t nbytes;
+  struct xpm_cached_color *p;
+
+  if (bucket < 0)
+    bucket = xpm_color_bucket (color_name);
+
+  nbytes = sizeof *p + strlen (color_name);
+  p = (struct xpm_cached_color *) xmalloc (nbytes);
+  strcpy (p->name, color_name);
+  p->color = *color;
+  p->next = xpm_color_cache[bucket];
+  xpm_color_cache[bucket] = p;
+  return p;
+}
+
+/* Look up color COLOR_NAME for frame F in the color cache.  If found,
+   return the cached definition in *COLOR.  Otherwise, make a new
+   entry in the cache and allocate the color.  Value is zero if color
+   allocation failed.  */
+
+static int
+xpm_lookup_color (f, color_name, color)
+     struct frame *f;
+     char *color_name;
+     XColor *color;
+{
+  struct xpm_cached_color *p;
+  int h = xpm_color_bucket (color_name);
+
+  for (p = xpm_color_cache[h]; p; p = p->next)
+    if (strcmp (p->name, color_name) == 0)
+      break;
+
+  if (p != NULL)
+    *color = p->color;
+  else if (XParseColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f),
+			color_name, color))
+    {
+      color->pixel = lookup_rgb_color (f, color->red, color->green,
+				       color->blue);
+      p = xpm_cache_color (f, color_name, color, h);
+    }
+  /* You get `opaque' at least from ImageMagick converting pbm to xpm
+     with transparency, and it's useful.  */
+  else if (strcmp ("opaque", color_name) == 0)
+    {
+      bzero (color, sizeof (XColor));  /* Is this necessary/correct?  */
+      color->pixel = FRAME_FOREGROUND_PIXEL (f);
+      p = xpm_cache_color (f, color_name, color, h);
+    }
+
+  return p != NULL;
+}
+
+
+/* Callback for allocating color COLOR_NAME.  Called from the XPM lib.
+   CLOSURE is a pointer to the frame on which we allocate the
+   color.  Return in *COLOR the allocated color.  Value is non-zero
+   if successful.  */
+
+static int
+xpm_alloc_color (dpy, cmap, color_name, color, closure)
+     Display *dpy;
+     Colormap cmap;
+     char *color_name;
+     XColor *color;
+     void *closure;
+{
+  return xpm_lookup_color ((struct frame *) closure, color_name, color);
+}
+
+
+/* Callback for freeing NPIXELS colors contained in PIXELS.  CLOSURE
+   is a pointer to the frame on which we allocate the color.  Value is
+   non-zero if successful.  */
+
+static int
+xpm_free_colors (dpy, cmap, pixels, npixels, closure)
+     Display *dpy;
+     Colormap cmap;
+     Pixel *pixels;
+     int npixels;
+     void *closure;
+{
+  return 1;
+}
+
+#endif /* ALLOC_XPM_COLORS */
+
+
+#ifdef HAVE_NTGUI
+
+/* XPM library details.  */
+
+DEF_IMGLIB_FN (XpmFreeAttributes);
+DEF_IMGLIB_FN (XpmCreateImageFromBuffer);
+DEF_IMGLIB_FN (XpmReadFileToImage);
+DEF_IMGLIB_FN (XImageFree);
+
+
+static int
+init_xpm_functions (void)
+{
+  HMODULE library;
+
+  if (!(library = LoadLibrary ("libXpm.dll")))
+    return 0;
+
+  LOAD_IMGLIB_FN (library, XpmFreeAttributes);
+  LOAD_IMGLIB_FN (library, XpmCreateImageFromBuffer);
+  LOAD_IMGLIB_FN (library, XpmReadFileToImage);
+  LOAD_IMGLIB_FN (library, XImageFree);
+  return 1;
+}
+
+#endif /* HAVE_NTGUI */
+
+
+/* Value is non-zero if COLOR_SYMBOLS is a valid color symbols list
+   for XPM images.  Such a list must consist of conses whose car and
+   cdr are strings.  */
+
+static int
+xpm_valid_color_symbols_p (color_symbols)
+     Lisp_Object color_symbols;
+{
+  while (CONSP (color_symbols))
+    {
+      Lisp_Object sym = XCAR (color_symbols);
+      if (!CONSP (sym)
+	  || !STRINGP (XCAR (sym))
+	  || !STRINGP (XCDR (sym)))
+	break;
+      color_symbols = XCDR (color_symbols);
+    }
+
+  return NILP (color_symbols);
+}
+
+
+/* Value is non-zero if OBJECT is a valid XPM image specification.  */
+
+static int
+xpm_image_p (object)
+     Lisp_Object object;
+{
+  struct image_keyword fmt[XPM_LAST];
+  bcopy (xpm_format, fmt, sizeof fmt);
+  return (parse_image_spec (object, fmt, XPM_LAST, Qxpm)
+	  /* Either `:file' or `:data' must be present.  */
+	  && fmt[XPM_FILE].count + fmt[XPM_DATA].count == 1
+	  /* Either no `:color-symbols' or it's a list of conses
+	     whose car and cdr are strings.  */
+	  && (fmt[XPM_COLOR_SYMBOLS].count == 0
+	      || xpm_valid_color_symbols_p (fmt[XPM_COLOR_SYMBOLS].value)));
+}
+
+
+/* Load image IMG which will be displayed on frame F.  Value is
+   non-zero if successful.  */
+
+static int
+xpm_load (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  int rc;
+  XpmAttributes attrs;
+  Lisp_Object specified_file, color_symbols;
+#ifdef HAVE_NTGUI
+  HDC hdc;
+  xpm_XImage * xpm_image = NULL, * xpm_mask = NULL;
+#endif /* HAVE_NTGUI */
+
+  /* Configure the XPM lib.  Use the visual of frame F.  Allocate
+     close colors.  Return colors allocated.  */
+  bzero (&attrs, sizeof attrs);
+
+#ifndef HAVE_NTGUI
+  attrs.visual = FRAME_X_VISUAL (f);
+  attrs.colormap = FRAME_X_COLORMAP (f);
+  attrs.valuemask |= XpmVisual;
+  attrs.valuemask |= XpmColormap;
+#endif /* HAVE_NTGUI */
+
+#ifdef ALLOC_XPM_COLORS
+  /* Allocate colors with our own functions which handle
+     failing color allocation more gracefully.  */
+  attrs.color_closure = f;
+  attrs.alloc_color = xpm_alloc_color;
+  attrs.free_colors = xpm_free_colors;
+  attrs.valuemask |= XpmAllocColor | XpmFreeColors | XpmColorClosure;
+#else /* not ALLOC_XPM_COLORS */
+  /* Let the XPM lib allocate colors.  */
+  attrs.valuemask |= XpmReturnAllocPixels;
+#ifdef XpmAllocCloseColors
+  attrs.alloc_close_colors = 1;
+  attrs.valuemask |= XpmAllocCloseColors;
+#else /* not XpmAllocCloseColors */
+  attrs.closeness = 600;
+  attrs.valuemask |= XpmCloseness;
+#endif /* not XpmAllocCloseColors */
+#endif /* ALLOC_XPM_COLORS */
+
+  /* If image specification contains symbolic color definitions, add
+     these to `attrs'.  */
+  color_symbols = image_spec_value (img->spec, QCcolor_symbols, NULL);
+  if (CONSP (color_symbols))
+    {
+      Lisp_Object tail;
+      XpmColorSymbol *xpm_syms;
+      int i, size;
+
+      attrs.valuemask |= XpmColorSymbols;
+
+      /* Count number of symbols.  */
+      attrs.numsymbols = 0;
+      for (tail = color_symbols; CONSP (tail); tail = XCDR (tail))
+	++attrs.numsymbols;
+
+      /* Allocate an XpmColorSymbol array.  */
+      size = attrs.numsymbols * sizeof *xpm_syms;
+      xpm_syms = (XpmColorSymbol *) alloca (size);
+      bzero (xpm_syms, size);
+      attrs.colorsymbols = xpm_syms;
+
+      /* Fill the color symbol array.  */
+      for (tail = color_symbols, i = 0;
+	   CONSP (tail);
+	   ++i, tail = XCDR (tail))
+	{
+	  Lisp_Object name = XCAR (XCAR (tail));
+	  Lisp_Object color = XCDR (XCAR (tail));
+	  xpm_syms[i].name = (char *) alloca (SCHARS (name) + 1);
+	  strcpy (xpm_syms[i].name, SDATA (name));
+	  xpm_syms[i].value = (char *) alloca (SCHARS (color) + 1);
+	  strcpy (xpm_syms[i].value, SDATA (color));
+	}
+    }
+
+  /* Create a pixmap for the image, either from a file, or from a
+     string buffer containing data in the same format as an XPM file.  */
+#ifdef ALLOC_XPM_COLORS
+  xpm_init_color_cache (f, &attrs);
+#endif
+
+  specified_file = image_spec_value (img->spec, QCfile, NULL);
+
+#ifdef HAVE_NTGUI
+  {
+    HDC frame_dc = get_frame_dc (f);
+    hdc = CreateCompatibleDC (frame_dc);
+    release_frame_dc (f, frame_dc);
+  }
+#endif /* HAVE_NTGUI */
+
+  if (STRINGP (specified_file))
+    {
+      Lisp_Object file = x_find_image_file (specified_file);
+      if (!STRINGP (file))
+	{
+	  image_error ("Cannot find image file `%s'", specified_file, Qnil);
+	  return 0;
+	}
+
+#ifdef HAVE_NTGUI
+      /* XpmReadFileToPixmap is not available in the Windows port of
+	 libxpm.  But XpmReadFileToImage almost does what we want.  */
+      rc = fn_XpmReadFileToImage (&hdc, SDATA (file),
+				  &xpm_image, &xpm_mask,
+				  &attrs);
+#else
+      rc = XpmReadFileToPixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+				SDATA (file), &img->pixmap, &img->mask,
+				&attrs);
+#endif /* HAVE_NTGUI */
+    }
+  else
+    {
+      Lisp_Object buffer = image_spec_value (img->spec, QCdata, NULL);
+#ifdef HAVE_NTGUI
+      /* XpmCreatePixmapFromBuffer is not available in the Windows port
+	 of libxpm.  But XpmCreateImageFromBuffer almost does what we want.  */
+      rc = fn_XpmCreateImageFromBuffer (&hdc, SDATA (buffer),
+					&xpm_image, &xpm_mask,
+					&attrs);
+#else
+      rc = XpmCreatePixmapFromBuffer (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+				      SDATA (buffer),
+				      &img->pixmap, &img->mask,
+				      &attrs);
+#endif /* HAVE_NTGUI */
+    }
+
+  if (rc == XpmSuccess)
+    {
+#if defined (COLOR_TABLE_SUPPORT) && defined (ALLOC_XPM_COLORS)
+      img->colors = colors_in_color_table (&img->ncolors);
+#else /* not ALLOC_XPM_COLORS */
+      int i;
+
+#ifdef HAVE_NTGUI
+      /* W32 XPM uses XImage to wrap what W32 Emacs calls a Pixmap,
+	 plus some duplicate attributes.  */
+      if (xpm_image && xpm_image->bitmap)
+	{
+	  img->pixmap = xpm_image->bitmap;
+	  /* XImageFree in libXpm frees XImage struct without destroying
+	     the bitmap, which is what we want.  */
+	  fn_XImageFree (xpm_image);
+	}
+      if (xpm_mask && xpm_mask->bitmap)
+	{
+	  /* The mask appears to be inverted compared with what we expect.
+	     TODO: invert our expectations.  See other places where we
+	     have to invert bits because our idea of masks is backwards.  */
+	  HGDIOBJ old_obj;
+	  old_obj = SelectObject (hdc, xpm_mask->bitmap);
+
+	  PatBlt (hdc, 0, 0, xpm_mask->width, xpm_mask->height, DSTINVERT);
+	  SelectObject (hdc, old_obj);
+
+	  img->mask = xpm_mask->bitmap;
+	  fn_XImageFree (xpm_mask);
+	  DeleteDC (hdc);
+	}
+
+      DeleteDC (hdc);
+#endif /* HAVE_NTGUI */
+
+      /* Remember allocated colors.  */
+      img->ncolors = attrs.nalloc_pixels;
+      img->colors = (unsigned long *) xmalloc (img->ncolors
+					       * sizeof *img->colors);
+      for (i = 0; i < attrs.nalloc_pixels; ++i)
+	{
+	  img->colors[i] = attrs.alloc_pixels[i];
+#ifdef DEBUG_X_COLORS
+	  register_color (img->colors[i]);
+#endif
+	}
+#endif /* not ALLOC_XPM_COLORS */
+
+      img->width = attrs.width;
+      img->height = attrs.height;
+      xassert (img->width > 0 && img->height > 0);
+
+      /* The call to XpmFreeAttributes below frees attrs.alloc_pixels.  */
+#ifdef HAVE_NTGUI
+      fn_XpmFreeAttributes (&attrs);
+#else
+      XpmFreeAttributes (&attrs);
+#endif /* HAVE_NTGUI */
+    }
+  else
+    {
+#ifdef HAVE_NTGUI
+      DeleteDC (hdc);
+#endif /* HAVE_NTGUI */
+
+      switch (rc)
+	{
+	case XpmOpenFailed:
+	  image_error ("Error opening XPM file (%s)", img->spec, Qnil);
+	  break;
+
+	case XpmFileInvalid:
+	  image_error ("Invalid XPM file (%s)", img->spec, Qnil);
+	  break;
+
+	case XpmNoMemory:
+	  image_error ("Out of memory (%s)", img->spec, Qnil);
+	  break;
+
+	case XpmColorFailed:
+	  image_error ("Color allocation error (%s)", img->spec, Qnil);
+	  break;
+
+	default:
+	  image_error ("Unknown error (%s)", img->spec, Qnil);
+	  break;
+	}
+    }
+
+#ifdef ALLOC_XPM_COLORS
+  xpm_free_color_cache ();
+#endif
+  return rc == XpmSuccess;
+}
+
+#endif /* HAVE_XPM */
+
+
+/***********************************************************************
+			     Color table
+ ***********************************************************************/
+
+#ifdef COLOR_TABLE_SUPPORT
+
+/* An entry in the color table mapping an RGB color to a pixel color.  */
+
+struct ct_color
+{
+  int r, g, b;
+  unsigned long pixel;
+
+  /* Next in color table collision list.  */
+  struct ct_color *next;
+};
+
+/* The bucket vector size to use.  Must be prime.  */
+
+#define CT_SIZE 101
+
+/* Value is a hash of the RGB color given by R, G, and B.  */
+
+#define CT_HASH_RGB(R, G, B) (((R) << 16) ^ ((G) << 8) ^ (B))
+
+/* The color hash table.  */
+
+struct ct_color **ct_table;
+
+/* Number of entries in the color table.  */
+
+int ct_colors_allocated;
+
+/* Initialize the color table.  */
+
+static void
+init_color_table ()
+{
+  int size = CT_SIZE * sizeof (*ct_table);
+  ct_table = (struct ct_color **) xmalloc (size);
+  bzero (ct_table, size);
+  ct_colors_allocated = 0;
+}
+
+
+/* Free memory associated with the color table.  */
+
+static void
+free_color_table ()
+{
+  int i;
+  struct ct_color *p, *next;
+
+  for (i = 0; i < CT_SIZE; ++i)
+    for (p = ct_table[i]; p; p = next)
+      {
+	next = p->next;
+	xfree (p);
+      }
+
+  xfree (ct_table);
+  ct_table = NULL;
+}
+
+
+/* Value is a pixel color for RGB color R, G, B on frame F.  If an
+   entry for that color already is in the color table, return the
+   pixel color of that entry.  Otherwise, allocate a new color for R,
+   G, B, and make an entry in the color table.  */
+
+static unsigned long
+lookup_rgb_color (f, r, g, b)
+     struct frame *f;
+     int r, g, b;
+{
+  unsigned hash = CT_HASH_RGB (r, g, b);
+  int i = hash % CT_SIZE;
+  struct ct_color *p;
+  Display_Info *dpyinfo;
+
+  /* Handle TrueColor visuals specially, which improves performance by
+     two orders of magnitude.  Freeing colors on TrueColor visuals is
+     a nop, and pixel colors specify RGB values directly.  See also
+     the Xlib spec, chapter 3.1.  */
+  dpyinfo = FRAME_X_DISPLAY_INFO (f);
+  if (dpyinfo->red_bits > 0)
+    {
+      unsigned long pr, pg, pb;
+
+      /* Apply gamma-correction like normal color allocation does.  */
+      if (f->gamma)
+	{
+	  XColor color;
+	  color.red = r, color.green = g, color.blue = b;
+	  gamma_correct (f, &color);
+	  r = color.red, g = color.green, b = color.blue;
+	}
+
+      /* Scale down RGB values to the visual's bits per RGB, and shift
+	 them to the right position in the pixel color.  Note that the
+	 original RGB values are 16-bit values, as usual in X.  */
+      pr = (r >> (16 - dpyinfo->red_bits))   << dpyinfo->red_offset;
+      pg = (g >> (16 - dpyinfo->green_bits)) << dpyinfo->green_offset;
+      pb = (b >> (16 - dpyinfo->blue_bits))  << dpyinfo->blue_offset;
+
+      /* Assemble the pixel color.  */
+      return pr | pg | pb;
+    }
+  
+  for (p = ct_table[i]; p; p = p->next)
+    if (p->r == r && p->g == g && p->b == b)
+      break;
+
+  if (p == NULL)
+    {
+
+#ifdef HAVE_X_WINDOWS
+      XColor color;
+      Colormap cmap;
+      int rc;
+
+      color.red = r;
+      color.green = g;
+      color.blue = b;
+
+      cmap = FRAME_X_COLORMAP (f);
+      rc = x_alloc_nearest_color (f, cmap, &color);
+      if (rc)
+	{
+	  ++ct_colors_allocated;
+	  p = (struct ct_color *) xmalloc (sizeof *p);
+	  p->r = r;
+	  p->g = g;
+	  p->b = b;
+	  p->pixel = color.pixel;
+	  p->next = ct_table[i];
+	  ct_table[i] = p;
+	}
+      else
+	return FRAME_FOREGROUND_PIXEL (f);
+
+#else
+      COLORREF color;
+#ifdef HAVE_NTGUI
+      color = PALETTERGB (r, g, b);
+#else
+      color = RGB_TO_ULONG (r, g, b);
+#endif /* HAVE_NTGUI */
+      ++ct_colors_allocated;
+      p = (struct ct_color *) xmalloc (sizeof *p);
+      p->r = r;
+      p->g = g;
+      p->b = b;
+      p->pixel = color;
+      p->next = ct_table[i];
+      ct_table[i] = p;
+#endif /* HAVE_X_WINDOWS */
+
+    }
+
+  return p->pixel;
+}
+
+
+/* Look up pixel color PIXEL which is used on frame F in the color
+   table.  If not already present, allocate it.  Value is PIXEL.  */
+
+static unsigned long
+lookup_pixel_color (f, pixel)
+     struct frame *f;
+     unsigned long pixel;
+{
+  int i = pixel % CT_SIZE;
+  struct ct_color *p;
+
+  for (p = ct_table[i]; p; p = p->next)
+    if (p->pixel == pixel)
+      break;
+
+  if (p == NULL)
+    {
+      XColor color;
+      Colormap cmap;
+      int rc;
+
+#ifdef HAVE_X_WINDOWS
+      cmap = FRAME_X_COLORMAP (f);
+      color.pixel = pixel;
+      x_query_color (f, &color);
+      rc = x_alloc_nearest_color (f, cmap, &color);
+#else
+      BLOCK_INPUT;
+      cmap = DefaultColormapOfScreen (FRAME_X_SCREEN (f));
+      color.pixel = pixel;
+      XQueryColor (NULL, cmap, &color);
+      rc = x_alloc_nearest_color (f, cmap, &color);
+      UNBLOCK_INPUT;
+#endif /* HAVE_X_WINDOWS */
+
+      if (rc)
+	{
+	  ++ct_colors_allocated;
+
+	  p = (struct ct_color *) xmalloc (sizeof *p);
+	  p->r = color.red;
+	  p->g = color.green;
+	  p->b = color.blue;
+	  p->pixel = pixel;
+	  p->next = ct_table[i];
+	  ct_table[i] = p;
+	}
+      else
+	return FRAME_FOREGROUND_PIXEL (f);
+    }
+  return p->pixel;
+}
+
+
+/* Value is a vector of all pixel colors contained in the color table,
+   allocated via xmalloc.  Set *N to the number of colors.  */
+
+static unsigned long *
+colors_in_color_table (n)
+     int *n;
+{
+  int i, j;
+  struct ct_color *p;
+  unsigned long *colors;
+
+  if (ct_colors_allocated == 0)
+    {
+      *n = 0;
+      colors = NULL;
+    }
+  else
+    {
+      colors = (unsigned long *) xmalloc (ct_colors_allocated
+					  * sizeof *colors);
+      *n = ct_colors_allocated;
+
+      for (i = j = 0; i < CT_SIZE; ++i)
+	for (p = ct_table[i]; p; p = p->next)
+	  colors[j++] = p->pixel;
+    }
+
+  return colors;
+}
+
+#else /* COLOR_TABLE_SUPPORT */
+
+static unsigned long
+lookup_rgb_color (f, r, g, b)
+     struct frame *f;
+     int r, g, b;
+{
+  unsigned long pixel;
+
+#ifdef MAC_OS
+  pixel = RGB_TO_ULONG (r >> 8, g >> 8, b >> 8);
+  gamma_correct (f, &pixel);
+#endif /* MAC_OS */
+
+#ifdef HAVE_NTGUI
+  pixel = PALETTERGB (r >> 8, g >> 8, b >> 8);
+#endif /* HAVE_NTGUI */
+
+  return pixel;
+}
+
+static void
+init_color_table ()
+{
+}
+#endif /* COLOR_TABLE_SUPPORT */
+
+
+/***********************************************************************
+			      Algorithms
+ ***********************************************************************/
+
+static XColor *x_to_xcolors P_ ((struct frame *, struct image *, int));
+static void x_from_xcolors P_ ((struct frame *, struct image *, XColor *));
+static void x_detect_edges P_ ((struct frame *, struct image *, int[9], int));
+
+#ifdef HAVE_NTGUI
+static void XPutPixel (XImagePtr , int, int, COLORREF);
+#endif /* HAVE_NTGUI */
+
+/* Non-zero means draw a cross on images having `:conversion
+   disabled'.  */
+
+int cross_disabled_images;
+
+/* Edge detection matrices for different edge-detection
+   strategies.  */
+
+static int emboss_matrix[9] = {
+   /* x - 1	x	x + 1  */
+        2,     -1,  	  0,		/* y - 1 */
+       -1,      0,        1,		/* y     */
+        0,      1,       -2		/* y + 1 */
+};
+
+static int laplace_matrix[9] = {
+   /* x - 1	x	x + 1  */
+        1,      0,  	  0,		/* y - 1 */
+        0,      0,        0,		/* y     */
+        0,      0,       -1		/* y + 1 */
+};
+
+/* Value is the intensity of the color whose red/green/blue values
+   are R, G, and B.  */
+
+#define COLOR_INTENSITY(R, G, B) ((2 * (R) + 3 * (G) + (B)) / 6)
+
+
+/* On frame F, return an array of XColor structures describing image
+   IMG->pixmap.  Each XColor structure has its pixel color set.  RGB_P
+   non-zero means also fill the red/green/blue members of the XColor
+   structures.  Value is a pointer to the array of XColors structures,
+   allocated with xmalloc; it must be freed by the caller.  */
+
+static XColor *
+x_to_xcolors (f, img, rgb_p)
+     struct frame *f;
+     struct image *img;
+     int rgb_p;
+{
+  int x, y;
+  XColor *colors, *p;
+  XImagePtr_or_DC ximg;
+#ifdef HAVE_NTGUI
+  HDC hdc;
+  HGDIOBJ prev;
+#endif /* HAVE_NTGUI */
+
+  colors = (XColor *) xmalloc (img->width * img->height * sizeof *colors);
+
+#ifndef HAVE_NTGUI
+  /* Get the X image IMG->pixmap.  */
+  ximg = XGetImage (FRAME_X_DISPLAY (f), img->pixmap,
+		    0, 0, img->width, img->height, ~0, ZPixmap);
+#else
+  /* Load the image into a memory device context.  */
+  hdc = get_frame_dc (f);
+  ximg = CreateCompatibleDC (hdc);
+  release_frame_dc (f, hdc);
+  prev = SelectObject (ximg, img->pixmap);
+#endif /* HAVE_NTGUI */
+
+  /* Fill the `pixel' members of the XColor array.  I wished there
+     were an easy and portable way to circumvent XGetPixel.  */
+  p = colors;
+  for (y = 0; y < img->height; ++y)
+    {
+      XColor *row = p;
+
+#ifdef HAVE_X_WINDOWS
+      for (x = 0; x < img->width; ++x, ++p)
+	p->pixel = XGetPixel (ximg, x, y);
+      if (rgb_p)
+	x_query_colors (f, row, img->width);
+
+#else
+
+      for (x = 0; x < img->width; ++x, ++p)
+	{
+	  /* W32_TODO: palette support needed here?  */
+	  p->pixel = GET_PIXEL (ximg, x, y);
+	  if (rgb_p)
+	    {
+#ifdef MAC_OS
+	      p->red = RED16_FROM_ULONG (p->pixel);
+	      p->green = GREEN16_FROM_ULONG (p->pixel);
+	      p->blue = BLUE16_FROM_ULONG (p->pixel);
+#endif  /* MAC_OS */
+#ifdef HAVE_NTGUI
+	      p->red = 256 * GetRValue (p->pixel);
+	      p->green = 256 * GetGValue (p->pixel);
+	      p->blue = 256 * GetBValue (p->pixel);
+#endif /* HAVE_NTGUI */
+	    }
+	}
+#endif /* HAVE_X_WINDOWS */
+    }
+
+  Destroy_Image (ximg, prev);
+
+  return colors;
+}
+
+#ifdef HAVE_NTGUI
+
+/* Put a pixel of COLOR at position X, Y in XIMG.  XIMG must have been
+   created with CreateDIBSection, with the pointer to the bit values
+   stored in ximg->data.  */
+
+static void XPutPixel (ximg, x, y, color)
+     XImagePtr  ximg;
+     int x, y;
+     COLORREF color;
+{
+  int width = ximg->info.bmiHeader.biWidth;
+  int height = ximg->info.bmiHeader.biHeight;
+  unsigned char * pixel;
+
+  /* True color images.  */
+  if (ximg->info.bmiHeader.biBitCount == 24)
+    {
+      int rowbytes = width * 3;
+      /* Ensure scanlines are aligned on 4 byte boundaries.  */
+      if (rowbytes % 4)
+	rowbytes += 4 - (rowbytes % 4);
+
+      pixel = ximg->data + y * rowbytes + x * 3;
+      /* Windows bitmaps are in BGR order.  */
+      *pixel = GetBValue (color);
+      *(pixel + 1) = GetGValue (color);
+      *(pixel + 2) = GetRValue (color);
+    }
+  /* Monochrome images.  */
+  else if (ximg->info.bmiHeader.biBitCount == 1)
+    {
+      int rowbytes = width / 8;
+      /* Ensure scanlines are aligned on 4 byte boundaries.  */
+      if (rowbytes % 4)
+	rowbytes += 4 - (rowbytes % 4);
+      pixel = ximg->data + y * rowbytes + x / 8;
+      /* Filter out palette info.  */
+      if (color & 0x00ffffff)
+	*pixel = *pixel | (1 << x % 8);
+      else
+	*pixel = *pixel & ~(1 << x % 8);
+    }
+  else
+    image_error ("XPutPixel: palette image not supported", Qnil, Qnil);
+}
+
+#endif /* HAVE_NTGUI */
+
+/* Create IMG->pixmap from an array COLORS of XColor structures, whose
+   RGB members are set.  F is the frame on which this all happens.
+   COLORS will be freed; an existing IMG->pixmap will be freed, too.  */
+
+static void
+x_from_xcolors (f, img, colors)
+     struct frame *f;
+     struct image *img;
+     XColor *colors;
+{
+  int x, y;
+  XImagePtr oimg;
+  Pixmap pixmap;
+  XColor *p;
+
+  init_color_table ();
+
+  x_create_x_image_and_pixmap (f, img->width, img->height, 0,
+			       &oimg, &pixmap);
+  p = colors;
+  for (y = 0; y < img->height; ++y)
+    for (x = 0; x < img->width; ++x, ++p)
+      {
+	unsigned long pixel;
+	pixel = lookup_rgb_color (f, p->red, p->green, p->blue);
+	XPutPixel (oimg, x, y, pixel);
+      }
+
+  xfree (colors);
+  x_clear_image_1 (f, img, 1, 0, 1);
+
+  x_put_x_image (f, oimg, pixmap, img->width, img->height);
+  x_destroy_x_image (oimg);
+  img->pixmap = pixmap;
+#ifdef COLOR_TABLE_SUPPORT
+  img->colors = colors_in_color_table (&img->ncolors);
+  free_color_table ();
+#endif /* COLOR_TABLE_SUPPORT */
+}
+
+
+/* On frame F, perform edge-detection on image IMG.
+
+   MATRIX is a nine-element array specifying the transformation
+   matrix.  See emboss_matrix for an example.
+
+   COLOR_ADJUST is a color adjustment added to each pixel of the
+   outgoing image.  */
+
+static void
+x_detect_edges (f, img, matrix, color_adjust)
+     struct frame *f;
+     struct image *img;
+     int matrix[9], color_adjust;
+{
+  XColor *colors = x_to_xcolors (f, img, 1);
+  XColor *new, *p;
+  int x, y, i, sum;
+
+  for (i = sum = 0; i < 9; ++i)
+    sum += abs (matrix[i]);
+
+#define COLOR(A, X, Y) ((A) + (Y) * img->width + (X))
+
+  new = (XColor *) xmalloc (img->width * img->height * sizeof *new);
+
+  for (y = 0; y < img->height; ++y)
+    {
+      p = COLOR (new, 0, y);
+      p->red = p->green = p->blue = 0xffff/2;
+      p = COLOR (new, img->width - 1, y);
+      p->red = p->green = p->blue = 0xffff/2;
+    }
+
+  for (x = 1; x < img->width - 1; ++x)
+    {
+      p = COLOR (new, x, 0);
+      p->red = p->green = p->blue = 0xffff/2;
+      p = COLOR (new, x, img->height - 1);
+      p->red = p->green = p->blue = 0xffff/2;
+    }
+
+  for (y = 1; y < img->height - 1; ++y)
+    {
+      p = COLOR (new, 1, y);
+
+      for (x = 1; x < img->width - 1; ++x, ++p)
+	{
+	  int r, g, b, y1, x1;
+
+	  r = g = b = i = 0;
+	  for (y1 = y - 1; y1 < y + 2; ++y1)
+	    for (x1 = x - 1; x1 < x + 2; ++x1, ++i)
+	      if (matrix[i])
+	        {
+	          XColor *t = COLOR (colors, x1, y1);
+		  r += matrix[i] * t->red;
+		  g += matrix[i] * t->green;
+		  b += matrix[i] * t->blue;
+		}
+
+	  r = (r / sum + color_adjust) & 0xffff;
+	  g = (g / sum + color_adjust) & 0xffff;
+	  b = (b / sum + color_adjust) & 0xffff;
+	  p->red = p->green = p->blue = COLOR_INTENSITY (r, g, b);
+	}
+    }
+
+  xfree (colors);
+  x_from_xcolors (f, img, new);
+
+#undef COLOR
+}
+
+
+/* Perform the pre-defined `emboss' edge-detection on image IMG
+   on frame F.  */
+
+static void
+x_emboss (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  x_detect_edges (f, img, emboss_matrix, 0xffff / 2);
+}
+
+
+/* Transform image IMG which is used on frame F with a Laplace
+   edge-detection algorithm.  The result is an image that can be used
+   to draw disabled buttons, for example.  */
+
+static void
+x_laplace (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  x_detect_edges (f, img, laplace_matrix, 45000);
+}
+
+
+/* Perform edge-detection on image IMG on frame F, with specified
+   transformation matrix MATRIX and color-adjustment COLOR_ADJUST.
+
+   MATRIX must be either
+
+   - a list of at least 9 numbers in row-major form
+   - a vector of at least 9 numbers
+
+   COLOR_ADJUST nil means use a default; otherwise it must be a
+   number.  */
+
+static void
+x_edge_detection (f, img, matrix, color_adjust)
+     struct frame *f;
+     struct image *img;
+     Lisp_Object matrix, color_adjust;
+{
+  int i = 0;
+  int trans[9];
+
+  if (CONSP (matrix))
+    {
+      for (i = 0;
+	   i < 9 && CONSP (matrix) && NUMBERP (XCAR (matrix));
+	   ++i, matrix = XCDR (matrix))
+	trans[i] = XFLOATINT (XCAR (matrix));
+    }
+  else if (VECTORP (matrix) && ASIZE (matrix) >= 9)
+    {
+      for (i = 0; i < 9 && NUMBERP (AREF (matrix, i)); ++i)
+	trans[i] = XFLOATINT (AREF (matrix, i));
+    }
+
+  if (NILP (color_adjust))
+    color_adjust = make_number (0xffff / 2);
+
+  if (i == 9 && NUMBERP (color_adjust))
+    x_detect_edges (f, img, trans, (int) XFLOATINT (color_adjust));
+}
+
+
+/* Transform image IMG on frame F so that it looks disabled.  */
+
+static void
+x_disable_image (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+#ifdef HAVE_NTGUI
+  int n_planes = dpyinfo->n_planes * dpyinfo->n_cbits;
+#else
+  int n_planes = dpyinfo->n_planes;
+#endif /* HAVE_NTGUI */
+
+  if (n_planes >= 2)
+    {
+      /* Color (or grayscale).  Convert to gray, and equalize.  Just
+	 drawing such images with a stipple can look very odd, so
+	 we're using this method instead.  */
+      XColor *colors = x_to_xcolors (f, img, 1);
+      XColor *p, *end;
+      const int h = 15000;
+      const int l = 30000;
+
+      for (p = colors, end = colors + img->width * img->height;
+	   p < end;
+	   ++p)
+	{
+	  int i = COLOR_INTENSITY (p->red, p->green, p->blue);
+	  int i2 = (0xffff - h - l) * i / 0xffff + l;
+	  p->red = p->green = p->blue = i2;
+	}
+
+      x_from_xcolors (f, img, colors);
+    }
+
+  /* Draw a cross over the disabled image, if we must or if we
+     should.  */
+  if (n_planes < 2 || cross_disabled_images)
+    {
+#ifndef HAVE_NTGUI
+      Display *dpy = FRAME_X_DISPLAY (f);
+      GC gc;
+
+#ifdef MAC_OS
+#define XCreateGC_pixmap(dpy, pixmap) XCreateGC (dpy, NULL, 0, NULL)
+#define MaskForeground(f)  PIX_MASK_DRAW (f)
+#else
+#define XCreateGC_pixmap(dpy, pixmap) XCreateGC (dpy, pixmap, 0, NULL)
+#define MaskForeground(f)  WHITE_PIX_DEFAULT (f)
+#endif
+
+      gc = XCreateGC_pixmap (dpy, img->pixmap);
+      XSetForeground (dpy, gc, BLACK_PIX_DEFAULT (f));
+      XDrawLine (dpy, img->pixmap, gc, 0, 0,
+		 img->width - 1, img->height - 1);
+      XDrawLine (dpy, img->pixmap, gc, 0, img->height - 1,
+		 img->width - 1, 0);
+      XFreeGC (dpy, gc);
+
+      if (img->mask)
+	{
+	  gc = XCreateGC_pixmap (dpy, img->mask);
+	  XSetForeground (dpy, gc, MaskForeground (f));
+	  XDrawLine (dpy, img->mask, gc, 0, 0,
+		     img->width - 1, img->height - 1);
+	  XDrawLine (dpy, img->mask, gc, 0, img->height - 1,
+		     img->width - 1, 0);
+	  XFreeGC (dpy, gc);
+	}
+#else
+      HDC hdc, bmpdc;
+      HGDIOBJ prev;
+
+      hdc = get_frame_dc (f);
+      bmpdc = CreateCompatibleDC (hdc);
+      release_frame_dc (f, hdc);
+
+      prev = SelectObject (bmpdc, img->pixmap);
+
+      SetTextColor (bmpdc, BLACK_PIX_DEFAULT (f));
+      MoveToEx (bmpdc, 0, 0, NULL);
+      LineTo (bmpdc, img->width - 1, img->height - 1);
+      MoveToEx (bmpdc, 0, img->height - 1, NULL);
+      LineTo (bmpdc, img->width - 1, 0);
+
+      if (img->mask)
+	{
+	  SelectObject (bmpdc, img->mask);
+	  SetTextColor (bmpdc, WHITE_PIX_DEFAULT (f));
+	  MoveToEx (bmpdc, 0, 0, NULL);
+	  LineTo (bmpdc, img->width - 1, img->height - 1);
+	  MoveToEx (bmpdc, 0, img->height - 1, NULL);
+	  LineTo (bmpdc, img->width - 1, 0);
+	}
+      SelectObject (bmpdc, prev);
+      DeleteDC (bmpdc);
+#endif /* HAVE_NTGUI */
+    }
+}
+
+
+/* Build a mask for image IMG which is used on frame F.  FILE is the
+   name of an image file, for error messages.  HOW determines how to
+   determine the background color of IMG.  If it is a list '(R G B)',
+   with R, G, and B being integers >= 0, take that as the color of the
+   background.  Otherwise, determine the background color of IMG
+   heuristically.  Value is non-zero if successful. */
+
+static int
+x_build_heuristic_mask (f, img, how)
+     struct frame *f;
+     struct image *img;
+     Lisp_Object how;
+{
+  XImagePtr_or_DC ximg;
+#ifndef HAVE_NTGUI
+  XImagePtr mask_img;
+#else
+  HDC frame_dc;
+  HGDIOBJ prev;
+  char *mask_img;
+  int row_width;
+#endif /* HAVE_NTGUI */
+  int x, y, rc, use_img_background;
+  unsigned long bg = 0;
+
+  if (img->mask)
+    {
+      Free_Pixmap (FRAME_X_DISPLAY (f), img->mask);
+      img->mask = NO_PIXMAP;
+      img->background_transparent_valid = 0;
+    }
+
+#ifndef HAVE_NTGUI
+  /* Create an image and pixmap serving as mask.  */
+  rc = x_create_x_image_and_pixmap (f, img->width, img->height, 1,
+				    &mask_img, &img->mask);
+  if (!rc)
+    return 0;
+
+  /* Get the X image of IMG->pixmap.  */
+  ximg = XGetImage (FRAME_X_DISPLAY (f), img->pixmap, 0, 0,
+		    img->width, img->height,
+		    ~0, ZPixmap);
+#else
+  /* Create the bit array serving as mask.  */
+  row_width = (img->width + 7) / 8;
+  mask_img = xmalloc (row_width * img->height);
+  bzero (mask_img, row_width * img->height);
+
+  /* Create a memory device context for IMG->pixmap.  */
+  frame_dc = get_frame_dc (f);
+  ximg = CreateCompatibleDC (frame_dc);
+  release_frame_dc (f, frame_dc);
+  prev = SelectObject (ximg, img->pixmap);
+#endif /* HAVE_NTGUI */
+
+  /* Determine the background color of ximg.  If HOW is `(R G B)'
+     take that as color.  Otherwise, use the image's background color. */
+  use_img_background = 1;
+
+  if (CONSP (how))
+    {
+      int rgb[3], i;
+
+      for (i = 0; i < 3 && CONSP (how) && NATNUMP (XCAR (how)); ++i)
+	{
+	  rgb[i] = XFASTINT (XCAR (how)) & 0xffff;
+	  how = XCDR (how);
+	}
+
+      if (i == 3 && NILP (how))
+	{
+	  char color_name[30];
+	  sprintf (color_name, "#%04x%04x%04x", rgb[0], rgb[1], rgb[2]);
+	  bg = (
+#ifdef HAVE_NTGUI
+		0x00ffffff & /* Filter out palette info.  */
+#endif /* HAVE_NTGUI */
+		x_alloc_image_color (f, img, build_string (color_name), 0));
+	  use_img_background = 0;
+	}
+    }
+
+  if (use_img_background)
+    bg = four_corners_best (ximg, img->width, img->height);
+
+  /* Set all bits in mask_img to 1 whose color in ximg is different
+     from the background color bg.  */
+#ifndef HAVE_NTGUI
+  for (y = 0; y < img->height; ++y)
+    for (x = 0; x < img->width; ++x)
+      XPutPixel (mask_img, x, y, (XGetPixel (ximg, x, y) != bg
+				  ? PIX_MASK_DRAW (f) : PIX_MASK_RETAIN (f)));
+
+  /* Fill in the background_transparent field while we have the mask handy. */
+  image_background_transparent (img, f, mask_img);
+
+  /* Put mask_img into img->mask.  */
+  x_put_x_image (f, mask_img, img->mask, img->width, img->height);
+  x_destroy_x_image (mask_img);
+
+#else
+  for (y = 0; y < img->height; ++y)
+    for (x = 0; x < img->width; ++x)
+      {
+	COLORREF p = GetPixel (ximg, x, y);
+	if (p != bg)
+	  mask_img[y * row_width + x / 8] |= 1 << (x % 8);
+      }
+
+  /* Create the mask image.  */
+  img->mask = w32_create_pixmap_from_bitmap_data (img->width, img->height,
+						  mask_img);
+  /* Fill in the background_transparent field while we have the mask handy. */
+  SelectObject (ximg, img->mask);
+  image_background_transparent (img, f, ximg);
+
+  /* Was: x_destroy_x_image ((XImagePtr )mask_img); which seems bogus ++kfs */
+  xfree (mask_img);
+#endif /* HAVE_NTGUI */
+
+  Destroy_Image (ximg, prev);
+
+  return 1;
+}
+
+
+/***********************************************************************
+		       PBM (mono, gray, color)
+ ***********************************************************************/
+
+static int pbm_image_p P_ ((Lisp_Object object));
+static int pbm_load P_ ((struct frame *f, struct image *img));
+static int pbm_scan_number P_ ((unsigned char **, unsigned char *));
+
+/* The symbol `pbm' identifying images of this type.  */
+
+Lisp_Object Qpbm;
+
+/* Indices of image specification fields in gs_format, below.  */
+
+enum pbm_keyword_index
+{
+  PBM_TYPE,
+  PBM_FILE,
+  PBM_DATA,
+  PBM_ASCENT,
+  PBM_MARGIN,
+  PBM_RELIEF,
+  PBM_ALGORITHM,
+  PBM_HEURISTIC_MASK,
+  PBM_MASK,
+  PBM_FOREGROUND,
+  PBM_BACKGROUND,
+  PBM_LAST
+};
+
+/* Vector of image_keyword structures describing the format
+   of valid user-defined image specifications.  */
+
+static struct image_keyword pbm_format[PBM_LAST] =
+{
+  {":type",		IMAGE_SYMBOL_VALUE,			1},
+  {":file",		IMAGE_STRING_VALUE,			0},
+  {":data",		IMAGE_STRING_VALUE,			0},
+  {":ascent",		IMAGE_ASCENT_VALUE,			0},
+  {":margin",		IMAGE_POSITIVE_INTEGER_VALUE_OR_PAIR,	0},
+  {":relief",		IMAGE_INTEGER_VALUE,			0},
+  {":conversion",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":heuristic-mask",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":mask",		IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":foreground",	IMAGE_STRING_OR_NIL_VALUE,		0},
+  {":background",	IMAGE_STRING_OR_NIL_VALUE,		0}
+};
+
+/* Structure describing the image type `pbm'.  */
+
+static struct image_type pbm_type =
+{
+  &Qpbm,
+  pbm_image_p,
+  pbm_load,
+  x_clear_image,
+  NULL
+};
+
+
+/* Return non-zero if OBJECT is a valid PBM image specification.  */
+
+static int
+pbm_image_p (object)
+     Lisp_Object object;
+{
+  struct image_keyword fmt[PBM_LAST];
+
+  bcopy (pbm_format, fmt, sizeof fmt);
+
+  if (!parse_image_spec (object, fmt, PBM_LAST, Qpbm))
+    return 0;
+
+  /* Must specify either :data or :file.  */
+  return fmt[PBM_DATA].count + fmt[PBM_FILE].count == 1;
+}
+
+
+/* Scan a decimal number from *S and return it.  Advance *S while
+   reading the number.  END is the end of the string.  Value is -1 at
+   end of input.  */
+
+static int
+pbm_scan_number (s, end)
+     unsigned char **s, *end;
+{
+  int c = 0, val = -1;
+
+  while (*s < end)
+    {
+      /* Skip white-space.  */
+      while (*s < end && (c = *(*s)++, isspace (c)))
+	;
+
+      if (c == '#')
+	{
+	  /* Skip comment to end of line.  */
+	  while (*s < end && (c = *(*s)++, c != '\n'))
+	    ;
+	}
+      else if (isdigit (c))
+	{
+	  /* Read decimal number.  */
+	  val = c - '0';
+	  while (*s < end && (c = *(*s)++, isdigit (c)))
+	    val = 10 * val + c - '0';
+	  break;
+	}
+      else
+	break;
+    }
+
+  return val;
+}
+
+
+#ifdef HAVE_NTGUI
+#if 0  /* Unused. ++kfs  */
+
+/* Read FILE into memory.  Value is a pointer to a buffer allocated
+   with xmalloc holding FILE's contents.  Value is null if an error
+   occurred.  *SIZE is set to the size of the file.  */
+
+static char *
+pbm_read_file (file, size)
+     Lisp_Object file;
+     int *size;
+{
+  FILE *fp = NULL;
+  char *buf = NULL;
+  struct stat st;
+
+  if (stat (SDATA (file), &st) == 0
+      && (fp = fopen (SDATA (file), "rb")) != NULL
+      && (buf = (char *) xmalloc (st.st_size),
+	  fread (buf, 1, st.st_size, fp) == st.st_size))
+    {
+      *size = st.st_size;
+      fclose (fp);
+    }
+  else
+    {
+      if (fp)
+	fclose (fp);
+      if (buf)
+	{
+	  xfree (buf);
+	  buf = NULL;
+	}
+    }
+
+  return buf;
+}
+#endif
+#endif /* HAVE_NTGUI */
+
+/* Load PBM image IMG for use on frame F.  */
+
+static int
+pbm_load (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  int raw_p, x, y;
+  int width, height, max_color_idx = 0;
+  XImagePtr ximg;
+  Lisp_Object file, specified_file;
+  enum {PBM_MONO, PBM_GRAY, PBM_COLOR} type;
+  struct gcpro gcpro1;
+  unsigned char *contents = NULL;
+  unsigned char *end, *p;
+  int size;
+
+  specified_file = image_spec_value (img->spec, QCfile, NULL);
+  file = Qnil;
+  GCPRO1 (file);
+
+  if (STRINGP (specified_file))
+    {
+      file = x_find_image_file (specified_file);
+      if (!STRINGP (file))
+	{
+	  image_error ("Cannot find image file `%s'", specified_file, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+
+      contents = slurp_file (SDATA (file), &size);
+      if (contents == NULL)
+	{
+	  image_error ("Error reading `%s'", file, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+
+      p = contents;
+      end = contents + size;
+    }
+  else
+    {
+      Lisp_Object data;
+      data = image_spec_value (img->spec, QCdata, NULL);
+      p = SDATA (data);
+      end = p + SBYTES (data);
+    }
+
+  /* Check magic number.  */
+  if (end - p < 2 || *p++ != 'P')
+    {
+      image_error ("Not a PBM image: `%s'", img->spec, Qnil);
+    error:
+      xfree (contents);
+      UNGCPRO;
+      return 0;
+    }
+
+  switch (*p++)
+    {
+    case '1':
+      raw_p = 0, type = PBM_MONO;
+      break;
+
+    case '2':
+      raw_p = 0, type = PBM_GRAY;
+      break;
+
+    case '3':
+      raw_p = 0, type = PBM_COLOR;
+      break;
+
+    case '4':
+      raw_p = 1, type = PBM_MONO;
+      break;
+
+    case '5':
+      raw_p = 1, type = PBM_GRAY;
+      break;
+
+    case '6':
+      raw_p = 1, type = PBM_COLOR;
+      break;
+
+    default:
+      image_error ("Not a PBM image: `%s'", img->spec, Qnil);
+      goto error;
+    }
+
+  /* Read width, height, maximum color-component.  Characters
+     starting with `#' up to the end of a line are ignored.  */
+  width = pbm_scan_number (&p, end);
+  height = pbm_scan_number (&p, end);
+
+  if (type != PBM_MONO)
+    {
+      max_color_idx = pbm_scan_number (&p, end);
+      if (raw_p && max_color_idx > 255)
+	max_color_idx = 255;
+    }
+
+  if (width < 0
+      || height < 0
+      || (type != PBM_MONO && max_color_idx < 0))
+    goto error;
+
+  if (!x_create_x_image_and_pixmap (f, width, height, 0,
+				    &ximg, &img->pixmap))
+    goto error;
+
+  /* Initialize the color hash table.  */
+  init_color_table ();
+
+  if (type == PBM_MONO)
+    {
+      int c = 0, g;
+      struct image_keyword fmt[PBM_LAST];
+      unsigned long fg = FRAME_FOREGROUND_PIXEL (f);
+      unsigned long bg = FRAME_BACKGROUND_PIXEL (f);
+
+      /* Parse the image specification.  */
+      bcopy (pbm_format, fmt, sizeof fmt);
+      parse_image_spec (img->spec, fmt, PBM_LAST, Qpbm);
+
+      /* Get foreground and background colors, maybe allocate colors.  */
+      if (fmt[PBM_FOREGROUND].count
+	  && STRINGP (fmt[PBM_FOREGROUND].value))
+	fg = x_alloc_image_color (f, img, fmt[PBM_FOREGROUND].value, fg);
+      if (fmt[PBM_BACKGROUND].count
+	  && STRINGP (fmt[PBM_BACKGROUND].value))
+	{
+	  bg = x_alloc_image_color (f, img, fmt[PBM_BACKGROUND].value, bg);
+	  img->background = bg;
+	  img->background_valid = 1;
+	}
+
+      for (y = 0; y < height; ++y)
+	for (x = 0; x < width; ++x)
+	  {
+	    if (raw_p)
+	      {
+		if ((x & 7) == 0)
+		  c = *p++;
+		g = c & 0x80;
+		c <<= 1;
+	      }
+	    else
+	      g = pbm_scan_number (&p, end);
+
+	    XPutPixel (ximg, x, y, g ? fg : bg);
+	  }
+    }
+  else
+    {
+      for (y = 0; y < height; ++y)
+	for (x = 0; x < width; ++x)
+	  {
+	    int r, g, b;
+
+	    if (type == PBM_GRAY)
+	      r = g = b = raw_p ? *p++ : pbm_scan_number (&p, end);
+	    else if (raw_p)
+	      {
+		r = *p++;
+		g = *p++;
+		b = *p++;
+	      }
+	    else
+	      {
+		r = pbm_scan_number (&p, end);
+		g = pbm_scan_number (&p, end);
+		b = pbm_scan_number (&p, end);
+	      }
+
+	    if (r < 0 || g < 0 || b < 0)
+	      {
+		x_destroy_x_image (ximg);
+		image_error ("Invalid pixel value in image `%s'",
+			     img->spec, Qnil);
+		goto error;
+	      }
+
+	    /* RGB values are now in the range 0..max_color_idx.
+	       Scale this to the range 0..0xffff supported by X.  */
+	    r = (double) r * 65535 / max_color_idx;
+	    g = (double) g * 65535 / max_color_idx;
+	    b = (double) b * 65535 / max_color_idx;
+	    XPutPixel (ximg, x, y, lookup_rgb_color (f, r, g, b));
+	  }
+    }
+
+#ifdef COLOR_TABLE_SUPPORT
+  /* Store in IMG->colors the colors allocated for the image, and
+     free the color table.  */
+  img->colors = colors_in_color_table (&img->ncolors);
+  free_color_table ();
+#endif /* COLOR_TABLE_SUPPORT */
+
+  img->width = width;
+  img->height = height;
+
+  /* Maybe fill in the background field while we have ximg handy.  */
+
+  if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
+    IMAGE_BACKGROUND (img, f, ximg);
+
+  /* Put the image into a pixmap.  */
+  x_put_x_image (f, ximg, img->pixmap, width, height);
+  x_destroy_x_image (ximg);
+
+  /* X and W32 versions did it here, MAC version above.  ++kfs
+     img->width = width;   
+     img->height = height; */
+
+  UNGCPRO;
+  xfree (contents);
+  return 1;
+}
+
+
+/***********************************************************************
+				 PNG
+ ***********************************************************************/
+
+#if defined (HAVE_PNG) || defined (MAC_OS)
+
+/* Function prototypes.  */
+
+static int png_image_p P_ ((Lisp_Object object));
+static int png_load P_ ((struct frame *f, struct image *img));
+
+/* The symbol `png' identifying images of this type.  */
+
+Lisp_Object Qpng;
+
+/* Indices of image specification fields in png_format, below.  */
+
+enum png_keyword_index
+{
+  PNG_TYPE,
+  PNG_DATA,
+  PNG_FILE,
+  PNG_ASCENT,
+  PNG_MARGIN,
+  PNG_RELIEF,
+  PNG_ALGORITHM,
+  PNG_HEURISTIC_MASK,
+  PNG_MASK,
+  PNG_BACKGROUND,
+  PNG_LAST
+};
+
+/* Vector of image_keyword structures describing the format
+   of valid user-defined image specifications.  */
+
+static struct image_keyword png_format[PNG_LAST] =
+{
+  {":type",		IMAGE_SYMBOL_VALUE,			1},
+  {":data",		IMAGE_STRING_VALUE,			0},
+  {":file",		IMAGE_STRING_VALUE,			0},
+  {":ascent",		IMAGE_ASCENT_VALUE,			0},
+  {":margin",		IMAGE_POSITIVE_INTEGER_VALUE_OR_PAIR,	0},
+  {":relief",		IMAGE_INTEGER_VALUE,			0},
+  {":conversion",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":heuristic-mask",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":mask",		IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":background",	IMAGE_STRING_OR_NIL_VALUE,		0}
+};
+
+/* Structure describing the image type `png'.  */
+
+static struct image_type png_type =
+{
+  &Qpng,
+  png_image_p,
+  png_load,
+  x_clear_image,
+  NULL
+};
+
+/* Return non-zero if OBJECT is a valid PNG image specification.  */
+
+static int
+png_image_p (object)
+     Lisp_Object object;
+{
+  struct image_keyword fmt[PNG_LAST];
+  bcopy (png_format, fmt, sizeof fmt);
+
+  if (!parse_image_spec (object, fmt, PNG_LAST, Qpng))
+    return 0;
+
+  /* Must specify either the :data or :file keyword.  */
+  return fmt[PNG_FILE].count + fmt[PNG_DATA].count == 1;
+}
+
+#endif /* HAVE_PNG || MAC_OS */
+
+
+#ifdef HAVE_PNG
+
+#if defined HAVE_LIBPNG_PNG_H
+# include <libpng/png.h>
+#else
+# include <png.h>
+#endif
+
+#ifdef HAVE_NTGUI
+/* PNG library details.  */
+
+DEF_IMGLIB_FN (png_get_io_ptr);
+DEF_IMGLIB_FN (png_check_sig);
+DEF_IMGLIB_FN (png_create_read_struct);
+DEF_IMGLIB_FN (png_create_info_struct);
+DEF_IMGLIB_FN (png_destroy_read_struct);
+DEF_IMGLIB_FN (png_set_read_fn);
+DEF_IMGLIB_FN (png_init_io);
+DEF_IMGLIB_FN (png_set_sig_bytes);
+DEF_IMGLIB_FN (png_read_info);
+DEF_IMGLIB_FN (png_get_IHDR);
+DEF_IMGLIB_FN (png_get_valid);
+DEF_IMGLIB_FN (png_set_strip_16);
+DEF_IMGLIB_FN (png_set_expand);
+DEF_IMGLIB_FN (png_set_gray_to_rgb);
+DEF_IMGLIB_FN (png_set_background);
+DEF_IMGLIB_FN (png_get_bKGD);
+DEF_IMGLIB_FN (png_read_update_info);
+DEF_IMGLIB_FN (png_get_channels);
+DEF_IMGLIB_FN (png_get_rowbytes);
+DEF_IMGLIB_FN (png_read_image);
+DEF_IMGLIB_FN (png_read_end);
+DEF_IMGLIB_FN (png_error);
+
+static int
+init_png_functions (void)
+{
+  HMODULE library;
+
+  /* Ensure zlib is loaded.  Try debug version first.  */
+  if (!LoadLibrary ("zlibd.dll")
+      && !LoadLibrary ("zlib.dll"))
+    return 0;
+
+  /* Try loading libpng under probable names.  */
+  if (!(library = LoadLibrary ("libpng13d.dll"))
+      && !(library = LoadLibrary ("libpng13.dll"))
+      && !(library = LoadLibrary ("libpng12d.dll"))
+      && !(library = LoadLibrary ("libpng12.dll"))
+      && !(library = LoadLibrary ("libpng.dll")))
+    return 0;
+
+  LOAD_IMGLIB_FN (library, png_get_io_ptr);
+  LOAD_IMGLIB_FN (library, png_check_sig);
+  LOAD_IMGLIB_FN (library, png_create_read_struct);
+  LOAD_IMGLIB_FN (library, png_create_info_struct);
+  LOAD_IMGLIB_FN (library, png_destroy_read_struct);
+  LOAD_IMGLIB_FN (library, png_set_read_fn);
+  LOAD_IMGLIB_FN (library, png_init_io);
+  LOAD_IMGLIB_FN (library, png_set_sig_bytes);
+  LOAD_IMGLIB_FN (library, png_read_info);
+  LOAD_IMGLIB_FN (library, png_get_IHDR);
+  LOAD_IMGLIB_FN (library, png_get_valid);
+  LOAD_IMGLIB_FN (library, png_set_strip_16);
+  LOAD_IMGLIB_FN (library, png_set_expand);
+  LOAD_IMGLIB_FN (library, png_set_gray_to_rgb);
+  LOAD_IMGLIB_FN (library, png_set_background);
+  LOAD_IMGLIB_FN (library, png_get_bKGD);
+  LOAD_IMGLIB_FN (library, png_read_update_info);
+  LOAD_IMGLIB_FN (library, png_get_channels);
+  LOAD_IMGLIB_FN (library, png_get_rowbytes);
+  LOAD_IMGLIB_FN (library, png_read_image);
+  LOAD_IMGLIB_FN (library, png_read_end);
+  LOAD_IMGLIB_FN (library, png_error);
+  return 1;
+}
+#else
+
+#define fn_png_get_io_ptr		png_get_io_ptr
+#define fn_png_check_sig		png_check_sig
+#define fn_png_create_read_struct	png_create_read_struct
+#define fn_png_create_info_struct	png_create_info_struct
+#define fn_png_destroy_read_struct	png_destroy_read_struct
+#define fn_png_set_read_fn		png_set_read_fn
+#define fn_png_init_io			png_init_io
+#define fn_png_set_sig_bytes		png_set_sig_bytes
+#define fn_png_read_info		png_read_info
+#define fn_png_get_IHDR			png_get_IHDR
+#define fn_png_get_valid		png_get_valid
+#define fn_png_set_strip_16		png_set_strip_16
+#define fn_png_set_expand		png_set_expand
+#define fn_png_set_gray_to_rgb		png_set_gray_to_rgb
+#define fn_png_set_background		png_set_background
+#define fn_png_get_bKGD			png_get_bKGD
+#define fn_png_read_update_info		png_read_update_info
+#define fn_png_get_channels		png_get_channels
+#define fn_png_get_rowbytes		png_get_rowbytes
+#define fn_png_read_image		png_read_image
+#define fn_png_read_end			png_read_end
+#define fn_png_error			png_error
+
+#endif /* HAVE_NTGUI */
+
+/* Error and warning handlers installed when the PNG library
+   is initialized.  */
+
+static void
+my_png_error (png_ptr, msg)
+     png_struct *png_ptr;
+     char *msg;
+{
+  xassert (png_ptr != NULL);
+  image_error ("PNG error: %s", build_string (msg), Qnil);
+  longjmp (png_ptr->jmpbuf, 1);
+}
+
+
+static void
+my_png_warning (png_ptr, msg)
+     png_struct *png_ptr;
+     char *msg;
+{
+  xassert (png_ptr != NULL);
+  image_error ("PNG warning: %s", build_string (msg), Qnil);
+}
+
+/* Memory source for PNG decoding.  */
+
+struct png_memory_storage
+{
+  unsigned char *bytes;		/* The data       */
+  size_t len;			/* How big is it? */
+  int index;			/* Where are we?  */
+};
+
+
+/* Function set as reader function when reading PNG image from memory.
+   PNG_PTR is a pointer to the PNG control structure.  Copy LENGTH
+   bytes from the input to DATA.  */
+
+static void
+png_read_from_memory (png_ptr, data, length)
+     png_structp png_ptr;
+     png_bytep data;
+     png_size_t length;
+{
+  struct png_memory_storage *tbr
+    = (struct png_memory_storage *) fn_png_get_io_ptr (png_ptr);
+
+  if (length > tbr->len - tbr->index)
+    fn_png_error (png_ptr, "Read error");
+
+  bcopy (tbr->bytes + tbr->index, data, length);
+  tbr->index = tbr->index + length;
+}
+
+/* Load PNG image IMG for use on frame F.  Value is non-zero if
+   successful.  */
+
+static int
+png_load (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  Lisp_Object file, specified_file;
+  Lisp_Object specified_data;
+  int x, y, i;
+  XImagePtr ximg, mask_img = NULL;
+  struct gcpro gcpro1;
+  png_struct *png_ptr = NULL;
+  png_info *info_ptr = NULL, *end_info = NULL;
+  FILE *volatile fp = NULL;
+  png_byte sig[8];
+  png_byte * volatile pixels = NULL;
+  png_byte ** volatile rows = NULL;
+  png_uint_32 width, height;
+  int bit_depth, color_type, interlace_type;
+  png_byte channels;
+  png_uint_32 row_bytes;
+  int transparent_p;
+  double screen_gamma;
+  struct png_memory_storage tbr;  /* Data to be read */
+
+  /* Find out what file to load.  */
+  specified_file = image_spec_value (img->spec, QCfile, NULL);
+  specified_data = image_spec_value (img->spec, QCdata, NULL);
+  file = Qnil;
+  GCPRO1 (file);
+
+  if (NILP (specified_data))
+    {
+      file = x_find_image_file (specified_file);
+      if (!STRINGP (file))
+	{
+	  image_error ("Cannot find image file `%s'", specified_file, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+
+      /* Open the image file.  */
+      fp = fopen (SDATA (file), "rb");
+      if (!fp)
+	{
+	  image_error ("Cannot open image file `%s'", file, Qnil);
+	  UNGCPRO;
+	  fclose (fp);
+	  return 0;
+	}
+
+      /* Check PNG signature.  */
+      if (fread (sig, 1, sizeof sig, fp) != sizeof sig
+	  || !fn_png_check_sig (sig, sizeof sig))
+	{
+	  image_error ("Not a PNG file: `%s'", file, Qnil);
+	  UNGCPRO;
+	  fclose (fp);
+	  return 0;
+	}
+    }
+  else
+    {
+      /* Read from memory.  */
+      tbr.bytes = SDATA (specified_data);
+      tbr.len = SBYTES (specified_data);
+      tbr.index = 0;
+
+      /* Check PNG signature.  */
+      if (tbr.len < sizeof sig
+	  || !fn_png_check_sig (tbr.bytes, sizeof sig))
+	{
+	  image_error ("Not a PNG image: `%s'", img->spec, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+
+      /* Need to skip past the signature.  */
+      tbr.bytes += sizeof (sig);
+    }
+
+  /* Initialize read and info structs for PNG lib.  */
+  png_ptr = fn_png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL,
+				       my_png_error, my_png_warning);
+  if (!png_ptr)
+    {
+      if (fp) fclose (fp);
+      UNGCPRO;
+      return 0;
+    }
+
+  info_ptr = fn_png_create_info_struct (png_ptr);
+  if (!info_ptr)
+    {
+      fn_png_destroy_read_struct (&png_ptr, NULL, NULL);
+      if (fp) fclose (fp);
+      UNGCPRO;
+      return 0;
+    }
+
+  end_info = fn_png_create_info_struct (png_ptr);
+  if (!end_info)
+    {
+      fn_png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
+      if (fp) fclose (fp);
+      UNGCPRO;
+      return 0;
+    }
+
+  /* Set error jump-back.  We come back here when the PNG library
+     detects an error.  */
+  if (setjmp (png_ptr->jmpbuf))
+    {
+    error:
+      if (png_ptr)
+        fn_png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
+      xfree (pixels);
+      xfree (rows);
+      if (fp) fclose (fp);
+      UNGCPRO;
+      return 0;
+    }
+
+  /* Read image info.  */
+  if (!NILP (specified_data))
+    fn_png_set_read_fn (png_ptr, (void *) &tbr, png_read_from_memory);
+  else
+    fn_png_init_io (png_ptr, fp);
+
+  fn_png_set_sig_bytes (png_ptr, sizeof sig);
+  fn_png_read_info (png_ptr, info_ptr);
+  fn_png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
+		   &interlace_type, NULL, NULL);
+
+  /* If image contains simply transparency data, we prefer to
+     construct a clipping mask.  */
+  if (fn_png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
+    transparent_p = 1;
+  else
+    transparent_p = 0;
+
+  /* This function is easier to write if we only have to handle
+     one data format: RGB or RGBA with 8 bits per channel.  Let's
+     transform other formats into that format.  */
+
+  /* Strip more than 8 bits per channel.  */
+  if (bit_depth == 16)
+    fn_png_set_strip_16 (png_ptr);
+
+  /* Expand data to 24 bit RGB, or 8 bit grayscale, with alpha channel
+     if available.  */
+  fn_png_set_expand (png_ptr);
+
+  /* Convert grayscale images to RGB.  */
+  if (color_type == PNG_COLOR_TYPE_GRAY
+      || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+    fn_png_set_gray_to_rgb (png_ptr);
+
+  screen_gamma = (f->gamma ? 1 / f->gamma / 0.45455 : 2.2);
+
+#if 0 /* Avoid double gamma correction for PNG images. */
+  { /* Tell the PNG lib to handle gamma correction for us.  */
+    int intent;
+    double image_gamma;
+#if defined(PNG_READ_sRGB_SUPPORTED) || defined(PNG_WRITE_sRGB_SUPPORTED)
+    if (png_get_sRGB (png_ptr, info_ptr, &intent))
+      /* The libpng documentation says this is right in this case.  */
+      png_set_gamma (png_ptr, screen_gamma, 0.45455);
+    else
+#endif
+      if (png_get_gAMA (png_ptr, info_ptr, &image_gamma))
+	/* Image contains gamma information.  */
+	png_set_gamma (png_ptr, screen_gamma, image_gamma);
+      else
+	/* Use the standard default for the image gamma.  */
+	png_set_gamma (png_ptr, screen_gamma, 0.45455);
+  }
+#endif /* if 0 */
+
+  /* Handle alpha channel by combining the image with a background
+     color.  Do this only if a real alpha channel is supplied.  For
+     simple transparency, we prefer a clipping mask.  */
+  if (!transparent_p)
+    {
+      png_color_16 *image_bg;
+      Lisp_Object specified_bg
+	= image_spec_value (img->spec, QCbackground, NULL);
+
+      if (STRINGP (specified_bg))
+	/* The user specified `:background', use that.  */
+	{
+	  /* W32 version incorrectly used COLORREF here!!  ++kfs */
+	  XColor color;
+	  if (x_defined_color (f, SDATA (specified_bg), &color, 0))
+	    {
+	      png_color_16 user_bg;
+
+	      bzero (&user_bg, sizeof user_bg);
+	      user_bg.red = color.red >> PNG_BG_COLOR_SHIFT;
+	      user_bg.green = color.green >> PNG_BG_COLOR_SHIFT;
+	      user_bg.blue = color.blue >> PNG_BG_COLOR_SHIFT;
+
+	      fn_png_set_background (png_ptr, &user_bg,
+				     PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
+	    }
+	}
+      else if (fn_png_get_bKGD (png_ptr, info_ptr, &image_bg))
+	/* Image contains a background color with which to
+	   combine the image.  */
+	fn_png_set_background (png_ptr, image_bg,
+			       PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
+      else
+	{
+	  /* Image does not contain a background color with which
+	     to combine the image data via an alpha channel.  Use
+	     the frame's background instead.  */
+#ifdef HAVE_X_WINDOWS
+	  XColor color;
+	  png_color_16 frame_background;
+
+	  color.pixel = FRAME_BACKGROUND_PIXEL (f);
+	  x_query_color (f, &color);
+
+	  bzero (&frame_background, sizeof frame_background);
+	  frame_background.red = color.red;
+	  frame_background.green = color.green;
+	  frame_background.blue = color.blue;
+#endif /* HAVE_X_WINDOWS */
+
+#ifdef HAVE_NTGUI
+	  COLORREF color;
+	  png_color_16 frame_background;
+	  color = FRAME_BACKGROUND_PIXEL (f);
+#if 0 /* W32 TODO : Colormap support.  */
+	  x_query_color (f, &color);
+#endif
+	  bzero (&frame_background, sizeof frame_background);
+	  frame_background.red = 256 * GetRValue (color);
+	  frame_background.green = 256 * GetGValue (color);
+	  frame_background.blue = 256 * GetBValue (color);
+#endif /* HAVE_NTGUI */
+
+#ifdef MAC_OS
+	  unsigned long color;
+	  png_color_16 frame_background;
+	  color = FRAME_BACKGROUND_PIXEL (f);
+#if 0 /* MAC/W32 TODO : Colormap support.  */
+	  x_query_color (f, &color);
+#endif
+	  bzero (&frame_background, sizeof frame_background);
+	  frame_background.red = RED_FROM_ULONG (color);
+	  frame_background.green = GREEN_FROM_ULONG (color);
+	  frame_background.blue = BLUE_FROM_ULONG (color);
+#endif /* MAC_OS */
+
+	  fn_png_set_background (png_ptr, &frame_background,
+				 PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
+	}
+    }
+
+  /* Update info structure.  */
+  fn_png_read_update_info (png_ptr, info_ptr);
+
+  /* Get number of channels.  Valid values are 1 for grayscale images
+     and images with a palette, 2 for grayscale images with transparency
+     information (alpha channel), 3 for RGB images, and 4 for RGB
+     images with alpha channel, i.e. RGBA.  If conversions above were
+     sufficient we should only have 3 or 4 channels here.  */
+  channels = fn_png_get_channels (png_ptr, info_ptr);
+  xassert (channels == 3 || channels == 4);
+
+  /* Number of bytes needed for one row of the image.  */
+  row_bytes = fn_png_get_rowbytes (png_ptr, info_ptr);
+
+  /* Allocate memory for the image.  */
+  pixels = (png_byte *) xmalloc (row_bytes * height * sizeof *pixels);
+  rows = (png_byte **) xmalloc (height * sizeof *rows);
+  for (i = 0; i < height; ++i)
+    rows[i] = pixels + i * row_bytes;
+
+  /* Read the entire image.  */
+  fn_png_read_image (png_ptr, rows);
+  fn_png_read_end (png_ptr, info_ptr);
+  if (fp)
+    {
+      fclose (fp);
+      fp = NULL;
+    }
+
+  /* Create the X image and pixmap.  */
+  if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg,
+				    &img->pixmap))
+    goto error;
+
+  /* Create an image and pixmap serving as mask if the PNG image
+     contains an alpha channel.  */
+  if (channels == 4
+      && !transparent_p
+      && !x_create_x_image_and_pixmap (f, width, height, 1,
+				       &mask_img, &img->mask))
+    {
+      x_destroy_x_image (ximg);
+      Free_Pixmap (FRAME_X_DISPLAY (f), img->pixmap);
+      img->pixmap = NO_PIXMAP;
+      goto error;
+    }
+
+  /* Fill the X image and mask from PNG data.  */
+  init_color_table ();
+
+  for (y = 0; y < height; ++y)
+    {
+      png_byte *p = rows[y];
+
+      for (x = 0; x < width; ++x)
+	{
+	  unsigned r, g, b;
+
+	  r = *p++ << 8;
+	  g = *p++ << 8;
+	  b = *p++ << 8;
+	  XPutPixel (ximg, x, y, lookup_rgb_color (f, r, g, b));
+	  /* An alpha channel, aka mask channel, associates variable
+	     transparency with an image.  Where other image formats
+	     support binary transparency---fully transparent or fully
+	     opaque---PNG allows up to 254 levels of partial transparency.
+	     The PNG library implements partial transparency by combining
+	     the image with a specified background color.
+
+	     I'm not sure how to handle this here nicely: because the
+	     background on which the image is displayed may change, for
+	     real alpha channel support, it would be necessary to create
+	     a new image for each possible background.
+
+	     What I'm doing now is that a mask is created if we have
+	     boolean transparency information.  Otherwise I'm using
+	     the frame's background color to combine the image with.  */
+
+	  if (channels == 4)
+	    {
+	      if (mask_img)
+		XPutPixel (mask_img, x, y, *p > 0 ? PIX_MASK_DRAW (f) : PIX_MASK_RETAIN (f));
+	      ++p;
+	    }
+	}
+    }
+
+  if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
+    /* Set IMG's background color from the PNG image, unless the user
+       overrode it.  */
+    {
+      png_color_16 *bg;
+      if (fn_png_get_bKGD (png_ptr, info_ptr, &bg))
+	{
+	  img->background = lookup_rgb_color (f, bg->red, bg->green, bg->blue);
+	  img->background_valid = 1;
+	}
+    }
+
+#ifdef COLOR_TABLE_SUPPORT
+  /* Remember colors allocated for this image.  */
+  img->colors = colors_in_color_table (&img->ncolors);
+  free_color_table ();
+#endif /* COLOR_TABLE_SUPPORT */
+
+  /* Clean up.  */
+  fn_png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
+  xfree (rows);
+  xfree (pixels);
+
+  img->width = width;
+  img->height = height;
+
+  /* Maybe fill in the background field while we have ximg handy. */
+  IMAGE_BACKGROUND (img, f, ximg);
+
+  /* Put the image into the pixmap, then free the X image and its buffer.  */
+  x_put_x_image (f, ximg, img->pixmap, width, height);
+  x_destroy_x_image (ximg);
+
+  /* Same for the mask.  */
+  if (mask_img)
+    {
+      /* Fill in the background_transparent field while we have the mask
+	 handy. */
+      image_background_transparent (img, f, mask_img);
+
+      x_put_x_image (f, mask_img, img->mask, img->width, img->height);
+      x_destroy_x_image (mask_img);
+    }
+
+  UNGCPRO;
+  return 1;
+}
+
+#else /* HAVE_PNG */
+
+#ifdef MAC_OS
+static int
+png_load (f, img)
+     struct frame *f;
+     struct image *img;
+{
+#ifdef MAC_OSX
+  if (MyCGImageCreateWithPNGDataProvider)
+    return image_load_quartz2d (f, img, 1);
+  else
+#endif
+    return image_load_quicktime (f, img, kQTFileTypePNG);
+}
+#endif  /* MAC_OS */
+
+#endif /* !HAVE_PNG */
+
+
+
+/***********************************************************************
+				 JPEG
+ ***********************************************************************/
+
+#if defined (HAVE_JPEG) || defined (MAC_OS)
+
+static int jpeg_image_p P_ ((Lisp_Object object));
+static int jpeg_load P_ ((struct frame *f, struct image *img));
+
+/* The symbol `jpeg' identifying images of this type.  */
+
+Lisp_Object Qjpeg;
+
+/* Indices of image specification fields in gs_format, below.  */
+
+enum jpeg_keyword_index
+{
+  JPEG_TYPE,
+  JPEG_DATA,
+  JPEG_FILE,
+  JPEG_ASCENT,
+  JPEG_MARGIN,
+  JPEG_RELIEF,
+  JPEG_ALGORITHM,
+  JPEG_HEURISTIC_MASK,
+  JPEG_MASK,
+  JPEG_BACKGROUND,
+  JPEG_LAST
+};
+
+/* Vector of image_keyword structures describing the format
+   of valid user-defined image specifications.  */
+
+static struct image_keyword jpeg_format[JPEG_LAST] =
+{
+  {":type",		IMAGE_SYMBOL_VALUE,			1},
+  {":data",		IMAGE_STRING_VALUE,			0},
+  {":file",		IMAGE_STRING_VALUE,			0},
+  {":ascent",		IMAGE_ASCENT_VALUE,			0},
+  {":margin",		IMAGE_POSITIVE_INTEGER_VALUE_OR_PAIR,	0},
+  {":relief",		IMAGE_INTEGER_VALUE,			0},
+  {":conversions",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":heuristic-mask",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":mask",		IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":background",	IMAGE_STRING_OR_NIL_VALUE,		0}
+};
+
+/* Structure describing the image type `jpeg'.  */
+
+static struct image_type jpeg_type =
+{
+  &Qjpeg,
+  jpeg_image_p,
+  jpeg_load,
+  x_clear_image,
+  NULL
+};
+
+/* Return non-zero if OBJECT is a valid JPEG image specification.  */
+
+static int
+jpeg_image_p (object)
+     Lisp_Object object;
+{
+  struct image_keyword fmt[JPEG_LAST];
+
+  bcopy (jpeg_format, fmt, sizeof fmt);
+
+  if (!parse_image_spec (object, fmt, JPEG_LAST, Qjpeg))
+    return 0;
+
+  /* Must specify either the :data or :file keyword.  */
+  return fmt[JPEG_FILE].count + fmt[JPEG_DATA].count == 1;
+}
+
+#endif /* HAVE_JPEG || MAC_OS */
+
+#ifdef HAVE_JPEG
+
+/* Work around a warning about HAVE_STDLIB_H being redefined in
+   jconfig.h.  */
+#ifdef HAVE_STDLIB_H
+#define HAVE_STDLIB_H_1
+#undef HAVE_STDLIB_H
+#endif /* HAVE_STLIB_H */
+
+#include <jpeglib.h>
+#include <jerror.h>
+#include <setjmp.h>
+
+#ifdef HAVE_STLIB_H_1
+#define HAVE_STDLIB_H 1
+#endif
+
+#ifdef HAVE_NTGUI
+
+/* JPEG library details.  */
+DEF_IMGLIB_FN (jpeg_CreateDecompress);
+DEF_IMGLIB_FN (jpeg_start_decompress);
+DEF_IMGLIB_FN (jpeg_finish_decompress);
+DEF_IMGLIB_FN (jpeg_destroy_decompress);
+DEF_IMGLIB_FN (jpeg_read_header);
+DEF_IMGLIB_FN (jpeg_read_scanlines);
+DEF_IMGLIB_FN (jpeg_stdio_src);
+DEF_IMGLIB_FN (jpeg_std_error);
+DEF_IMGLIB_FN (jpeg_resync_to_restart);
+
+static int
+init_jpeg_functions (void)
+{
+  HMODULE library;
+
+  if (!(library = LoadLibrary ("libjpeg.dll"))
+      && !(library = LoadLibrary ("jpeg-62.dll"))
+      && !(library = LoadLibrary ("jpeg.dll")))
+    return 0;
+
+  LOAD_IMGLIB_FN (library, jpeg_finish_decompress);
+  LOAD_IMGLIB_FN (library, jpeg_read_scanlines);
+  LOAD_IMGLIB_FN (library, jpeg_start_decompress);
+  LOAD_IMGLIB_FN (library, jpeg_read_header);
+  LOAD_IMGLIB_FN (library, jpeg_stdio_src);
+  LOAD_IMGLIB_FN (library, jpeg_CreateDecompress);
+  LOAD_IMGLIB_FN (library, jpeg_destroy_decompress);
+  LOAD_IMGLIB_FN (library, jpeg_std_error);
+  LOAD_IMGLIB_FN (library, jpeg_resync_to_restart);
+  return 1;
+}
+
+/* Wrapper since we can't directly assign the function pointer
+   to another function pointer that was declared more completely easily.  */
+static boolean
+jpeg_resync_to_restart_wrapper(cinfo, desired)
+     j_decompress_ptr cinfo;
+     int desired;
+{
+  return fn_jpeg_resync_to_restart (cinfo, desired);
+}
+
+#else
+
+#define fn_jpeg_CreateDecompress(a,b,c)	jpeg_create_decompress(a)
+#define fn_jpeg_start_decompress	jpeg_start_decompress
+#define fn_jpeg_finish_decompress	jpeg_finish_decompress
+#define fn_jpeg_destroy_decompress	jpeg_destroy_decompress
+#define fn_jpeg_read_header		jpeg_read_header
+#define fn_jpeg_read_scanlines		jpeg_read_scanlines
+#define fn_jpeg_stdio_src		jpeg_stdio_src
+#define fn_jpeg_std_error		jpeg_std_error
+#define jpeg_resync_to_restart_wrapper	jpeg_resync_to_restart
+
+#endif /* HAVE_NTGUI */
+
+struct my_jpeg_error_mgr
+{
+  struct jpeg_error_mgr pub;
+  jmp_buf setjmp_buffer;
+};
+
+
+static void
+my_error_exit (cinfo)
+     j_common_ptr cinfo;
+{
+  struct my_jpeg_error_mgr *mgr = (struct my_jpeg_error_mgr *) cinfo->err;
+  longjmp (mgr->setjmp_buffer, 1);
+}
+
+
+/* Init source method for JPEG data source manager.  Called by
+   jpeg_read_header() before any data is actually read.  See
+   libjpeg.doc from the JPEG lib distribution.  */
+
+static void
+our_init_source (cinfo)
+     j_decompress_ptr cinfo;
+{
+}
+
+
+/* Fill input buffer method for JPEG data source manager.  Called
+   whenever more data is needed.  We read the whole image in one step,
+   so this only adds a fake end of input marker at the end.  */
+
+static boolean
+our_fill_input_buffer (cinfo)
+     j_decompress_ptr cinfo;
+{
+  /* Insert a fake EOI marker.  */
+  struct jpeg_source_mgr *src = cinfo->src;
+  static JOCTET buffer[2];
+
+  buffer[0] = (JOCTET) 0xFF;
+  buffer[1] = (JOCTET) JPEG_EOI;
+
+  src->next_input_byte = buffer;
+  src->bytes_in_buffer = 2;
+  return TRUE;
+}
+
+
+/* Method to skip over NUM_BYTES bytes in the image data.  CINFO->src
+   is the JPEG data source manager.  */
+
+static void
+our_skip_input_data (cinfo, num_bytes)
+     j_decompress_ptr cinfo;
+     long num_bytes;
+{
+  struct jpeg_source_mgr *src = (struct jpeg_source_mgr *) cinfo->src;
+
+  if (src)
+    {
+      if (num_bytes > src->bytes_in_buffer)
+	ERREXIT (cinfo, JERR_INPUT_EOF);
+
+      src->bytes_in_buffer -= num_bytes;
+      src->next_input_byte += num_bytes;
+    }
+}
+
+
+/* Method to terminate data source.  Called by
+   jpeg_finish_decompress() after all data has been processed.  */
+
+static void
+our_term_source (cinfo)
+     j_decompress_ptr cinfo;
+{
+}
+
+
+/* Set up the JPEG lib for reading an image from DATA which contains
+   LEN bytes.  CINFO is the decompression info structure created for
+   reading the image.  */
+
+static void
+jpeg_memory_src (cinfo, data, len)
+     j_decompress_ptr cinfo;
+     JOCTET *data;
+     unsigned int len;
+{
+  struct jpeg_source_mgr *src;
+
+  if (cinfo->src == NULL)
+    {
+      /* First time for this JPEG object?  */
+      cinfo->src = (struct jpeg_source_mgr *)
+	(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+				    sizeof (struct jpeg_source_mgr));
+      src = (struct jpeg_source_mgr *) cinfo->src;
+      src->next_input_byte = data;
+    }
+
+  src = (struct jpeg_source_mgr *) cinfo->src;
+  src->init_source = our_init_source;
+  src->fill_input_buffer = our_fill_input_buffer;
+  src->skip_input_data = our_skip_input_data;
+  src->resync_to_restart = jpeg_resync_to_restart_wrapper; /* Use default method.  */
+  src->term_source = our_term_source;
+  src->bytes_in_buffer = len;
+  src->next_input_byte = data;
+}
+
+
+/* Load image IMG for use on frame F.  Patterned after example.c
+   from the JPEG lib.  */
+
+static int
+jpeg_load (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  struct jpeg_decompress_struct cinfo;
+  struct my_jpeg_error_mgr mgr;
+  Lisp_Object file, specified_file;
+  Lisp_Object specified_data;
+  FILE * volatile fp = NULL;
+  JSAMPARRAY buffer;
+  int row_stride, x, y;
+  XImagePtr ximg = NULL;
+  int rc;
+  unsigned long *colors;
+  int width, height;
+  struct gcpro gcpro1;
+
+  /* Open the JPEG file.  */
+  specified_file = image_spec_value (img->spec, QCfile, NULL);
+  specified_data = image_spec_value (img->spec, QCdata, NULL);
+  file = Qnil;
+  GCPRO1 (file);
+
+  if (NILP (specified_data))
+    {
+      file = x_find_image_file (specified_file);
+      if (!STRINGP (file))
+	{
+	  image_error ("Cannot find image file `%s'", specified_file, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+
+      fp = fopen (SDATA (file), "rb");
+      if (fp == NULL)
+	{
+	  image_error ("Cannot open `%s'", file, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+    }
+
+  /* Customize libjpeg's error handling to call my_error_exit when an
+     error is detected.  This function will perform a longjmp.  */
+  cinfo.err = fn_jpeg_std_error (&mgr.pub);
+  mgr.pub.error_exit = my_error_exit;
+
+  if ((rc = setjmp (mgr.setjmp_buffer)) != 0)
+    {
+      if (rc == 1)
+	{
+	  /* Called from my_error_exit.  Display a JPEG error.  */
+	  char buffer[JMSG_LENGTH_MAX];
+	  cinfo.err->format_message ((j_common_ptr) &cinfo, buffer);
+	  image_error ("Error reading JPEG image `%s': %s", img->spec,
+		       build_string (buffer));
+	}
+
+      /* Close the input file and destroy the JPEG object.  */
+      if (fp)
+	fclose ((FILE *) fp);
+      fn_jpeg_destroy_decompress (&cinfo);
+
+      /* If we already have an XImage, free that.  */
+      x_destroy_x_image (ximg);
+
+      /* Free pixmap and colors.  */
+      x_clear_image (f, img);
+
+      UNGCPRO;
+      return 0;
+    }
+
+  /* Create the JPEG decompression object.  Let it read from fp.
+	 Read the JPEG image header.  */
+  fn_jpeg_CreateDecompress (&cinfo, JPEG_LIB_VERSION, sizeof (cinfo));
+
+  if (NILP (specified_data))
+    fn_jpeg_stdio_src (&cinfo, (FILE *) fp);
+  else
+    jpeg_memory_src (&cinfo, SDATA (specified_data),
+		     SBYTES (specified_data));
+
+  fn_jpeg_read_header (&cinfo, TRUE);
+
+  /* Customize decompression so that color quantization will be used.
+	 Start decompression.  */
+  cinfo.quantize_colors = TRUE;
+  fn_jpeg_start_decompress (&cinfo);
+  width = img->width = cinfo.output_width;
+  height = img->height = cinfo.output_height;
+
+  /* Create X image and pixmap.  */
+  if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap))
+    longjmp (mgr.setjmp_buffer, 2);
+
+  /* Allocate colors.  When color quantization is used,
+     cinfo.actual_number_of_colors has been set with the number of
+     colors generated, and cinfo.colormap is a two-dimensional array
+     of color indices in the range 0..cinfo.actual_number_of_colors.
+     No more than 255 colors will be generated.  */
+  {
+    int i, ir, ig, ib;
+
+    if (cinfo.out_color_components > 2)
+      ir = 0, ig = 1, ib = 2;
+    else if (cinfo.out_color_components > 1)
+      ir = 0, ig = 1, ib = 0;
+    else
+      ir = 0, ig = 0, ib = 0;
+
+    /* Use the color table mechanism because it handles colors that
+       cannot be allocated nicely.  Such colors will be replaced with
+       a default color, and we don't have to care about which colors
+       can be freed safely, and which can't.  */
+    init_color_table ();
+    colors = (unsigned long *) alloca (cinfo.actual_number_of_colors
+				       * sizeof *colors);
+
+    for (i = 0; i < cinfo.actual_number_of_colors; ++i)
+      {
+	/* Multiply RGB values with 255 because X expects RGB values
+	   in the range 0..0xffff.  */
+	int r = cinfo.colormap[ir][i] << 8;
+	int g = cinfo.colormap[ig][i] << 8;
+	int b = cinfo.colormap[ib][i] << 8;
+	colors[i] = lookup_rgb_color (f, r, g, b);
+      }
+
+#ifdef COLOR_TABLE_SUPPORT
+    /* Remember those colors actually allocated.  */
+    img->colors = colors_in_color_table (&img->ncolors);
+    free_color_table ();
+#endif /* COLOR_TABLE_SUPPORT */
+  }
+
+  /* Read pixels.  */
+  row_stride = width * cinfo.output_components;
+  buffer = cinfo.mem->alloc_sarray ((j_common_ptr) &cinfo, JPOOL_IMAGE,
+				    row_stride, 1);
+  for (y = 0; y < height; ++y)
+    {
+      fn_jpeg_read_scanlines (&cinfo, buffer, 1);
+      for (x = 0; x < cinfo.output_width; ++x)
+	XPutPixel (ximg, x, y, colors[buffer[0][x]]);
+    }
+
+  /* Clean up.  */
+  fn_jpeg_finish_decompress (&cinfo);
+  fn_jpeg_destroy_decompress (&cinfo);
+  if (fp)
+    fclose ((FILE *) fp);
+
+  /* Maybe fill in the background field while we have ximg handy. */
+  if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
+    IMAGE_BACKGROUND (img, f, ximg);
+
+  /* Put the image into the pixmap.  */
+  x_put_x_image (f, ximg, img->pixmap, width, height);
+  x_destroy_x_image (ximg);
+  UNGCPRO;
+  return 1;
+}
+
+#else /* HAVE_JPEG */
+
+#ifdef MAC_OS
+static int
+jpeg_load (f, img)
+     struct frame *f;
+     struct image *img;
+{
+#ifdef MAC_OSX
+  return image_load_quartz2d (f, img, 0);
+#else
+  return image_load_quicktime (f, img, kQTFileTypeJPEG);
+#endif
+}
+#endif  /* MAC_OS */
+
+#endif /* !HAVE_JPEG */
+
+
+
+/***********************************************************************
+				 TIFF
+ ***********************************************************************/
+
+#if defined (HAVE_TIFF) || defined (MAC_OS)
+
+static int tiff_image_p P_ ((Lisp_Object object));
+static int tiff_load P_ ((struct frame *f, struct image *img));
+
+/* The symbol `tiff' identifying images of this type.  */
+
+Lisp_Object Qtiff;
+
+/* Indices of image specification fields in tiff_format, below.  */
+
+enum tiff_keyword_index
+{
+  TIFF_TYPE,
+  TIFF_DATA,
+  TIFF_FILE,
+  TIFF_ASCENT,
+  TIFF_MARGIN,
+  TIFF_RELIEF,
+  TIFF_ALGORITHM,
+  TIFF_HEURISTIC_MASK,
+  TIFF_MASK,
+  TIFF_BACKGROUND,
+  TIFF_LAST
+};
+
+/* Vector of image_keyword structures describing the format
+   of valid user-defined image specifications.  */
+
+static struct image_keyword tiff_format[TIFF_LAST] =
+{
+  {":type",		IMAGE_SYMBOL_VALUE,			1},
+  {":data",		IMAGE_STRING_VALUE,			0},
+  {":file",		IMAGE_STRING_VALUE,			0},
+  {":ascent",		IMAGE_ASCENT_VALUE,			0},
+  {":margin",		IMAGE_POSITIVE_INTEGER_VALUE_OR_PAIR,	0},
+  {":relief",		IMAGE_INTEGER_VALUE,			0},
+  {":conversions",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":heuristic-mask",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":mask",		IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":background",	IMAGE_STRING_OR_NIL_VALUE,		0}
+};
+
+/* Structure describing the image type `tiff'.  */
+
+static struct image_type tiff_type =
+{
+  &Qtiff,
+  tiff_image_p,
+  tiff_load,
+  x_clear_image,
+  NULL
+};
+
+/* Return non-zero if OBJECT is a valid TIFF image specification.  */
+
+static int
+tiff_image_p (object)
+     Lisp_Object object;
+{
+  struct image_keyword fmt[TIFF_LAST];
+  bcopy (tiff_format, fmt, sizeof fmt);
+
+  if (!parse_image_spec (object, fmt, TIFF_LAST, Qtiff))
+    return 0;
+
+  /* Must specify either the :data or :file keyword.  */
+  return fmt[TIFF_FILE].count + fmt[TIFF_DATA].count == 1;
+}
+
+#endif /* HAVE_TIFF || MAC_OS */
+
+#ifdef HAVE_TIFF
+
+#include <tiffio.h>
+
+#ifdef HAVE_NTGUI
+
+/* TIFF library details.  */
+DEF_IMGLIB_FN (TIFFSetErrorHandler);
+DEF_IMGLIB_FN (TIFFSetWarningHandler);
+DEF_IMGLIB_FN (TIFFOpen);
+DEF_IMGLIB_FN (TIFFClientOpen);
+DEF_IMGLIB_FN (TIFFGetField);
+DEF_IMGLIB_FN (TIFFReadRGBAImage);
+DEF_IMGLIB_FN (TIFFClose);
+
+static int
+init_tiff_functions (void)
+{
+  HMODULE library;
+
+  if (!(library = LoadLibrary ("libtiff.dll")))
+    return 0;
+
+  LOAD_IMGLIB_FN (library, TIFFSetErrorHandler);
+  LOAD_IMGLIB_FN (library, TIFFSetWarningHandler);
+  LOAD_IMGLIB_FN (library, TIFFOpen);
+  LOAD_IMGLIB_FN (library, TIFFClientOpen);
+  LOAD_IMGLIB_FN (library, TIFFGetField);
+  LOAD_IMGLIB_FN (library, TIFFReadRGBAImage);
+  LOAD_IMGLIB_FN (library, TIFFClose);
+  return 1;
+}
+
+#else
+
+#define fn_TIFFSetErrorHandler		TIFFSetErrorHandler
+#define fn_TIFFSetWarningHandler	TIFFSetWarningHandler
+#define fn_TIFFOpen			TIFFOpen
+#define fn_TIFFClientOpen		TIFFClientOpen
+#define fn_TIFFGetField			TIFFGetField
+#define fn_TIFFReadRGBAImage		TIFFReadRGBAImage
+#define fn_TIFFClose			TIFFClose
+
+#endif /* HAVE_NTGUI */
+
+
+/* Reading from a memory buffer for TIFF images Based on the PNG
+   memory source, but we have to provide a lot of extra functions.
+   Blah.
+
+   We really only need to implement read and seek, but I am not
+   convinced that the TIFF library is smart enough not to destroy
+   itself if we only hand it the function pointers we need to
+   override.  */
+
+typedef struct
+{
+  unsigned char *bytes;
+  size_t len;
+  int index;
+}
+tiff_memory_source;
+
+static size_t
+tiff_read_from_memory (data, buf, size)
+     thandle_t data;
+     tdata_t buf;
+     tsize_t size;
+{
+  tiff_memory_source *src = (tiff_memory_source *) data;
+
+  if (size > src->len - src->index)
+    return (size_t) -1;
+  bcopy (src->bytes + src->index, buf, size);
+  src->index += size;
+  return size;
+}
+
+static size_t
+tiff_write_from_memory (data, buf, size)
+     thandle_t data;
+     tdata_t buf;
+     tsize_t size;
+{
+  return (size_t) -1;
+}
+
+static toff_t
+tiff_seek_in_memory (data, off, whence)
+     thandle_t data;
+     toff_t off;
+     int whence;
+{
+  tiff_memory_source *src = (tiff_memory_source *) data;
+  int idx;
+
+  switch (whence)
+    {
+    case SEEK_SET:		/* Go from beginning of source.  */
+      idx = off;
+      break;
+
+    case SEEK_END:		/* Go from end of source.  */
+      idx = src->len + off;
+      break;
+
+    case SEEK_CUR:		/* Go from current position.  */
+      idx = src->index + off;
+      break;
+
+    default:			/* Invalid `whence'.   */
+      return -1;
+    }
+
+  if (idx > src->len || idx < 0)
+    return -1;
+
+  src->index = idx;
+  return src->index;
+}
+
+static int
+tiff_close_memory (data)
+     thandle_t data;
+{
+  /* NOOP */
+  return 0;
+}
+
+static int
+tiff_mmap_memory (data, pbase, psize)
+     thandle_t data;
+     tdata_t *pbase;
+     toff_t *psize;
+{
+  /* It is already _IN_ memory. */
+  return 0;
+}
+
+static void
+tiff_unmap_memory (data, base, size)
+     thandle_t data;
+     tdata_t base;
+     toff_t size;
+{
+  /* We don't need to do this. */
+}
+
+static toff_t
+tiff_size_of_memory (data)
+     thandle_t data;
+{
+  return ((tiff_memory_source *) data)->len;
+}
+
+
+static void
+tiff_error_handler (title, format, ap)
+     const char *title, *format;
+     va_list ap;
+{
+  char buf[512];
+  int len;
+
+  len = sprintf (buf, "TIFF error: %s ", title);
+  vsprintf (buf + len, format, ap);
+  add_to_log (buf, Qnil, Qnil);
+}
+
+
+static void
+tiff_warning_handler (title, format, ap)
+     const char *title, *format;
+     va_list ap;
+{
+  char buf[512];
+  int len;
+
+  len = sprintf (buf, "TIFF warning: %s ", title);
+  vsprintf (buf + len, format, ap);
+  add_to_log (buf, Qnil, Qnil);
+}
+
+
+/* Load TIFF image IMG for use on frame F.  Value is non-zero if
+   successful.  */
+
+static int
+tiff_load (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  Lisp_Object file, specified_file;
+  Lisp_Object specified_data;
+  TIFF *tiff;
+  int width, height, x, y;
+  uint32 *buf;
+  int rc;
+  XImagePtr ximg;
+  struct gcpro gcpro1;
+  tiff_memory_source memsrc;
+
+  specified_file = image_spec_value (img->spec, QCfile, NULL);
+  specified_data = image_spec_value (img->spec, QCdata, NULL);
+  file = Qnil;
+  GCPRO1 (file);
+
+  fn_TIFFSetErrorHandler (tiff_error_handler);
+  fn_TIFFSetWarningHandler (tiff_warning_handler);
+
+  if (NILP (specified_data))
+    {
+      /* Read from a file */
+      file = x_find_image_file (specified_file);
+      if (!STRINGP (file))
+	{
+	  image_error ("Cannot find image file `%s'", specified_file, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+
+      /* Try to open the image file.  */
+      tiff = fn_TIFFOpen (SDATA (file), "r");
+      if (tiff == NULL)
+	{
+	  image_error ("Cannot open `%s'", file, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+    }
+  else
+    {
+      /* Memory source! */
+      memsrc.bytes = SDATA (specified_data);
+      memsrc.len = SBYTES (specified_data);
+      memsrc.index = 0;
+
+      tiff = fn_TIFFClientOpen ("memory_source", "r", &memsrc,
+                                (TIFFReadWriteProc) tiff_read_from_memory,
+                                (TIFFReadWriteProc) tiff_write_from_memory,
+                                tiff_seek_in_memory,
+                                tiff_close_memory,
+                                tiff_size_of_memory,
+                                tiff_mmap_memory,
+                                tiff_unmap_memory);
+
+      if (!tiff)
+	{
+	  image_error ("Cannot open memory source for `%s'", img->spec, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+    }
+
+  /* Get width and height of the image, and allocate a raster buffer
+     of width x height 32-bit values.  */
+  fn_TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width);
+  fn_TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height);
+  buf = (uint32 *) xmalloc (width * height * sizeof *buf);
+
+  rc = fn_TIFFReadRGBAImage (tiff, width, height, buf, 0);
+  fn_TIFFClose (tiff);
+  if (!rc)
+    {
+      image_error ("Error reading TIFF image `%s'", img->spec, Qnil);
+      xfree (buf);
+      UNGCPRO;
+      return 0;
+    }
+
+  /* Create the X image and pixmap.  */
+  if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap))
+    {
+      xfree (buf);
+      UNGCPRO;
+      return 0;
+    }
+
+  /* Initialize the color table.  */
+  init_color_table ();
+
+  /* Process the pixel raster.  Origin is in the lower-left corner.  */
+  for (y = 0; y < height; ++y)
+    {
+      uint32 *row = buf + y * width;
+
+      for (x = 0; x < width; ++x)
+	{
+	  uint32 abgr = row[x];
+	  int r = TIFFGetR (abgr) << 8;
+	  int g = TIFFGetG (abgr) << 8;
+	  int b = TIFFGetB (abgr) << 8;
+	  XPutPixel (ximg, x, height - 1 - y, lookup_rgb_color (f, r, g, b));
+	}
+    }
+
+#ifdef COLOR_TABLE_SUPPORT
+  /* Remember the colors allocated for the image.  Free the color table.  */
+  img->colors = colors_in_color_table (&img->ncolors);
+  free_color_table ();
+#endif /* COLOR_TABLE_SUPPORT */
+
+  img->width = width;
+  img->height = height;
+
+  /* Maybe fill in the background field while we have ximg handy. */
+  if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
+    IMAGE_BACKGROUND (img, f, ximg);
+
+  /* Put the image into the pixmap, then free the X image and its buffer.  */
+  x_put_x_image (f, ximg, img->pixmap, width, height);
+  x_destroy_x_image (ximg);
+  xfree (buf);
+
+  UNGCPRO;
+  return 1;
+}
+
+#else /* HAVE_TIFF */
+
+#ifdef MAC_OS
+static int
+tiff_load (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  return image_load_quicktime (f, img, kQTFileTypeTIFF);
+}
+#endif /* MAC_OS */
+
+#endif /* !HAVE_TIFF */
+
+
+
+/***********************************************************************
+				 GIF
+ ***********************************************************************/
+
+#if defined (HAVE_GIF) || defined (MAC_OS)
+
+static int gif_image_p P_ ((Lisp_Object object));
+static int gif_load P_ ((struct frame *f, struct image *img));
+
+/* The symbol `gif' identifying images of this type.  */
+
+Lisp_Object Qgif;
+
+/* Indices of image specification fields in gif_format, below.  */
+
+enum gif_keyword_index
+{
+  GIF_TYPE,
+  GIF_DATA,
+  GIF_FILE,
+  GIF_ASCENT,
+  GIF_MARGIN,
+  GIF_RELIEF,
+  GIF_ALGORITHM,
+  GIF_HEURISTIC_MASK,
+  GIF_MASK,
+  GIF_IMAGE,
+  GIF_BACKGROUND,
+  GIF_LAST
+};
+
+/* Vector of image_keyword structures describing the format
+   of valid user-defined image specifications.  */
+
+static struct image_keyword gif_format[GIF_LAST] =
+{
+  {":type",		IMAGE_SYMBOL_VALUE,			1},
+  {":data",		IMAGE_STRING_VALUE,			0},
+  {":file",		IMAGE_STRING_VALUE,			0},
+  {":ascent",		IMAGE_ASCENT_VALUE,			0},
+  {":margin",		IMAGE_POSITIVE_INTEGER_VALUE_OR_PAIR,	0},
+  {":relief",		IMAGE_INTEGER_VALUE,			0},
+  {":conversion",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":heuristic-mask",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":mask",		IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":image",		IMAGE_NON_NEGATIVE_INTEGER_VALUE,	0},
+  {":background",	IMAGE_STRING_OR_NIL_VALUE,		0}
+};
+
+/* Structure describing the image type `gif'.  */
+
+static struct image_type gif_type =
+{
+  &Qgif,
+  gif_image_p,
+  gif_load,
+  x_clear_image,
+  NULL
+};
+
+/* Return non-zero if OBJECT is a valid GIF image specification.  */
+
+static int
+gif_image_p (object)
+     Lisp_Object object;
+{
+  struct image_keyword fmt[GIF_LAST];
+  bcopy (gif_format, fmt, sizeof fmt);
+
+  if (!parse_image_spec (object, fmt, GIF_LAST, Qgif))
+    return 0;
+
+  /* Must specify either the :data or :file keyword.  */
+  return fmt[GIF_FILE].count + fmt[GIF_DATA].count == 1;
+}
+
+#endif /* HAVE_GIF || MAC_OS */
+
+#ifdef HAVE_GIF
+
+#if defined (HAVE_NTGUI) || defined (MAC_OS)
+/* avoid conflict with QuickdrawText.h */
+#define DrawText gif_DrawText
+#include <gif_lib.h>
+#undef DrawText
+
+#else /* HAVE_NTGUI || MAC_OS */
+
+#include <gif_lib.h>
+
+#endif /* HAVE_NTGUI || MAC_OS */
+
+
+#ifdef HAVE_NTGUI
+
+/* GIF library details.  */
+DEF_IMGLIB_FN (DGifCloseFile);
+DEF_IMGLIB_FN (DGifSlurp);
+DEF_IMGLIB_FN (DGifOpen);
+DEF_IMGLIB_FN (DGifOpenFileName);
+
+static int
+init_gif_functions (void)
+{
+  HMODULE library;
+
+  if (!(library = LoadLibrary ("libungif.dll")))
+    return 0;
+
+  LOAD_IMGLIB_FN (library, DGifCloseFile);
+  LOAD_IMGLIB_FN (library, DGifSlurp);
+  LOAD_IMGLIB_FN (library, DGifOpen);
+  LOAD_IMGLIB_FN (library, DGifOpenFileName);
+  return 1;
+}
+
+#else
+
+#define fn_DGifCloseFile	DGifCloseFile
+#define fn_DGifSlurp		DGifSlurp
+#define fn_DGifOpen		DGifOpen
+#define fn_DGifOpenFileName	DGifOpenFileName
+
+#endif /* HAVE_NTGUI */
+
+/* Reading a GIF image from memory
+   Based on the PNG memory stuff to a certain extent. */
+
+typedef struct
+{
+  unsigned char *bytes;
+  size_t len;
+  int index;
+}
+gif_memory_source;
+
+/* Make the current memory source available to gif_read_from_memory.
+   It's done this way because not all versions of libungif support
+   a UserData field in the GifFileType structure.  */
+static gif_memory_source *current_gif_memory_src;
+
+static int
+gif_read_from_memory (file, buf, len)
+     GifFileType *file;
+     GifByteType *buf;
+     int len;
+{
+  gif_memory_source *src = current_gif_memory_src;
+
+  if (len > src->len - src->index)
+    return -1;
+
+  bcopy (src->bytes + src->index, buf, len);
+  src->index += len;
+  return len;
+}
+
+
+/* Load GIF image IMG for use on frame F.  Value is non-zero if
+   successful.  */
+
+static int
+gif_load (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  Lisp_Object file, specified_file;
+  Lisp_Object specified_data;
+  int rc, width, height, x, y, i;
+  XImagePtr ximg;
+  ColorMapObject *gif_color_map;
+  unsigned long pixel_colors[256];
+  GifFileType *gif;
+  struct gcpro gcpro1;
+  Lisp_Object image;
+  int ino, image_left, image_top, image_width, image_height;
+  gif_memory_source memsrc;
+  unsigned char *raster;
+
+  specified_file = image_spec_value (img->spec, QCfile, NULL);
+  specified_data = image_spec_value (img->spec, QCdata, NULL);
+  file = Qnil;
+  GCPRO1 (file);
+
+  if (NILP (specified_data))
+    {
+      file = x_find_image_file (specified_file);
+      if (!STRINGP (file))
+	{
+	  image_error ("Cannot find image file `%s'", specified_file, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+
+      /* Open the GIF file.  */
+      gif = fn_DGifOpenFileName (SDATA (file));
+      if (gif == NULL)
+	{
+	  image_error ("Cannot open `%s'", file, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+    }
+  else
+    {
+      /* Read from memory! */
+      current_gif_memory_src = &memsrc;
+      memsrc.bytes = SDATA (specified_data);
+      memsrc.len = SBYTES (specified_data);
+      memsrc.index = 0;
+
+      gif = fn_DGifOpen(&memsrc, gif_read_from_memory);
+      if (!gif)
+	{
+	  image_error ("Cannot open memory source `%s'", img->spec, Qnil);
+	  UNGCPRO;
+	  return 0;
+	}
+    }
+
+  /* Read entire contents.  */
+  rc = fn_DGifSlurp (gif);
+  if (rc == GIF_ERROR)
+    {
+      image_error ("Error reading `%s'", img->spec, Qnil);
+      fn_DGifCloseFile (gif);
+      UNGCPRO;
+      return 0;
+    }
+
+  image = image_spec_value (img->spec, QCindex, NULL);
+  ino = INTEGERP (image) ? XFASTINT (image) : 0;
+  if (ino >= gif->ImageCount)
+    {
+      image_error ("Invalid image number `%s' in image `%s'",
+		   image, img->spec);
+      fn_DGifCloseFile (gif);
+      UNGCPRO;
+      return 0;
+    }
+
+  width = img->width = max (gif->SWidth, gif->Image.Left + gif->Image.Width);
+  height = img->height = max (gif->SHeight, gif->Image.Top + gif->Image.Height);
+
+  /* Create the X image and pixmap.  */
+  if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap))
+    {
+      fn_DGifCloseFile (gif);
+      UNGCPRO;
+      return 0;
+    }
+
+  /* Allocate colors.  */
+  gif_color_map = gif->SavedImages[ino].ImageDesc.ColorMap;
+  if (!gif_color_map)
+    gif_color_map = gif->SColorMap;
+  init_color_table ();
+  bzero (pixel_colors, sizeof pixel_colors);
+
+  for (i = 0; i < gif_color_map->ColorCount; ++i)
+    {
+      int r = gif_color_map->Colors[i].Red << 8;
+      int g = gif_color_map->Colors[i].Green << 8;
+      int b = gif_color_map->Colors[i].Blue << 8;
+      pixel_colors[i] = lookup_rgb_color (f, r, g, b);
+    }
+
+#ifdef COLOR_TABLE_SUPPORT
+  img->colors = colors_in_color_table (&img->ncolors);
+  free_color_table ();
+#endif /* COLOR_TABLE_SUPPORT */
+
+  /* Clear the part of the screen image that are not covered by
+     the image from the GIF file.  Full animated GIF support
+     requires more than can be done here (see the gif89 spec,
+     disposal methods).  Let's simply assume that the part
+     not covered by a sub-image is in the frame's background color.  */
+  image_top = gif->SavedImages[ino].ImageDesc.Top;
+  image_left = gif->SavedImages[ino].ImageDesc.Left;
+  image_width = gif->SavedImages[ino].ImageDesc.Width;
+  image_height = gif->SavedImages[ino].ImageDesc.Height;
+
+  for (y = 0; y < image_top; ++y)
+    for (x = 0; x < width; ++x)
+      XPutPixel (ximg, x, y, FRAME_BACKGROUND_PIXEL (f));
+
+  for (y = image_top + image_height; y < height; ++y)
+    for (x = 0; x < width; ++x)
+      XPutPixel (ximg, x, y, FRAME_BACKGROUND_PIXEL (f));
+
+  for (y = image_top; y < image_top + image_height; ++y)
+    {
+      for (x = 0; x < image_left; ++x)
+	XPutPixel (ximg, x, y, FRAME_BACKGROUND_PIXEL (f));
+      for (x = image_left + image_width; x < width; ++x)
+	XPutPixel (ximg, x, y, FRAME_BACKGROUND_PIXEL (f));
+    }
+
+  /* Read the GIF image into the X image.  We use a local variable
+     `raster' here because RasterBits below is a char *, and invites
+     problems with bytes >= 0x80.  */
+  raster = (unsigned char *) gif->SavedImages[ino].RasterBits;
+
+  if (gif->SavedImages[ino].ImageDesc.Interlace)
+    {
+      static int interlace_start[] = {0, 4, 2, 1};
+      static int interlace_increment[] = {8, 8, 4, 2};
+      int pass;
+      int row = interlace_start[0];
+
+      pass = 0;
+
+      for (y = 0; y < image_height; y++)
+	{
+	  if (row >= image_height)
+	    {
+	      row = interlace_start[++pass];
+	      while (row >= image_height)
+		row = interlace_start[++pass];
+	    }
+
+	  for (x = 0; x < image_width; x++)
+	    {
+	      int i = raster[(y * image_width) + x];
+	      XPutPixel (ximg, x + image_left, row + image_top,
+			 pixel_colors[i]);
+	    }
+
+	  row += interlace_increment[pass];
+	}
+    }
+  else
+    {
+      for (y = 0; y < image_height; ++y)
+	for (x = 0; x < image_width; ++x)
+	  {
+	    int i = raster[y * image_width + x];
+	    XPutPixel (ximg, x + image_left, y + image_top, pixel_colors[i]);
+	  }
+    }
+
+  fn_DGifCloseFile (gif);
+
+  /* Maybe fill in the background field while we have ximg handy. */
+  if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
+    IMAGE_BACKGROUND (img, f, ximg);
+
+  /* Put the image into the pixmap, then free the X image and its buffer.  */
+  x_put_x_image (f, ximg, img->pixmap, width, height);
+  x_destroy_x_image (ximg);
+
+  UNGCPRO;
+  return 1;
+}
+
+#else
+
+#ifdef MAC_OS
+static int
+gif_load (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  Lisp_Object specified_file, file;
+  Lisp_Object specified_data;
+  OSErr err;
+  Boolean graphic_p, movie_p, prefer_graphic_p;
+  Handle dh = NULL;
+  Movie movie = NULL;
+  Lisp_Object image;
+  Track track = NULL;
+  Media media = NULL;
+  long nsamples;
+  Rect rect;
+  Lisp_Object specified_bg;
+  XColor color;
+  RGBColor bg_color;
+  int width, height;
+  XImagePtr ximg;
+  TimeValue time;
+  struct gcpro gcpro1;
+  int ino;
+
+  specified_file = image_spec_value (img->spec, QCfile, NULL);
+  specified_data = image_spec_value (img->spec, QCdata, NULL);
+
+  if (NILP (specified_data))
+    {
+      /* Read from a file */
+      FSSpec fss;
+      short refnum;
+
+      err = find_image_fsspec (specified_file, &file, &fss);
+      if (err != noErr)
+	{
+	  if (err == fnfErr)
+	    image_error ("Cannot find image file `%s'", specified_file, Qnil);
+	  else
+	    goto open_error;
+	}
+
+      err = CanQuickTimeOpenFile (&fss, kQTFileTypeGIF, 0,
+				  &graphic_p, &movie_p, &prefer_graphic_p, 0);
+      if (err != noErr)
+	goto open_error;
+
+      if (!graphic_p && !movie_p)
+	goto open_error;
+      if (prefer_graphic_p)
+	return image_load_qt_1 (f, img, kQTFileTypeGIF, &fss, NULL);
+      err = OpenMovieFile (&fss, &refnum, fsRdPerm);
+      if (err != noErr)
+	goto open_error;
+      err = NewMovieFromFile (&movie, refnum, NULL, NULL, 0, NULL);
+      CloseMovieFile (refnum);
+      if (err != noErr)
+	{
+	  image_error ("Error reading `%s'", file, Qnil);
+	  return 0;
+	}
+    }
+  else
+    {
+      /* Memory source! */
+      Handle dref = NULL;
+      long file_type_atom[3];
+
+      err = PtrToHand (SDATA (specified_data), &dh, SBYTES (specified_data));
+      if (err != noErr)
+	{
+	  image_error ("Cannot allocate data handle for `%s'",
+		       img->spec, Qnil);
+	  goto error;
+	}
+
+      file_type_atom[0] = EndianU32_NtoB (sizeof (long) * 3);
+      file_type_atom[1] = EndianU32_NtoB (kDataRefExtensionMacOSFileType);
+      file_type_atom[2] = EndianU32_NtoB (kQTFileTypeGIF);
+      err = PtrToHand (&dh, &dref, sizeof (Handle));
+      if (err == noErr)
+	/* no file name */
+	err = PtrAndHand ("\p", dref, 1);
+      if (err == noErr)
+	err = PtrAndHand (file_type_atom, dref, sizeof (long) * 3);
+      if (err != noErr)
+	{
+	  image_error ("Cannot allocate handle data ref for `%s'", img->spec, Qnil);
+	  goto error;
+	}
+      err = CanQuickTimeOpenDataRef (dref, HandleDataHandlerSubType, &graphic_p,
+				     &movie_p, &prefer_graphic_p, 0);
+      if (err != noErr)
+	goto open_error;
+
+      if (!graphic_p && !movie_p)
+	goto open_error;
+      if (prefer_graphic_p)
+	{
+	  int success_p;
+
+	  DisposeHandle (dref);
+	  success_p = image_load_qt_1 (f, img, kQTFileTypeGIF, NULL, dh);
+	  DisposeHandle (dh);
+	  return success_p;
+	}
+      err = NewMovieFromDataRef (&movie, 0, NULL, dref,
+				 HandleDataHandlerSubType);
+      DisposeHandle (dref);
+      if (err != noErr)
+	goto open_error;
+    }
+
+  image = image_spec_value (img->spec, QCindex, NULL);
+  ino = INTEGERP (image) ? XFASTINT (image) : 0;
+  track = GetMovieIndTrack (movie, 1);
+  media = GetTrackMedia (track);
+  nsamples = GetMediaSampleCount (media);
+  if (ino >= nsamples)
+    {
+      image_error ("Invalid image number `%s' in image `%s'",
+		   image, img->spec);
+      goto error;
+    }
+
+  specified_bg = image_spec_value (img->spec, QCbackground, NULL);
+  if (!STRINGP (specified_bg) ||
+      !mac_defined_color (f, SDATA (specified_bg), &color, 0))
+    {
+      color.pixel = FRAME_BACKGROUND_PIXEL (f);
+      color.red = RED16_FROM_ULONG (color.pixel);
+      color.green = GREEN16_FROM_ULONG (color.pixel);
+      color.blue = BLUE16_FROM_ULONG (color.pixel);
+    }
+  GetMovieBox (movie, &rect);
+  width = img->width = rect.right - rect.left;
+  height = img->height = rect.bottom - rect.top;
+  if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap))
+    goto error;
+
+  SetGWorld (ximg, NULL);
+  bg_color.red = color.red;
+  bg_color.green = color.green;
+  bg_color.blue = color.blue;
+  RGBBackColor (&bg_color);
+  SetMovieActive (movie, TRUE);
+  SetMovieGWorld (movie, ximg, NULL);
+  SampleNumToMediaTime (media, ino + 1, &time, NULL);
+  SetMovieTimeValue (movie, time);
+  MoviesTask (movie, 0L);
+  DisposeTrackMedia (media);
+  DisposeMovieTrack (track);
+  DisposeMovie (movie);
+  if (dh)
+    DisposeHandle (dh);
+  /* Maybe fill in the background field while we have ximg handy. */
+  if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
+    IMAGE_BACKGROUND (img, f, ximg);
+
+  /* Put the image into the pixmap.  */
+  x_put_x_image (f, ximg, img->pixmap, width, height);
+  x_destroy_x_image (ximg);
+  return 1;
+
+ open_error:
+  image_error ("Cannot open `%s'", file, Qnil);
+ error:
+  if (media)
+    DisposeTrackMedia (media);
+  if (track)
+    DisposeMovieTrack (track);
+  if (movie)
+    DisposeMovie (movie);
+  if (dh)
+    DisposeHandle (dh);
+  return 0;
+}
+#endif /* MAC_OS */
+
+#endif /* HAVE_GIF */
+
+
+
+/***********************************************************************
+				Ghostscript
+ ***********************************************************************/
+
+#ifdef HAVE_X_WINDOWS
+#define HAVE_GHOSTSCRIPT 1
+#endif /* HAVE_X_WINDOWS */
+
+/* The symbol `postscript' identifying images of this type.  */
+
+Lisp_Object Qpostscript;
+
+#ifdef HAVE_GHOSTSCRIPT
+
+static int gs_image_p P_ ((Lisp_Object object));
+static int gs_load P_ ((struct frame *f, struct image *img));
+static void gs_clear_image P_ ((struct frame *f, struct image *img));
+
+/* Keyword symbols.  */
+
+Lisp_Object QCloader, QCbounding_box, QCpt_width, QCpt_height;
+
+/* Indices of image specification fields in gs_format, below.  */
+
+enum gs_keyword_index
+{
+  GS_TYPE,
+  GS_PT_WIDTH,
+  GS_PT_HEIGHT,
+  GS_FILE,
+  GS_LOADER,
+  GS_BOUNDING_BOX,
+  GS_ASCENT,
+  GS_MARGIN,
+  GS_RELIEF,
+  GS_ALGORITHM,
+  GS_HEURISTIC_MASK,
+  GS_MASK,
+  GS_BACKGROUND,
+  GS_LAST
+};
+
+/* Vector of image_keyword structures describing the format
+   of valid user-defined image specifications.  */
+
+static struct image_keyword gs_format[GS_LAST] =
+{
+  {":type",		IMAGE_SYMBOL_VALUE,			1},
+  {":pt-width",		IMAGE_POSITIVE_INTEGER_VALUE,		1},
+  {":pt-height",	IMAGE_POSITIVE_INTEGER_VALUE,		1},
+  {":file",		IMAGE_STRING_VALUE,			1},
+  {":loader",		IMAGE_FUNCTION_VALUE,			0},
+  {":bounding-box",	IMAGE_DONT_CHECK_VALUE_TYPE,		1},
+  {":ascent",		IMAGE_ASCENT_VALUE,			0},
+  {":margin",		IMAGE_POSITIVE_INTEGER_VALUE_OR_PAIR,	0},
+  {":relief",		IMAGE_INTEGER_VALUE,			0},
+  {":conversion",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":heuristic-mask",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":mask",		IMAGE_DONT_CHECK_VALUE_TYPE,		0},
+  {":background",	IMAGE_STRING_OR_NIL_VALUE,		0}
+};
+
+/* Structure describing the image type `ghostscript'.  */
+
+static struct image_type gs_type =
+{
+  &Qpostscript,
+  gs_image_p,
+  gs_load,
+  gs_clear_image,
+  NULL
+};
+
+
+/* Free X resources of Ghostscript image IMG which is used on frame F.  */
+
+static void
+gs_clear_image (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  /* IMG->data.ptr_val may contain a recorded colormap.  */
+  xfree (img->data.ptr_val);
+  x_clear_image (f, img);
+}
+
+
+/* Return non-zero if OBJECT is a valid Ghostscript image
+   specification.  */
+
+static int
+gs_image_p (object)
+     Lisp_Object object;
+{
+  struct image_keyword fmt[GS_LAST];
+  Lisp_Object tem;
+  int i;
+
+  bcopy (gs_format, fmt, sizeof fmt);
+
+  if (!parse_image_spec (object, fmt, GS_LAST, Qpostscript))
+    return 0;
+
+  /* Bounding box must be a list or vector containing 4 integers.  */
+  tem = fmt[GS_BOUNDING_BOX].value;
+  if (CONSP (tem))
+    {
+      for (i = 0; i < 4; ++i, tem = XCDR (tem))
+	if (!CONSP (tem) || !INTEGERP (XCAR (tem)))
+	  return 0;
+      if (!NILP (tem))
+	return 0;
+    }
+  else if (VECTORP (tem))
+    {
+      if (XVECTOR (tem)->size != 4)
+	return 0;
+      for (i = 0; i < 4; ++i)
+	if (!INTEGERP (XVECTOR (tem)->contents[i]))
+	  return 0;
+    }
+  else
+    return 0;
+
+  return 1;
+}
+
+
+/* Load Ghostscript image IMG for use on frame F.  Value is non-zero
+   if successful.  */
+
+static int
+gs_load (f, img)
+     struct frame *f;
+     struct image *img;
+{
+  char buffer[100];
+  Lisp_Object window_and_pixmap_id = Qnil, loader, pt_height, pt_width;
+  struct gcpro gcpro1, gcpro2;
+  Lisp_Object frame;
+  double in_width, in_height;
+  Lisp_Object pixel_colors = Qnil;
+
+  /* Compute pixel size of pixmap needed from the given size in the
+     image specification.  Sizes in the specification are in pt.  1 pt
+     = 1/72 in, xdpi and ydpi are stored in the frame's X display
+     info.  */
+  pt_width = image_spec_value (img->spec, QCpt_width, NULL);
+  in_width = XFASTINT (pt_width) / 72.0;
+  img->width = in_width * FRAME_X_DISPLAY_INFO (f)->resx;
+  pt_height = image_spec_value (img->spec, QCpt_height, NULL);
+  in_height = XFASTINT (pt_height) / 72.0;
+  img->height = in_height * FRAME_X_DISPLAY_INFO (f)->resy;
+
+  /* Create the pixmap.  */
+  xassert (img->pixmap == NO_PIXMAP);
+
+  /* Only W32 version did BLOCK_INPUT here.  ++kfs */
+  BLOCK_INPUT;
+  img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+			       img->width, img->height,
+			       DefaultDepthOfScreen (FRAME_X_SCREEN (f)));
+  UNBLOCK_INPUT;
+
+  if (!img->pixmap)
+    {
+      image_error ("Unable to create pixmap for `%s'", img->spec, Qnil);
+      return 0;
+    }
+
+  /* Call the loader to fill the pixmap.  It returns a process object
+     if successful.  We do not record_unwind_protect here because
+     other places in redisplay like calling window scroll functions
+     don't either.  Let the Lisp loader use `unwind-protect' instead.  */
+  GCPRO2 (window_and_pixmap_id, pixel_colors);
+
+  sprintf (buffer, "%lu %lu",
+	   (unsigned long) FRAME_X_WINDOW (f),
+	   (unsigned long) img->pixmap);
+  window_and_pixmap_id = build_string (buffer);
+
+  sprintf (buffer, "%lu %lu",
+	   FRAME_FOREGROUND_PIXEL (f),
+	   FRAME_BACKGROUND_PIXEL (f));
+  pixel_colors = build_string (buffer);
+
+  XSETFRAME (frame, f);
+  loader = image_spec_value (img->spec, QCloader, NULL);
+  if (NILP (loader))
+    loader = intern ("gs-load-image");
+
+  img->data.lisp_val = call6 (loader, frame, img->spec,
+			      make_number (img->width),
+			      make_number (img->height),
+			      window_and_pixmap_id,
+			      pixel_colors);
+  UNGCPRO;
+  return PROCESSP (img->data.lisp_val);
+}
+
+
+/* Kill the Ghostscript process that was started to fill PIXMAP on
+   frame F.  Called from XTread_socket when receiving an event
+   telling Emacs that Ghostscript has finished drawing.  */
+
+void
+x_kill_gs_process (pixmap, f)
+     Pixmap pixmap;
+     struct frame *f;
+{
+  struct image_cache *c = FRAME_X_IMAGE_CACHE (f);
+  int class, i;
+  struct image *img;
+
+  /* Find the image containing PIXMAP.  */
+  for (i = 0; i < c->used; ++i)
+    if (c->images[i]->pixmap == pixmap)
+      break;
+
+  /* Should someone in between have cleared the image cache, for
+     instance, give up.  */
+  if (i == c->used)
+    return;
+
+  /* Kill the GS process.  We should have found PIXMAP in the image
+     cache and its image should contain a process object.  */
+  img = c->images[i];
+  xassert (PROCESSP (img->data.lisp_val));
+  Fkill_process (img->data.lisp_val, Qnil);
+  img->data.lisp_val = Qnil;
+
+#if defined (HAVE_X_WINDOWS)
+
+  /* On displays with a mutable colormap, figure out the colors
+     allocated for the image by looking at the pixels of an XImage for
+     img->pixmap.  */
+  class = FRAME_X_VISUAL (f)->class;
+  if (class != StaticColor && class != StaticGray && class != TrueColor)
+    {
+      XImagePtr ximg;
+
+      BLOCK_INPUT;
+
+      /* Try to get an XImage for img->pixmep.  */
+      ximg = XGetImage (FRAME_X_DISPLAY (f), img->pixmap,
+			0, 0, img->width, img->height, ~0, ZPixmap);
+      if (ximg)
+	{
+	  int x, y;
+
+	  /* Initialize the color table.  */
+	  init_color_table ();
+
+	  /* For each pixel of the image, look its color up in the
+	     color table.  After having done so, the color table will
+	     contain an entry for each color used by the image.  */
+	  for (y = 0; y < img->height; ++y)
+	    for (x = 0; x < img->width; ++x)
+	      {
+		unsigned long pixel = XGetPixel (ximg, x, y);
+		lookup_pixel_color (f, pixel);
+	      }
+
+	  /* Record colors in the image.  Free color table and XImage.  */
+#ifdef COLOR_TABLE_SUPPORT
+	  img->colors = colors_in_color_table (&img->ncolors);
+	  free_color_table ();
+#endif
+	  XDestroyImage (ximg);
+
+#if 0 /* This doesn't seem to be the case.  If we free the colors
+	 here, we get a BadAccess later in x_clear_image when
+	 freeing the colors.  */
+	  /* We have allocated colors once, but Ghostscript has also
+	     allocated colors on behalf of us.  So, to get the
+	     reference counts right, free them once.  */
+	  if (img->ncolors)
+	    x_free_colors (f, img->colors, img->ncolors);
+#endif
+	}
+      else
+	image_error ("Cannot get X image of `%s'; colors will not be freed",
+		     img->spec, Qnil);
+
+      UNBLOCK_INPUT;
+    }
+#endif /* HAVE_X_WINDOWS */
+
+  /* Now that we have the pixmap, compute mask and transform the
+     image if requested.  */
+  BLOCK_INPUT;
+  postprocess_image (f, img);
+  UNBLOCK_INPUT;
+}
+
+#endif /* HAVE_GHOSTSCRIPT */
+
+
+/***********************************************************************
+				Tests
+ ***********************************************************************/
+
+#if GLYPH_DEBUG
+
+DEFUN ("imagep", Fimagep, Simagep, 1, 1, 0,
+       doc: /* Value is non-nil if SPEC is a valid image specification.  */)
+  (spec)
+     Lisp_Object spec;
+{
+  return valid_image_p (spec) ? Qt : Qnil;
+}
+
+
+DEFUN ("lookup-image", Flookup_image, Slookup_image, 1, 1, 0, "")
+  (spec)
+     Lisp_Object spec;
+{
+  int id = -1;
+
+  if (valid_image_p (spec))
+    id = lookup_image (SELECTED_FRAME (), spec);
+
+  debug_print (spec);
+  return make_number (id);
+}
+
+#endif /* GLYPH_DEBUG != 0 */
+
+
+/***********************************************************************
+			    Initialization
+ ***********************************************************************/
+
+void
+syms_of_image ()
+{
+  QCascent = intern (":ascent");
+  staticpro (&QCascent);
+  QCmargin = intern (":margin");
+  staticpro (&QCmargin);
+  QCrelief = intern (":relief");
+  staticpro (&QCrelief);
+  QCconversion = intern (":conversion");
+  staticpro (&QCconversion);
+  QCcolor_symbols = intern (":color-symbols");
+  staticpro (&QCcolor_symbols);
+  QCheuristic_mask = intern (":heuristic-mask");
+  staticpro (&QCheuristic_mask);
+  QCindex = intern (":index");
+  staticpro (&QCindex);
+  QCmatrix = intern (":matrix");
+  staticpro (&QCmatrix);
+  QCcolor_adjustment = intern (":color-adjustment");
+  staticpro (&QCcolor_adjustment);
+  QCmask = intern (":mask");
+  staticpro (&QCmask);
+
+  Qlaplace = intern ("laplace");
+  staticpro (&Qlaplace);
+  Qemboss = intern ("emboss");
+  staticpro (&Qemboss);
+  Qedge_detection = intern ("edge-detection");
+  staticpro (&Qedge_detection);
+  Qheuristic = intern ("heuristic");
+  staticpro (&Qheuristic);
+  Qcenter = intern ("center");
+  staticpro (&Qcenter);
+
+  Qpostscript = intern ("postscript");
+  staticpro (&Qpostscript);
+#ifdef HAVE_GHOSTSCRIPT
+  QCloader = intern (":loader");
+  staticpro (&QCloader);
+  QCbounding_box = intern (":bounding-box");
+  staticpro (&QCbounding_box);
+  QCpt_width = intern (":pt-width");
+  staticpro (&QCpt_width);
+  QCpt_height = intern (":pt-height");
+  staticpro (&QCpt_height);
+#endif /* HAVE_GHOSTSCRIPT */
+
+  Qpbm = intern ("pbm");
+  staticpro (&Qpbm);
+
+  Qxbm = intern ("xbm");
+  staticpro (&Qxbm);
+
+#ifdef HAVE_XPM
+  Qxpm = intern ("xpm");
+  staticpro (&Qxpm);
+#endif
+
+#if defined (HAVE_JPEG) || defined (MAC_OS)
+  Qjpeg = intern ("jpeg");
+  staticpro (&Qjpeg);
+#endif
+
+#if defined (HAVE_TIFF) || defined (MAC_OS)
+  Qtiff = intern ("tiff");
+  staticpro (&Qtiff);
+#endif
+
+#if defined (HAVE_GIF) || defined (MAC_OS)
+  Qgif = intern ("gif");
+  staticpro (&Qgif);
+#endif
+
+#if defined (HAVE_PNG) || defined (MAC_OS)
+  Qpng = intern ("png");
+  staticpro (&Qpng);
+#endif
+
+  defsubr (&Sclear_image_cache);
+  defsubr (&Simage_size);
+  defsubr (&Simage_mask_p);
+
+#if GLYPH_DEBUG
+  defsubr (&Simagep);
+  defsubr (&Slookup_image);
+#endif
+
+  DEFVAR_BOOL ("cross-disabled-images", &cross_disabled_images,
+    doc: /* Non-nil means always draw a cross over disabled images.
+Disabled images are those having an `:conversion disabled' property.
+A cross is always drawn on black & white displays.  */);
+  cross_disabled_images = 0;
+
+  DEFVAR_LISP ("x-bitmap-file-path", &Vx_bitmap_file_path,
+    doc: /* List of directories to search for window system bitmap files.  */);
+  Vx_bitmap_file_path = decode_env_path ((char *) 0, PATH_BITMAPS);
+
+  DEFVAR_LISP ("image-cache-eviction-delay", &Vimage_cache_eviction_delay,
+    doc: /* Time after which cached images are removed from the cache.
+When an image has not been displayed this many seconds, remove it
+from the image cache.  Value must be an integer or nil with nil
+meaning don't clear the cache.  */);
+  Vimage_cache_eviction_delay = make_number (30 * 60);
+}
+
+
+#ifdef HAVE_NTGUI
+/* Image types that rely on external libraries are loaded dynamically
+   if the library is available.  */
+#define IF_LIB_AVAILABLE(init_lib_fn)  if (init_lib_fn())
+#else
+#define IF_LIB_AVAILABLE(init_func)    /* Load unconditionally */
+#endif /* HAVE_NTGUI */
+
+void
+init_image ()
+{
+  image_types = NULL;
+  Vimage_types = Qnil;
+
+  define_image_type (&xbm_type);
+  define_image_type (&pbm_type);
+
+#ifdef HAVE_XPM
+  IF_LIB_AVAILABLE(init_xpm_functions)
+    define_image_type (&xpm_type);
+#endif
+
+#if defined (HAVE_JPEG) || defined (MAC_OS)
+  IF_LIB_AVAILABLE(init_jpeg_functions)
+    define_image_type (&jpeg_type);
+#endif
+
+#if defined (HAVE_TIFF) || defined (MAC_OS)
+  IF_LIB_AVAILABLE(init_tiff_functions)
+    define_image_type (&tiff_type);
+#endif
+
+#if defined (HAVE_GIF) || defined (MAC_OS)
+  IF_LIB_AVAILABLE(init_gif_functions)
+    define_image_type (&gif_type);
+#endif
+
+#if defined (HAVE_PNG) || defined (MAC_OS)
+  IF_LIB_AVAILABLE(init_png_functions)
+    define_image_type (&png_type);
+#endif
+
+#ifdef HAVE_GHOSTSCRIPT
+  define_image_type (&gs_type);
+#endif
+
+#ifdef MAC_OS
+  /* Animated gifs use QuickTime Movie Toolbox.  So initialize it here. */
+  EnterMovies ();
+#ifdef MAC_OSX
+  init_image_func_pointer ();
+#endif
+#endif
+}
+