diff src/glyphs-x.c @ 251:677f6a0ee643 r20-5b24

Import from CVS: tag r20-5b24
author cvs
date Mon, 13 Aug 2007 10:19:59 +0200
parents f220cc83d72e
children 11cf20601dec
line wrap: on
line diff
--- a/src/glyphs-x.c	Mon Aug 13 10:19:12 2007 +0200
+++ b/src/glyphs-x.c	Mon Aug 13 10:19:59 2007 +0200
@@ -37,14 +37,12 @@
    Pointer/icon overhaul, more restructuring by Ben Wing for 19.14
 
    TODO:
-   ImageMagick support
    Convert images.el to C and stick it in here?
  */
 
 #include <config.h>
-#include <setjmp.h>
 #include "lisp.h"
-
+#include "lstream.h"
 #include "console-x.h"
 #include "glyphs-x.h"
 #include "objects-x.h"
@@ -57,17 +55,20 @@
 
 #include "sysfile.h"
 
-#ifdef HAVE_IMAGEMAGICK
-#define _XOS_H_
-#ifdef MAGICK_HEADERS_ARE_UNDER_X11
-#include <X11/magick/magick.h>
+#ifdef HAVE_PNG
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <png.h>
+#ifdef __cplusplus
+}
+#endif
 #else
-#include <magick/magick.h>
+#include <setjmp.h>
 #endif
-/*#include <image.h>*/
-/*#include <assert.h>*/
-
-#define OLDCOMPAT /* allow lisp code using the old names to still function */
+
+#ifdef MULE
+#include "mule-coding.h"
 #endif
 
 #define LISP_DEVICE_TO_X_SCREEN(dev)					\
@@ -90,20 +91,24 @@
 Lisp_Object Qxface;
 #endif
 
-#ifdef HAVE_IMAGEMAGICK
-DEFINE_IMAGE_INSTANTIATOR_FORMAT (imagick);
-Lisp_Object Qimagick;
-
-#ifdef OLDCOMPAT /* old compatibility */
+#ifdef HAVE_TIFF
 DEFINE_IMAGE_INSTANTIATOR_FORMAT (tiff);
-DEFINE_IMAGE_INSTANTIATOR_FORMAT (png);
-DEFINE_IMAGE_INSTANTIATOR_FORMAT (gif);
+Lisp_Object Qtiff;
+#endif
+
+#ifdef HAVE_JPEG
 DEFINE_IMAGE_INSTANTIATOR_FORMAT (jpeg);
-Lisp_Object Qtiff;
-Lisp_Object Qpng;
-Lisp_Object Qgif;
 Lisp_Object Qjpeg;
 #endif
+
+#ifdef HAVE_GIF
+DEFINE_IMAGE_INSTANTIATOR_FORMAT (gif);
+Lisp_Object Qgif;
+#endif
+
+#ifdef HAVE_PNG
+DEFINE_IMAGE_INSTANTIATOR_FORMAT (png);
+Lisp_Object Qpng;
 #endif
 
 DEFINE_IMAGE_INSTANTIATOR_FORMAT (cursor_font);
@@ -439,26 +444,36 @@
 static void
 write_lisp_string_to_temp_file (Lisp_Object string, char *filename_out)
 {
-  Extbyte *bytes;
-  Extcount len;
-  FILE *stream;
-
-  /* #### This is a definite problem under Mule due to the amount of
-     stack data it might allocate.  Need to be able to convert and
-     write out to a file. */
-  GET_STRING_BINARY_DATA_ALLOCA (string, bytes, len);
-
-  /* Write out to a temporary file ... */
+  Lisp_Object instream, outstream;
+  Lstream *istr, *ostr;
+  char tempbuf[1024]; /* some random amount */
+  int fubar = 0;
+  FILE *tmpfil;
+  static Extbyte_dynarr *conversion_out_dynarr = NULL;
+  Bytecount bstart, bend;
+  struct gcpro gcpro1, gcpro2;
+#ifdef MULE
+  Lisp_Object conv_out_stream;
+  Lstream *costr;
+  struct gcpro gcpro3;
+#endif
+
+  /* This function can GC */
+  if (!conversion_out_dynarr)
+    conversion_out_dynarr = Dynarr_new (Extbyte);
+  else
+    Dynarr_reset (conversion_out_dynarr);
+
+  /* Create the temporary file ... */
   sprintf (filename_out, "/tmp/emacs%d.XXXXXX", (int) getpid ());
   mktemp (filename_out);
-  stream = fopen (filename_out, "w");
-  if (!stream)
+  tmpfil = fopen (filename_out, "w");
+  if (!tmpfil)
     {
-    temp_file_error:
-      if (stream)
+      if (tmpfil)
 	{
 	  int old_errno = errno;
-	  fclose (stream);
+	  fclose (tmpfil);
 	  unlink (filename_out);
 	  errno = old_errno;
 	}
@@ -466,14 +481,63 @@
 			 list1 (build_string (filename_out)));
     }
 
-  if (fwrite (bytes, len, 1, stream) != 1)
-    goto temp_file_error;
-
-  if (fclose (stream) != 0)
-    {
-      stream = 0;
-      goto temp_file_error;
-    }
+  CHECK_STRING (string);
+  get_string_range_byte (string, Qnil, Qnil, &bstart, &bend,
+			 GB_HISTORICAL_STRING_BEHAVIOR);
+  instream = make_lisp_string_input_stream (string, bstart, bend);
+  istr = XLSTREAM (instream);
+  /* setup the out stream */
+  outstream = make_dynarr_output_stream((unsigned_char_dynarr *)conversion_out_dynarr);
+  ostr = XLSTREAM (outstream);
+#ifdef MULE
+  /* setup the conversion stream */
+  conv_out_stream = make_encoding_output_stream (ostr, Fget_coding_system(Qbinary));
+  costr = XLSTREAM (conv_out_stream);
+  GCPRO3 (instream, outstream, conv_out_stream);
+#else
+  GCPRO2 (instream, outstream);
+#endif
+
+  /* Get the data while doing the conversion */
+  while (1) {
+    int size_in_bytes = Lstream_read (istr, tempbuf, sizeof (tempbuf));
+    if (!size_in_bytes)
+      break;
+    /* It does seem the flushes are necessary... */
+#ifdef MULE
+    Lstream_write (costr, tempbuf, size_in_bytes);
+    Lstream_flush (costr);
+#else
+    Lstream_write (ostr, tempbuf, size_in_bytes);
+#endif
+    Lstream_flush (ostr);
+    if (fwrite ((unsigned char *)Dynarr_atp(conversion_out_dynarr, 0),
+		Dynarr_length(conversion_out_dynarr), 1, tmpfil) != 1)
+      {
+	fubar = 1;
+	break;
+      }
+    /* reset the dynarr */
+    Lstream_rewind(ostr);
+  }
+  
+  if (fclose (tmpfil) != 0)
+    fubar = 1;
+  Lstream_close (istr);
+#ifdef MULE
+  Lstream_close (costr);
+#endif
+  Lstream_close (ostr);
+  
+  UNGCPRO;
+  Lstream_delete (istr);
+  Lstream_delete (ostr);
+#ifdef MULE
+  Lstream_delete (costr);
+#endif
+  if (fubar)
+    report_file_error ("Writing temp file",
+		       list1 (build_string (filename_out)));
 }
 
 
@@ -1383,8 +1447,6 @@
     signal_simple_error ("Not an X device", device);
 
   dpy = DEVICE_X_DISPLAY (XDEVICE (device));
-  cmap = DEVICE_X_COLORMAP (XDEVICE(device));
-  depth = DEVICE_X_DEPTH (XDEVICE(device));
   xs = DefaultScreenOfDisplay (dpy);
 
   if (dest_mask & IMAGE_COLOR_PIXMAP_MASK)
@@ -1399,6 +1461,26 @@
 			      | IMAGE_POINTER_MASK);
   force_mono = (type != IMAGE_COLOR_PIXMAP);
 
+#if 0
+  /* Although I haven't found it documented yet, it appears that pointers are
+     always colored via the default window colormap... Sigh.  However, with
+     the current color structure, this will blow the doors off as things aren't set up
+     to differenciate between two colormaps per console.  AARGH! */
+  if (type == IMAGE_POINTER)
+    {
+      cmap = DefaultColormap(dpy, DefaultScreen(dpy));
+      depth = DefaultDepthOfScreen (xs);
+    }
+  else
+    {
+      cmap = DEVICE_X_COLORMAP (XDEVICE(device));
+      depth = DEVICE_X_DEPTH (XDEVICE(device));
+    }
+#else
+  cmap = DEVICE_X_COLORMAP (XDEVICE(device));
+  depth = DEVICE_X_DEPTH (XDEVICE(device));
+#endif
+
   x_initialize_pixmap_image_instance (ii, type);
 
   assert (!NILP (data));
@@ -1675,148 +1757,915 @@
 #endif /* HAVE_XPM */
 
 
-#ifdef HAVE_IMAGEMAGICK
+#ifdef HAVE_JPEG
+
 /**********************************************************************
- *                             ImageMagick                            *
+ *                             JPEG                                   *
  **********************************************************************/
-JMP_BUF imagick_jump;
-Lisp_Object imagick_err; /* slightly hackish way to return a proper error message */
-
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <jpeglib.h>
+#include <jerror.h>
+#ifdef __cplusplus
+}
+#endif
+
+/*#define USE_TEMP_FILES_FOR_JPEG_IMAGES 1*/
 static void
-imagick_validate (Lisp_Object instantiator)
-{
-	file_or_data_must_be_present (instantiator);
-}
-
-static void
-imagick_error_handler (const char *message,const char *qualifier)
+jpeg_validate (Lisp_Object instantiator)
 {
-  /* ImageMagick defaults to exiting on errors, which is an anti-thing.
-   * Dump the info into imagick_err, and jmp back */
-  if (qualifier != NULL)
-    imagick_err = emacs_doprnt_string_c((CONST Bufbyte *) GETTEXT ("ImageMagick error: %s (%s)"),
-					Qnil, -1, message, qualifier);
-  else
-    imagick_err = emacs_doprnt_string_c((CONST Bufbyte *) GETTEXT ("ImageMagick error: %s"),
-					Qnil, -1, message);
-  LONGJMP(imagick_jump,1);
-}
-
-static void
-imagick_warning_handler (const char *message,const char *qualifier)
-{
-  if (qualifier != NULL)
-    warn_when_safe(Qimagick, Qwarning, "ImageMagick warning: %s (%s)",message,qualifier);
-  else
-    warn_when_safe(Qimagick, Qwarning, "ImageMagick warning: %s",message);
+  file_or_data_must_be_present (instantiator);
 }
 
 static Lisp_Object
-imagick_normalize (Lisp_Object inst, Lisp_Object console_type)
+jpeg_normalize (Lisp_Object inst, Lisp_Object console_type)
 {
-	return simple_image_type_normalize (inst, console_type, Qimagick);
+  return simple_image_type_normalize (inst, console_type, Qjpeg);
 }
 
 static int
-imagick_possible_dest_types (void)
+jpeg_possible_dest_types (void)
 {
-	return IMAGE_COLOR_PIXMAP_MASK;
+  return IMAGE_COLOR_PIXMAP_MASK;
 }
 
-struct imagick_unwind_data
+/* To survive the otherwise baffling complexity of making sure
+   everything gets cleaned up in the presence of an error, we
+   use an unwind_protect(). */
+
+struct jpeg_unwind_data
 {
-	Display *dpy;
-	Colormap cmap;
-	FILE *instream;
-	Image *image;
-	XImage *ximage;
-	unsigned long *pixels;
-	unsigned long npixels;
-	char tempfile[50];
-	int tempfile_needs_to_be_removed;
+  Display *dpy;
+  Colormap cmap;
+  /* Stream that we need to close */
+  FILE *instream;
+  /* Object that holds state info for JPEG decoding */
+  struct jpeg_decompress_struct *cinfo_ptr;
+  /* Pixels to keep around while the image is active */
+  unsigned long *pixels;
+  int npixels;
+  /* Client-side image structure */
+  XImage *ximage;
+  /* Tempfile to remove */
+  char tempfile[50];
+  int tempfile_needs_to_be_removed;
 };
 
 static Lisp_Object
-imagick_instantiate_unwind (Lisp_Object unwind_obj)
+jpeg_instantiate_unwind (Lisp_Object unwind_obj)
+{
+  struct jpeg_unwind_data *data =
+    (struct jpeg_unwind_data *) get_opaque_ptr (unwind_obj);
+
+  free_opaque_ptr (unwind_obj);
+  if (data->cinfo_ptr)
+    jpeg_destroy_decompress (data->cinfo_ptr);
+
+  if (data->instream)
+    fclose (data->instream);
+
+  if (data->tempfile_needs_to_be_removed)
+    unlink (data->tempfile);
+
+  if (data->npixels > 0)
+    {
+      XFreeColors (data->dpy, data->cmap, data->pixels, data->npixels, 0L);
+      xfree (data->pixels);
+    }
+
+  if (data->ximage)
+    {
+      if (data->ximage->data)
+        {
+	  xfree (data->ximage->data);
+          data->ximage->data = 0;
+        }
+      XDestroyImage (data->ximage);
+    }
+
+  return Qnil;
+}
+
+/*
+ * ERROR HANDLING:
+ *
+ * The JPEG library's standard error handler (jerror.c) is divided into
+ * several "methods" which you can override individually.  This lets you
+ * adjust the behavior without duplicating a lot of code, which you might
+ * have to update with each future release.
+ *
+ * Our example here shows how to override the "error_exit" method so that
+ * control is returned to the library's caller when a fatal error occurs,
+ * rather than calling exit() as the standard error_exit method does.
+ *
+ * We use C's setjmp/longjmp facility to return control.  This means that the
+ * routine which calls the JPEG library must first execute a setjmp() call to
+ * establish the return point.  We want the replacement error_exit to do a
+ * longjmp().  But we need to make the setjmp buffer accessible to the
+ * error_exit routine.  To do this, we make a private extension of the
+ * standard JPEG error handler object.  (If we were using C++, we'd say we
+ * were making a subclass of the regular error handler.)
+ *
+ * Here's the extended error handler struct:
+ */
+
+struct my_jpeg_error_mgr
 {
-	struct imagick_unwind_data *data =
-		(struct imagick_unwind_data *) get_opaque_ptr (unwind_obj);
- 
-	free_opaque_ptr (unwind_obj);
-	if (data->instream)
-		fclose (data->instream);
-	if (data->tempfile_needs_to_be_removed)
-		unlink (data->tempfile);
-
-	if (data->image) {
-		DestroyImage(data->image);
-	}
-
-	if (data->ximage) {
-		if (data->ximage->data) {
-			xfree (data->ximage->data);
-			data->ximage->data = NULL;
-		}
-		XDestroyImage (data->ximage);
-	}
-
-	if (data->npixels > 0) {
-	  XFreeColors(data->dpy, data->cmap, data->pixels, data->npixels, 0L);
-	  xfree (data->pixels);
-	}
- 
-	return Qnil;
+  struct jpeg_error_mgr pub;	/* "public" fields */
+  jmp_buf setjmp_buffer;	/* for return to caller */
+};
+
+#if defined(JPEG_LIB_VERSION) && (JPEG_LIB_VERSION >= 61)
+METHODDEF(void)
+#else
+METHODDEF void
+#endif
+our_init_source (j_decompress_ptr cinfo) {
+}
+
+#if defined(JPEG_LIB_VERSION) && (JPEG_LIB_VERSION >= 61)
+METHODDEF(boolean)
+#else
+METHODDEF boolean
+#endif
+our_fill_input_buffer (j_decompress_ptr cinfo) {
+  /* Insert a fake EOI marker */
+  struct jpeg_source_mgr *src = (struct jpeg_source_mgr *) 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;
 }
 
+#if defined(JPEG_LIB_VERSION) && (JPEG_LIB_VERSION >= 61)
+METHODDEF(void)
+#else
+METHODDEF void
+#endif
+our_skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
+  struct jpeg_source_mgr *src = NULL;
+
+  src = (struct jpeg_source_mgr *) cinfo->src;
+
+  if (!src) {
+    return;
+  } else if (num_bytes > src->bytes_in_buffer) {
+    ERREXIT(cinfo, JERR_INPUT_EOF);
+    /*NOTREACHED*/
+  }
+
+  src->bytes_in_buffer -= num_bytes;
+  src->next_input_byte += num_bytes;
+}
+
+#if defined(JPEG_LIB_VERSION) && (JPEG_LIB_VERSION >= 61)
+METHODDEF(void)
+#else
+METHODDEF void
+#endif
+our_term_source (j_decompress_ptr cinfo) {
+}
+
+typedef struct {
+  struct jpeg_source_mgr pub;
+} our_jpeg_source_mgr;
+
 static void
-imagick_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
-					 Lisp_Object pointer_fg, Lisp_Object pointer_bg,
-					 int dest_mask, Lisp_Object domain)
+jpeg_memory_src (j_decompress_ptr cinfo, JOCTET *data, unsigned int len)
+{
+  struct jpeg_source_mgr *src = NULL;
+
+  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(our_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; /* use default method */
+  src->term_source = our_term_source;
+  src->bytes_in_buffer = len;
+  src->next_input_byte = data;
+}
+
+#if defined(JPEG_LIB_VERSION) && (JPEG_LIB_VERSION >= 61)
+METHODDEF(void)
+#else
+METHODDEF void
+#endif
+my_jpeg_error_exit (j_common_ptr cinfo)
+{
+  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
+  struct my_jpeg_error_mgr *myerr = (struct my_jpeg_error_mgr *) cinfo->err;
+
+  /* Return control to the setjmp point */
+  longjmp (myerr->setjmp_buffer, 1);
+}
+
+/* The code in this routine is based on example.c from the JPEG library
+   source code and from gif_instantiate() */
+static void
+jpeg_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
+		  Lisp_Object pointer_fg, Lisp_Object pointer_bg,
+		  int dest_mask, Lisp_Object domain)
 {
   struct Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
   Lisp_Object device = IMAGE_INSTANCE_DEVICE (ii);
   Display *dpy;
   Screen *scr;
-  Visual *visual;
   Colormap cmap;
-  Dimension depth;
-  struct imagick_unwind_data unwind;
-  int speccount;
-  ImageInfo image_info;
-
-  /* ImageMagick variables */
-
-  /* Basic error checking */
+  Visual *vis;
+  /* It is OK for the unwind data to be local to this function,
+     because the unwind-protect is always executed when this
+     stack frame is still valid. */
+  struct jpeg_unwind_data unwind;
+  int speccount = specpdl_depth ();
+
+  /* This struct contains the JPEG decompression parameters and pointers to
+   * working space (which is allocated as needed by the JPEG library).
+   */
+  struct jpeg_decompress_struct cinfo;
+  /* We use our private extension JPEG error handler.
+   * Note that this struct must live as long as the main JPEG parameter
+   * struct, to avoid dangling-pointer problems.
+   */
+  struct my_jpeg_error_mgr jerr;
+
   if (!DEVICE_X_P (XDEVICE (device)))
     signal_simple_error ("Not an X device", device);
 
   dpy = DEVICE_X_DISPLAY (XDEVICE (device));
   scr = DefaultScreenOfDisplay (dpy);
-  depth = DEVICE_X_DEPTH (XDEVICE (device));
-  visual = DEVICE_X_VISUAL (XDEVICE (device));
   cmap = DEVICE_X_COLORMAP (XDEVICE(device));
-
-  /* Set up the unwind */
+  vis = DEVICE_X_VISUAL (XDEVICE(device));
+
+  /* Step -1: First record our unwind-protect, which will clean up after
+     any exit, normal or not */
+
   memset (&unwind, 0, sizeof (unwind));
   unwind.dpy = dpy;
   unwind.cmap = cmap;
-  speccount = specpdl_depth();
-  record_unwind_protect(imagick_instantiate_unwind,make_opaque_ptr(&unwind));
-
-  /* Set up error handlers */
-  if (SETJMP(imagick_jump)) 
+  record_unwind_protect (jpeg_instantiate_unwind, make_opaque_ptr (&unwind));
+
+#ifdef USE_TEMP_FILES_FOR_JPEG_IMAGES
+  /* Step 0: Write out to a temp file.
+
+     The JPEG routines require you to read from a file unless
+     you provide your own special input handlers, which I don't
+     feel like doing. */
+  {
+    Lisp_Object data = find_keyword_in_vector (instantiator, Q_data);
+
+    assert (!NILP (data));
+
+    write_lisp_string_to_temp_file (data, unwind.tempfile);
+    unwind.tempfile_needs_to_be_removed = 1;
+
+    /* VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
+     * requires it in order to read binary files.
+     */
+
+    if ((unwind.instream = fopen (unwind.tempfile, "r")) == NULL)
+      report_file_error ("Opening JPEG temp file",
+			 list1 (build_string (unwind.tempfile)));
+  }
+#endif
+
+  /* Step 1: allocate and initialize JPEG decompression object */
+
+  /* We set up the normal JPEG error routines, then override error_exit. */
+  cinfo.err = jpeg_std_error (&jerr.pub);
+  jerr.pub.error_exit = my_jpeg_error_exit;
+
+  /* Establish the setjmp return context for my_error_exit to use. */
+  if (setjmp (jerr.setjmp_buffer))
     {
-      /* signal error GCPROs it's arguments */
-      signal_error(Qerror, list2(imagick_err, instantiator));
+      /* If we get here, the JPEG code has signaled an error.
+       * We need to clean up the JPEG object, close the input file, and return.
+       */
+
+      {
+	Lisp_Object errstring;
+	char buffer[JMSG_LENGTH_MAX];
+
+	/* Create the message */
+	(*cinfo.err->format_message) ((j_common_ptr) &cinfo, buffer);
+	errstring = build_string (buffer);
+
+	signal_simple_error_2 ("JPEG decoding error",
+			       errstring, instantiator);
+      }
+    }
+
+  /* Now we can initialize the JPEG decompression object. */
+  jpeg_create_decompress (&cinfo);
+  unwind.cinfo_ptr = &cinfo;
+
+  /* Step 2: specify data source (eg, a file) */
+
+#ifdef USE_TEMP_FILES_FOR_JPEG_IMAGES
+  jpeg_stdio_src (&cinfo, unwind.instream);
+#else
+  {
+    Lisp_Object data = find_keyword_in_vector (instantiator, Q_data);
+    Extbyte *bytes;
+    Extcount len;
+
+    /* #### This is a definite problem under Mule due to the amount of
+       stack data it might allocate.  Need to be able to convert and
+       write out to a file. */
+    GET_STRING_BINARY_DATA_ALLOCA (data, bytes, len);
+    jpeg_memory_src (&cinfo, bytes, len);
+  }
+#endif
+
+  /* Step 3: read file parameters with jpeg_read_header() */
+
+  jpeg_read_header (&cinfo, TRUE);
+  /* We can ignore the return value from jpeg_read_header since
+   *   (a) suspension is not possible with the stdio data source, and
+   *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
+   * See libjpeg.doc for more info.
+   */
+
+  /* Step 4: set parameters for decompression.   */
+
+  /* We request that the JPEG file be automatically quantized into
+     8-bit color in case it's not already (many JPEGs are stored in
+     24-bit color).  "Two-pass quantize" means that the colormap
+     is determined on-the-fly for this particular image rather than
+     quantizing to a supplied colormap.  We can get away with this
+     because we then use allocate_nearest_color().
+
+     #### Note of course that this is not the most color-effective
+     way of doing things -- we could quantize an image that has
+     lots of very similar colors, and eat up the colormap with these
+     (useless to other images) colors.  Unfortunately I don't think
+     there's any "general" way of maximizing the overall image
+     quality of lots of images, given that we don't know the
+     colors of the images until we come across each one.  Best we
+     could do would be various sorts of heuristics, which I don't
+     feel like dealing with now.  A better scheme would be the
+     way things are done under MS Windows, where the colormap is
+     dynamically adjusted for various applications; but that kind
+     of thing would have to be provided by X, which it isn't. */
+
+  cinfo.quantize_colors = TRUE;
+  cinfo.two_pass_quantize = TRUE;
+  cinfo.colormap = NULL;
+
+  /* Step 5: Start decompressor */
+
+  jpeg_start_decompress (&cinfo);
+  /* We can ignore the return value since suspension is not possible
+   * with the stdio data source.
+   */
+
+  /* At this point we know the size of the image and the colormap. */
+
+  /* Step 5.33: Allocate the colors */
+  {
+    int i;
+
+    /* Just in case the image contains out-of-range pixels, we go
+       ahead and allocate space for all of them. */
+    unwind.pixels = xnew_array (unsigned long, 256);
+    unwind.npixels = cinfo.actual_number_of_colors;
+
+    for (i = 0; i < 256; i++)
+      unwind.pixels[i] = 0;   /* Use a reasonable color for out of range. */
+
+    /* Allocate pixels for the various colors. */
+    for (i = 0; i < unwind.npixels; i++)
+      {
+	XColor color;
+	int ri, gi, bi;
+
+	ri = 0;
+	gi = cinfo.out_color_components > 1 ? 1 : 0;
+	bi = cinfo.out_color_components > 2 ? 2 : 0;
+
+	/* Ok... apparently, an entry of cinfo.colormap can be NULL if
+	   there are no bits of that color in the image.  How incredibly
+	   gross.  Wouldn't it be nice to have exceptions!? */
+	color.red = cinfo.colormap[ri] ? cinfo.colormap[ri][i] << 8 : 0;
+	color.green = cinfo.colormap[gi] ? cinfo.colormap[gi][i] << 8 : 0;
+	color.blue = cinfo.colormap[bi] ? cinfo.colormap[bi][i] << 8 : 0;
+	color.flags = DoRed | DoGreen | DoBlue;
+
+	allocate_nearest_color (dpy, cmap, vis, &color);
+	unwind.pixels[i] = color.pixel;
+      }
+  }
+
+  /* Step 5.66: Create the image */
+  {
+    int height = cinfo.output_height;
+    int width = cinfo.output_width;
+    int depth;
+    int bitmap_pad;
+
+    depth = DEVICE_X_DEPTH(XDEVICE(device));
+
+    /* first get bitmap_pad (from XPM) */
+    bitmap_pad = ((depth > 16) ? 32 :
+		  (depth >  8) ? 16 :
+		  8);
+
+    unwind.ximage = XCreateImage (dpy, DefaultVisualOfScreen (scr),
+				  depth, ZPixmap, 0, 0, width, height,
+				  bitmap_pad, 0);
+
+    if (!unwind.ximage)
+      signal_simple_error ("Unable to create X image struct", instantiator);
+
+    /* now that bytes_per_line must have been set properly alloc data */
+    unwind.ximage->data =
+      (char *) xmalloc (unwind.ximage->bytes_per_line * height);
+  }
+
+  /* Step 6: Read in the data and put into image */
+  {
+    JSAMPARRAY row_buffer;	/* Output row buffer */
+    int row_stride;		/* physical row width in output buffer */
+
+    /* We may need to do some setup of our own at this point before reading
+     * the data.  After jpeg_start_decompress() we have the correct scaled
+     * output image dimensions available, as well as the output colormap
+     * if we asked for color quantization.
+     * In this example, we need to make an output work buffer of the right size.
+     */
+    /* JSAMPLEs per row in output buffer.
+       Since we asked for quantized output, cinfo.output_components
+       will always be 1. */
+    row_stride = cinfo.output_width * cinfo.output_components;
+    /* Make a one-row-high sample array that will go away when done
+       with image */
+    row_buffer = ((*cinfo.mem->alloc_sarray)
+		  ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1));
+
+    /* Here we use the library's state variable cinfo.output_scanline as the
+     * loop counter, so that we don't have to keep track ourselves.
+     */
+    while (cinfo.output_scanline < cinfo.output_height)
+      {
+	int i;
+	int scanline = cinfo.output_scanline;
+
+	/* jpeg_read_scanlines expects an array of pointers to scanlines.
+	 * Here the array is only one element long, but you could ask for
+	 * more than one scanline at a time if that's more convenient.
+	 */
+	(void) jpeg_read_scanlines (&cinfo, row_buffer, 1);
+
+	for (i = 0; i < cinfo.output_width; i++)
+	  XPutPixel (unwind.ximage, i, scanline,
+		     /* Let's make sure we avoid getting bit like
+			what happened for GIF's.  It's probably the
+			case that JSAMPLE's are unsigned chars as
+			opposed to chars, but you never know.
+
+			(They could even be shorts if the library
+			was compiled with 12-bit samples -- ####
+			We should deal with this possibility) */
+		     unwind.pixels[(unsigned char) row_buffer[0][i]]);
+      }
+  }
+
+  /* Step 6.5: Create the pixmap and set up the image instance */
+  init_image_instance_from_x_image (ii, unwind.ximage, dest_mask,
+				    unwind.pixels, unwind.npixels,
+				    instantiator);
+
+  /* Step 7: Finish decompression */
+
+  jpeg_finish_decompress (&cinfo);
+  /* We can ignore the return value since suspension is not possible
+   * with the stdio data source.
+   */
+
+  /* And we're done!
+
+     Now that we've succeeded, we don't want the pixels
+     freed right now.  They're kept around in the image instance
+     structure until it's destroyed. */
+  unwind.npixels = 0;
+
+  /* This will clean up everything else. */
+  unbind_to (speccount, Qnil);
+}
+
+#endif /* HAVE_JPEG */
+
+#ifdef HAVE_GIF
+
+/**********************************************************************
+ *                               GIF                                  *
+ **********************************************************************/
+
+#include "gif_lib.h" /* This is in our own source tree */
+
+static void
+gif_validate (Lisp_Object instantiator)
+{
+  file_or_data_must_be_present (instantiator);
+}
+
+static Lisp_Object
+gif_normalize (Lisp_Object inst, Lisp_Object console_type)
+{
+  return simple_image_type_normalize (inst, console_type, Qgif);
+}
+
+static int
+gif_possible_dest_types (void)
+{
+  return IMAGE_COLOR_PIXMAP_MASK;
+}
+
+/* To survive the otherwise baffling complexity of making sure
+   everything gets cleaned up in the presence of an error, we
+   use an unwind_protect(). */
+
+struct gif_unwind_data
+{
+  Display *dpy;
+  Colormap cmap;
+  /* Object that holds the decoded data from a GIF file */
+  GifFileType *giffile;
+  /* Pixels to keep around while the image is active */
+  unsigned long *pixels;
+  int npixels;
+  /* Client-side image structure */
+  XImage *ximage;
+  /* Tempfile to remove */
+  char tempfile[50];
+  int tempfile_needs_to_be_removed;
+};
+
+static Lisp_Object
+gif_instantiate_unwind (Lisp_Object unwind_obj)
+{
+  struct gif_unwind_data *data =
+    (struct gif_unwind_data *) get_opaque_ptr (unwind_obj);
+
+  free_opaque_ptr (unwind_obj);
+  if (data->giffile)
+    DGifCloseFile (data->giffile);
+  if (data->tempfile_needs_to_be_removed)
+    unlink (data->tempfile);
+  if (data->npixels > 0)
+    {
+      XFreeColors (data->dpy, data->cmap, data->pixels, data->npixels, 0L);
+      xfree (data->pixels);
+    }
+  if (data->ximage)
+    {
+      if (data->ximage->data)
+        {
+	  xfree (data->ximage->data);
+          data->ximage->data = 0;
+        }
+      XDestroyImage (data->ximage);
     }
-  
-  SetErrorHandler(imagick_error_handler);
-  SetWarningHandler(imagick_warning_handler);
-
-  /* Write out to a temp file - not sure if ImageMagick supports the
-  ** notion of an abstract 'data source' right now.
-  ** JH: It doesn't as of 3.9.3
-  */
+
+  return Qnil;
+}
+
+static void
+gif_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
+		 Lisp_Object pointer_fg, Lisp_Object pointer_bg,
+		 int dest_mask, Lisp_Object domain)
+{
+  struct Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+  Lisp_Object device = IMAGE_INSTANCE_DEVICE (ii);
+  Display *dpy;
+  Screen *scr;
+  Colormap cmap;
+  Visual *vis;
+  /* It is OK for the unwind data to be local to this function,
+     because the unwind-protect is always executed when this
+     stack frame is still valid. */
+  struct gif_unwind_data unwind;
+  int speccount = specpdl_depth ();
+
+  if (!DEVICE_X_P (XDEVICE (device)))
+    signal_simple_error ("Not an X device", device);
+
+  dpy = DEVICE_X_DISPLAY (XDEVICE (device));
+  scr = DefaultScreenOfDisplay (dpy);
+  cmap = DEVICE_X_COLORMAP (XDEVICE(device));
+  vis = DEVICE_X_VISUAL (XDEVICE(device));
+
+  memset (&unwind, 0, sizeof (unwind));
+  unwind.dpy = dpy;
+  unwind.cmap = cmap;
+  record_unwind_protect (gif_instantiate_unwind, make_opaque_ptr (&unwind));
+
+  /* 1. Now decode the data. */
+
+  /* #### The GIF routines currently require that you read from a file,
+     so write out to a temp file.  We should change this. */
+  {
+    Lisp_Object data = find_keyword_in_vector (instantiator, Q_data);
+
+    assert (!NILP (data));
+
+    write_lisp_string_to_temp_file (data, unwind.tempfile);
+    unwind.tempfile_needs_to_be_removed = 1;
+
+    /* Then slurp the image into memory, decoding along the way.
+       The result is the image in a simple one-byte-per-pixel
+       format (#### the GIF routines only support 8-bit GIFs,
+       it appears). */
+    unwind.giffile = DGifOpenFileName (unwind.tempfile);
+    if (unwind.giffile == NULL)
+      {
+      gif_decode_error:
+	signal_simple_error ("Unable to decode GIF",
+			     build_string (EmacsPrintGifError ()));
+      }
+    /* DGifSlurp() doesn't handle interlaced files. */
+    /* Actually, it does, sort of.  It just sets the Interlace flag
+       and stores RasterBits in interlaced order.  We handle that below. */
+    if (DGifSlurp (unwind.giffile) != GIF_OK)
+      goto gif_decode_error;
+  }
+
+  /* 2. Now allocate the colors for the image. */
+  {
+    int i;
+    ColorMapObject *cmo = unwind.giffile->SColorMap;
+    /* Just in case the image contains out-of-range pixels, we go
+       ahead and allocate space for all of them. */
+    unwind.pixels = xnew_array (unsigned long, 256);
+    unwind.npixels = cmo->ColorCount;
+
+    for (i = 0; i < 256; i++)
+      unwind.pixels[i] = 0;   /* Use a reasonable color for out of range. */
+
+    /* Allocate pixels for the various colors. */
+    for (i = 0; i < cmo->ColorCount; i++)
+      {
+	XColor color;
+
+	color.red = cmo->Colors[i].Red << 8;
+	color.green = cmo->Colors[i].Green << 8;
+	color.blue = cmo->Colors[i].Blue << 8;
+	color.flags = DoRed | DoGreen | DoBlue;
+
+	allocate_nearest_color (dpy, cmap, vis, &color);
+	unwind.pixels[i] = color.pixel;
+      }
+  }
+
+  /* 3. Now create the image */
+  {
+    int height = unwind.giffile->SHeight;
+    int width = unwind.giffile->SWidth;
+    int depth;
+    int bitmap_pad;
+    int i, j, row, pass, interlace;
+    /* interlaced gifs have rows in this order:
+       0, 8, 16, ..., 4, 12, 20, ..., 2, 6, 10, ..., 1, 3, 5, ...  */
+    static int InterlacedOffset[] = { 0, 4, 2, 1 };
+    static int InterlacedJumps[] = { 8, 8, 4, 2 };
+
+    depth = DEVICE_X_DEPTH(XDEVICE(device));
+
+    /* first get bitmap_pad (from XPM) */
+    bitmap_pad = ((depth > 16) ? 32 :
+		  (depth >  8) ? 16 :
+		  8);
+
+    unwind.ximage = XCreateImage (dpy, vis,
+				  depth, ZPixmap, 0, 0, width, height,
+				  bitmap_pad, 0);
+
+    if (!unwind.ximage)
+      signal_simple_error ("Unable to create X image struct", instantiator);
+
+    /* now that bytes_per_line must have been set properly alloc data */
+    unwind.ximage->data =
+      (char *) xmalloc (unwind.ximage->bytes_per_line * height);
+
+    /* write the data --
+       #### XPutPixel() is a client-side-only function but could
+       still be slow.  Another possibility is to just convert to
+       XPM format and use the Xpm routines, which optimize this
+       stuff; but it's doubtful that this will be faster in the
+       long run, what with all the XPM overhead.  If this proves
+       to be a bottleneck here, maybe we should just copy the
+       optimization routines from XPM (they're in turn mostly
+       copied from the Xlib source code). */
+
+    /* Note: We just use the first image in the file and ignore the rest.
+             We check here that that image covers the full "screen" size.
+	     I don't know whether that's always the case.
+             -dkindred@cs.cmu.edu  */
+    if (unwind.giffile->SavedImages[0].ImageDesc.Height != height
+	|| unwind.giffile->SavedImages[0].ImageDesc.Width != width
+	|| unwind.giffile->SavedImages[0].ImageDesc.Left != 0
+	|| unwind.giffile->SavedImages[0].ImageDesc.Top != 0)
+      signal_simple_error ("First image in GIF file is not full size",
+			   instantiator);
+
+    interlace = unwind.giffile->SavedImages[0].ImageDesc.Interlace;
+    pass = 0;
+    row = interlace ? InterlacedOffset[pass] : 0;
+    for (i = 0; i < height; i++)
+      {
+	if (interlace && row >= height)
+	  row = InterlacedOffset[++pass];
+
+	for (j = 0; j < width; j++)
+	  XPutPixel (unwind.ximage, j, row,
+		     unwind.pixels[(unsigned char)
+				  /* incorrect signed declaration
+				     of RasterBits[] */
+				  (unwind.giffile->SavedImages[0].
+				   RasterBits[i * width + j])]);
+
+	row += interlace ? InterlacedJumps[pass] : 1;
+      }
+  }
+
+  /* 4. Now create the pixmap and set up the image instance */
+  init_image_instance_from_x_image (ii, unwind.ximage, dest_mask,
+				    unwind.pixels, unwind.npixels,
+				    instantiator);
+  /* Now that we've succeeded, we don't want the pixels
+     freed right now.  They're kept around in the image instance
+     structure until it's destroyed. */
+  unwind.npixels = 0;
+  unbind_to (speccount, Qnil);
+}
+
+#endif /* HAVE_GIF */
+
+
+#ifdef HAVE_PNG
+/* #define USE_TEMP_FILES_FOR_PNG_IMAGES 1 */
+
+/**********************************************************************
+ *                             PNG                                    *
+ **********************************************************************/
+static void
+png_validate (Lisp_Object instantiator)
+{
+  file_or_data_must_be_present (instantiator);
+}
+
+static Lisp_Object
+png_normalize (Lisp_Object inst, Lisp_Object console_type)
+{
+  return simple_image_type_normalize (inst, console_type, Qpng);
+}
+
+static int
+png_possible_dest_types (void)
+{
+  return IMAGE_COLOR_PIXMAP_MASK;
+}
+
+#if !defined (USE_TEMP_FILES_FOR_PNG_IMAGES) && (PNG_LIBPNG_VER >= 87)
+struct png_memory_storage
+{
+  Extbyte *bytes;		/* The data       */
+  Extcount len;			/* How big is it? */
+  int index;			/* Where are we?  */
+};
+
+static void png_read_from_memory(png_structp png_ptr, png_bytep data,
+				 png_uint_32 length)
+{
+   struct png_memory_storage *tbr =
+     (struct png_memory_storage *) png_get_io_ptr (png_ptr);
+
+   if (length > (tbr->len - tbr->index))
+     png_error (png_ptr, (png_const_charp) "Read Error");
+   memcpy(data,tbr->bytes + tbr->index,length);
+   tbr->index = tbr->index + length;
+}
+#endif /* !USE_TEMP_FILES_FOR_PNG_IMAGESS || PNG_LIBPNG_VER >= 87 */
+
+struct png_unwind_data
+{
+  Display *dpy;
+  Colormap cmap;
+  FILE *instream;
+  png_struct *png_ptr;
+  png_info *info_ptr;
+  unsigned long *pixels;
+  int npixels;
+  XImage *ximage;
+  char tempfile[50];
+  int tempfile_needs_to_be_removed;
+};
+
+static Lisp_Object
+png_instantiate_unwind (Lisp_Object unwind_obj)
+{
+  struct png_unwind_data *data =
+    (struct png_unwind_data *) get_opaque_ptr (unwind_obj);
+
+  free_opaque_ptr (unwind_obj);
+  if (data->png_ptr)
+    png_read_destroy (data->png_ptr, data->info_ptr, (png_info *) NULL);
+  if (data->instream)
+    fclose (data->instream);
+  if (data->tempfile_needs_to_be_removed)
+    unlink (data->tempfile);
+  if (data->npixels > 0)
+    {
+      XFreeColors (data->dpy, data->cmap, data->pixels, data->npixels, 0L);
+      xfree (data->pixels);
+    }
+
+  if (data->ximage)
+    {
+      if (data->ximage->data)
+	{
+	  xfree (data->ximage->data);
+	  data->ximage->data = 0;
+	}
+      XDestroyImage (data->ximage);
+    }
+
+  return Qnil;
+}
+
+/* This doesn't appear to be used. */
+#if 0
+#define get_png_val(p) _get_png_val (&(p), info_ptr.bit_depth)
+png_uint_16
+_get_png_val (png_byte **pp, int bit_depth)
+{
+  png_uint_16 c = 0;
+
+  if (bit_depth == 16) {
+    c = (*((*pp)++)) << 8;
+  }
+  c |= (*((*pp)++));
+
+  return c;
+}
+#endif
+
+static void
+png_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
+		 Lisp_Object pointer_fg, Lisp_Object pointer_bg,
+		 int dest_mask, Lisp_Object domain)
+{
+  struct Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance);
+  Lisp_Object device = IMAGE_INSTANCE_DEVICE (ii);
+  Display *dpy;
+  Colormap cmap;
+  Visual *vis;
+  struct png_unwind_data unwind;
+  int speccount = specpdl_depth ();
+
+  /* PNG variables */
+  png_struct *png_ptr;
+  png_info *info_ptr;
+
+  if (!DEVICE_X_P (XDEVICE (device)))
+    signal_simple_error ("Not an X device", device);
+
+  dpy = DEVICE_X_DISPLAY (XDEVICE (device));
+  cmap = DEVICE_X_COLORMAP (XDEVICE(device));
+  vis = DEVICE_X_VISUAL (XDEVICE(device));
+
+  png_ptr  = xnew (png_struct);
+  info_ptr = xnew (png_info);
+
+  memset (&unwind, 0, sizeof (unwind));
+  unwind.png_ptr = png_ptr;
+  unwind.info_ptr = info_ptr;
+  unwind.dpy = dpy;
+  unwind.cmap = cmap;
+
+  record_unwind_protect (png_instantiate_unwind, make_opaque_ptr (&unwind));
+
+  /* This code is a mixture of stuff from Ben's GIF/JPEG stuff from
+     this file, example.c from the libpng 0.81 distribution, and the
+     pngtopnm sources. -WMP-
+     */
+#if defined (USE_TEMP_FILES_FOR_PNG_IMAGES) || (PNG_LIBPNG_VER < 87)
+  /* Write out to a temp file - we really should take the time to
+     write appropriate memory bound IO stuff, but I am just trying
+     to get the stupid thing working right now.
+     */
   {
     Lisp_Object data = find_keyword_in_vector (instantiator, Q_data);
 
@@ -1826,156 +2675,254 @@
     unwind.tempfile_needs_to_be_removed = 1;
 
     if ((unwind.instream = fopen (unwind.tempfile, "rb")) == NULL)
-      report_file_error ("Opening ImageMagick temp file",
+      report_file_error ("Opening PNG temp file",
 			 list1 (build_string (unwind.tempfile)));
   }
-
-  /* Initialize structures and read in the image */
-  GetImageInfo(&image_info);
-  strcpy(image_info.filename,unwind.tempfile);
-  unwind.image = ReadImage(&image_info);
-
-  if (unwind.image == (Image *) NULL) {
-    signal_simple_error ("Unable to read image.",instantiator);
-  }
-
-#if 1
-  /*
-   * For now, force dithering everything, and deal with all images as if they
-   * were PseudoClass images
-   */
-  if (unwind.image->class != PseudoClass) {
-    QuantizeInfo quantize_info;
-    GetQuantizeInfo(&quantize_info);
-    quantize_info.number_colors=256;
-    quantize_info.tree_depth=8;
-    quantize_info.dither=True;
-    quantize_info.colorspace=RGBColorspace;
-    QuantizeImage(&quantize_info, unwind.image);
-    SyncImage(unwind.image);
-    /* #### It would probably be a good idea to sort the colormap by popularity,
-     * so that in case we run out of entries in the map, it will likely be on
-     * the less used colors
-     */
-  } else {
-    CompressColormap(unwind.image);
-    SyncImage(unwind.image);
-  }
-  
+#else
+  /* Nothing */
 #endif
 
-#if 0
-  DescribeImage(unwind.image,stderr,1);
-#endif
-
-  unwind.ximage = XCreateImage(dpy, visual, depth,
-			       (depth == 1) ? XYPixmap : ZPixmap,
-			       0, 0,
-			       unwind.image->columns,
-			       unwind.image->rows,
-			       XBitmapPad(dpy), 0);
-
-  if (!unwind.ximage) {
-    signal_simple_error("Unable to allocate XImage structure",
-			instantiator);
-  }
-
-  unwind.ximage->data = (char *) xmalloc(unwind.ximage->bytes_per_line *
-					 unwind.ximage->height);
-
-  if (unwind.ximage->data == (char *)NULL) {
-    signal_simple_error("Unable to allocate XImage data information",
-			instantiator);
+  /* Set the jmp_buf reurn context for png_error ... if this returns !0, then
+     we ran into a problem somewhere, and need to clean up after ourselves. */
+  if (setjmp (png_ptr->jmpbuf))
+    {
+      /* Am I doing enough here?  I think so, since most things happen
+         in png_unwind */
+      png_read_destroy (png_ptr, info_ptr, (png_info *) NULL);
+      signal_simple_error ("Error decoding PNG", instantiator);
+    }
+
+  /* Initialize all PNG structures */
+  png_info_init (info_ptr);
+  png_read_init (png_ptr);
+
+  /* Initialize the IO layer and read in header information */
+#if defined (USE_TEMP_FILES_FOR_PNG_IMAGES) || (PNG_LIBPNG_VER < 87)
+  png_init_io (png_ptr, unwind.instream);
+#else
+  {
+    Lisp_Object data = find_keyword_in_vector (instantiator, Q_data);
+    Extbyte *bytes;
+    Extcount len;
+    struct png_memory_storage tbr; /* Data to be read */
+
+    assert (!NILP (data));
+
+    /* #### This is a definite problem under Mule due to the amount of
+       stack data it might allocate.  Need to be able to convert and
+       write out to a file. */
+    GET_STRING_BINARY_DATA_ALLOCA (data, bytes, len);
+    tbr.bytes = bytes;
+    tbr.len = len;
+    tbr.index = 0;
+    png_set_read_fn(png_ptr,(void *) &tbr, png_read_from_memory);
   }
-
-  
-  /*
-  ** First pull out all of the colors used, and create a lookup for them
-  */
-
-  if (unwind.image->class == PseudoClass) {
-    int i;
-
-    unwind.npixels = unwind.image->colors;
-    unwind.pixels = xmalloc(unwind.npixels * sizeof(unsigned long));
-    for (i = 0; i < unwind.npixels; i++) {
-      XColor color;
-      /* ImageMagic uses 8bit values for colors, whilst X expects 16bits */
-      color.red = unwind.image->colormap[i].red << 8;
-      color.green = unwind.image->colormap[i].green << 8;
-      color.blue = unwind.image->colormap[i].blue << 8;
-      color.flags = DoRed | DoGreen | DoBlue;
-      allocate_nearest_color (dpy, cmap, visual, &color);
-      unwind.pixels[i] = color.pixel;
-    }
-  }
-  
-  /*
-  ** Need to pull the data from the 'Image' structure in
-  ** unwind.image and convert it to an 'XImage' in unwind.ximage
-  */
+#endif
+
+  png_read_info (png_ptr, info_ptr);
+
+  /* set up the transformations you want.  Note that these are
+     all optional.  Only call them if you want them */
+  /* tell libpng to strip 16 bit depth files down to 8 bits */
+  if (info_ptr->bit_depth == 16)
+    png_set_strip_16 (png_ptr);
+  if (info_ptr->bit_depth < 8)
+    png_set_packing (png_ptr);
+  /* ##### Perhaps some way to specify the screen gamma should be in here? */
+
   {
-    int i,j,x,b;
-    unsigned int bytes_per_pixel, scanline_pad;
-    unsigned long pixval;
-    unsigned char *q, *pixar;
-    RunlengthPacket *p;
-
-    q = (unsigned char *) unwind.ximage->data;
-    x  = 0;
-    p = unwind.image->pixels;
-    scanline_pad = unwind.ximage->bytes_per_line -
-      ((unwind.ximage->width * unwind.ximage->bits_per_pixel) >> 3);
-
-    /* Convert to multi-byte color-mapped X image. */
-    bytes_per_pixel=unwind.ximage->bits_per_pixel >> 3;
-
-    pixar = (unsigned char *) alloca (bytes_per_pixel);
-
-    for (i=0; i < unwind.image->packets; i++) {
-      if (unwind.image->class == PseudoClass) 
-	pixval = unwind.pixels[p->index];
+    int height = info_ptr->height;
+    int width = info_ptr->width;
+    int depth = info_ptr->bit_depth;
+    int linesize = max (info_ptr->bit_depth >> 3, 1) * width;
+    int bitmap_pad;
+    int y;
+    XColor color;
+    png_byte *png_pixels;
+    png_byte **row_pointers;
+    png_color static_color_cube[216];
+
+    /* Wow, allocate all the memory.  Truly, exciting. */
+    unwind.pixels = xnew_array (unsigned long, 256);
+    png_pixels    = xnew_array (png_byte, linesize * height);
+    row_pointers  = xnew_array (png_byte *, height);
+
+    for (y = 0; y < 256; y++)
+      unwind.pixels[y] = 0;
+    for (y = 0; y < height; y++)
+      row_pointers[y] = png_pixels + (linesize * y);
+
+    /*  #### This is where we should handle transparency, but I am unsure of
+	how exactly to get that information right now, in a safe manner. */
+#if 0
+    {
+      png_color_16 current_background;
+
+      /* Some appropriate magic should go here to get the current
+	 buffers (device?)  background color and convert it to a
+	 png_color_16 struct */
+      if (info_ptr->valid & PNG_INFO_bKGD)
+	png_set_background (png_ptr, &(info_ptr->background), PNG_GAMMA_FILE,
+			    1, 1.0);
       else
+	png_set_background (png_ptr, &current_background, PNG_GAMMA_SCREEN,
+			    0, 1.0);
+    }
+#endif
+
+    if ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||
+	(info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA))
+      {
+	if (!(info_ptr->valid & PNG_INFO_PLTE))
+	  {
+	    for (y = 0; y < 216; y++)
+	      {
+		static_color_cube[y].red = (y % 6) * 255.0 / 5;
+		static_color_cube[y].green = ((y / 6) % 6) * 255.0 / 5;
+		static_color_cube[y].blue = (y / 36) * 255.0 / 5;
+	      }
+	    png_set_dither (png_ptr, static_color_cube, 216, 216, NULL, 1);
+	  }
+	else
+	  {
+	    png_set_dither (png_ptr, info_ptr->palette, info_ptr->num_palette,
+			    info_ptr->num_palette, info_ptr->hist, 1);
+	  }
+      }
+
+    png_read_image (png_ptr, row_pointers);
+    png_read_end (png_ptr, info_ptr);
+
+    /* Ok, now we go and allocate all the colors */
+    if (info_ptr->valid & PNG_INFO_PLTE)
+      {
+	unwind.npixels = info_ptr->num_palette;
+	for (y = 0; y < unwind.npixels; y++)
+	  {
+	    color.red = info_ptr->palette[y].red << 8;
+	    color.green = info_ptr->palette[y].green << 8;
+	    color.blue = info_ptr->palette[y].blue << 8;
+	    color.flags = DoRed | DoGreen | DoBlue;
+	    allocate_nearest_color (dpy, cmap, vis, &color);
+	    unwind.pixels[y] = color.pixel;
+	  }
+      }
+    else
+      {
+	unwind.npixels = 216;
+	for (y = 0; y < 216; y++)
+	  {
+	    color.red = static_color_cube[y].red << 8;
+	    color.green = static_color_cube[y].green << 8;
+	    color.blue = static_color_cube[y].blue << 8;
+	    color.flags = DoRed|DoGreen|DoBlue;
+	    allocate_nearest_color (dpy, cmap, vis, &color);
+	    unwind.pixels[y] = color.pixel;
+	  }
+      }
+
+#ifdef PNG_SHOW_COMMENTS
+    /* ####
+     * I turn this off by default now, because the !%^@#!% comments
+     * show up every time the image is instantiated, which can get
+     * really really annoying.  There should be some way to pass this
+     * type of data down into the glyph code, where you can get to it
+     * from lisp anyway. - WMP
+     */
+    {
+      int i;
+
+      for (i = 0 ; i < info_ptr->num_text ; i++)
 	{
-	  /* ### NOW what? */
-	  pixval = 0;
+	  /* How paranoid do I have to be about no trailing NULLs, and
+	     using (int)info_ptr->text[i].text_length, and strncpy and a temp
+	     string somewhere? */
+
+	  warn_when_safe (Qpng, Qinfo, "%s - %s",
+			  info_ptr->text[i].key,
+			  info_ptr->text[i].text);
 	}
-
-      for (b=0; b < bytes_per_pixel; b++) {
-	if (unwind.ximage->bitmap_bit_order == LSBFirst) 
-	  pixar[b] = (unsigned char)pixval;
-	else
-	  pixar[bytes_per_pixel - 1 - b] = (unsigned char)pixval;
-	pixval>>=8;
-      }
-
-      for (j=0; j <= ((int) p->length); j++) {
-	for (b=0; b < bytes_per_pixel; b++) 
-	  *q++= pixar[b];
-	x++;
-	if (x == unwind.ximage->width) {
-	  x=0;
-	  q+=scanline_pad;
-	}
-      }
-      p++;
     }
+#endif
+
+    /* Now create the image */
+
+    depth = DEVICE_X_DEPTH(XDEVICE(device));
+
+    /* first get bitmap_pad (from XPM) */
+    bitmap_pad = ((depth > 16) ? 32 :
+		  (depth >  8) ? 16 :
+		  8);
+
+    unwind.ximage = XCreateImage (dpy, vis,
+				  depth, ZPixmap, 0, 0, width, height,
+				  bitmap_pad, 0);
+
+    if (!unwind.ximage)
+      signal_simple_error ("Unable to create X image struct",
+			   instantiator);
+
+    /* now that bytes_per_line must have been set properly alloc data */
+    unwind.ximage->data = (char *) xmalloc (unwind.ximage->bytes_per_line *
+					    height);
+
+    {
+      int i, j;
+      for (i = 0; i < height; i++)
+	for (j = 0; j < width; j++)
+	  XPutPixel (unwind.ximage, j, i,
+		     unwind.pixels[png_pixels[i * width + j]]);
+    }
+
+    xfree (row_pointers);
+    xfree (png_pixels);
   }
 
   init_image_instance_from_x_image (ii, unwind.ximage, dest_mask,
 				    unwind.pixels, unwind.npixels,
 				    instantiator);
 
-  /* And we are done!
-  ** Now that we've succeeded, we don't want the pixels
-  ** freed right now.  They're kept around in the image instance
-  ** structure until it's destroyed.
-  */
+  /* This will clean up everything else. */
   unwind.npixels = 0;
   unbind_to (speccount, Qnil);
 }
 
-#endif /* HAVE_IMAGEMAGICK */
+#endif /* HAVE_PNG */
+
+
+#ifdef HAVE_TIFF
+
+/**********************************************************************
+ *                             TIFF                                   *
+ **********************************************************************/
+static void
+tiff_validate (Lisp_Object instantiator)
+{
+  file_or_data_must_be_present (instantiator);
+}
+
+static Lisp_Object
+tiff_normalize (Lisp_Object inst, Lisp_Object console_type)
+{
+  signal_simple_error ("No TIFF support yet", inst);
+  return Qnil;
+}
+
+static int
+tiff_possible_dest_types (void)
+{
+  return IMAGE_COLOR_PIXMAP_MASK;
+}
+
+static void
+tiff_instantiate (Lisp_Object image_instance, Lisp_Object instantiator,
+		  Lisp_Object pointer_fg, Lisp_Object pointer_bg,
+		  int dest_mask, Lisp_Object domain)
+{
+  abort ();
+}
+
+#endif /* HAVE_TIFF */
 
 
 #ifdef HAVE_XFACE
@@ -2882,55 +3829,52 @@
   IIFORMAT_VALID_KEYWORD (font, Q_foreground, check_valid_string);
   IIFORMAT_VALID_KEYWORD (font, Q_background, check_valid_string);
 
-#ifdef HAVE_IMAGEMAGICK
-  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (imagick, "imagick");
-
-  IIFORMAT_HAS_METHOD (imagick, validate);
-  IIFORMAT_HAS_METHOD (imagick, normalize);
-  IIFORMAT_HAS_METHOD (imagick, possible_dest_types);
-  IIFORMAT_HAS_METHOD (imagick, instantiate);
-
-  IIFORMAT_VALID_KEYWORD (imagick, Q_data, check_valid_string);
-  IIFORMAT_VALID_KEYWORD (imagick, Q_file, check_valid_string);
-
-#ifdef OLDCOMPAT /* old graphics compatibility */
-#define IIFORMAT_USES_METHOD(format, source, m) \
-  (format##_image_instantiator_methods->m##_method = source##_##m)
-
+#ifdef HAVE_JPEG
+  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (jpeg, "jpeg");
+
+  IIFORMAT_HAS_METHOD (jpeg, validate);
+  IIFORMAT_HAS_METHOD (jpeg, normalize);
+  IIFORMAT_HAS_METHOD (jpeg, possible_dest_types);
+  IIFORMAT_HAS_METHOD (jpeg, instantiate);
+
+  IIFORMAT_VALID_KEYWORD (jpeg, Q_data, check_valid_string);
+  IIFORMAT_VALID_KEYWORD (jpeg, Q_file, check_valid_string);
+#endif
+
+#ifdef HAVE_GIF
+  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (gif, "gif");
+
+  IIFORMAT_HAS_METHOD (gif, validate);
+  IIFORMAT_HAS_METHOD (gif, normalize);
+  IIFORMAT_HAS_METHOD (gif, possible_dest_types);
+  IIFORMAT_HAS_METHOD (gif, instantiate);
+
+  IIFORMAT_VALID_KEYWORD (gif, Q_data, check_valid_string);
+  IIFORMAT_VALID_KEYWORD (gif, Q_file, check_valid_string);
+#endif
+
+#ifdef HAVE_PNG
+  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (png, "png");
+
+  IIFORMAT_HAS_METHOD (png, validate);
+  IIFORMAT_HAS_METHOD (png, normalize);
+  IIFORMAT_HAS_METHOD (png, possible_dest_types);
+  IIFORMAT_HAS_METHOD (png, instantiate);
+
+  IIFORMAT_VALID_KEYWORD (png, Q_data, check_valid_string);
+  IIFORMAT_VALID_KEYWORD (png, Q_file, check_valid_string);
+#endif
+
+#ifdef HAVE_TIFF
   INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (tiff, "tiff");
-  IIFORMAT_USES_METHOD (tiff, imagick, validate);
-  IIFORMAT_USES_METHOD (tiff, imagick, normalize);
-  IIFORMAT_USES_METHOD (tiff, imagick, possible_dest_types);
-  IIFORMAT_USES_METHOD (tiff, imagick, instantiate);
+
+  IIFORMAT_HAS_METHOD (tiff, validate);
+  IIFORMAT_HAS_METHOD (tiff, normalize);
+  IIFORMAT_HAS_METHOD (tiff, possible_dest_types);
+  IIFORMAT_HAS_METHOD (tiff, instantiate);
+
   IIFORMAT_VALID_KEYWORD (tiff, Q_data, check_valid_string);
   IIFORMAT_VALID_KEYWORD (tiff, Q_file, check_valid_string);
-
-  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (png, "png");
-  IIFORMAT_USES_METHOD (png, imagick, validate);
-  IIFORMAT_USES_METHOD (png, imagick, normalize);
-  IIFORMAT_USES_METHOD (png, imagick, possible_dest_types);
-  IIFORMAT_USES_METHOD (png, imagick, instantiate);
-  IIFORMAT_VALID_KEYWORD (png, Q_data, check_valid_string);
-  IIFORMAT_VALID_KEYWORD (png, Q_file, check_valid_string);
-
-  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (gif, "gif");
-  IIFORMAT_USES_METHOD (gif, imagick, validate);
-  IIFORMAT_USES_METHOD (gif, imagick, normalize);
-  IIFORMAT_USES_METHOD (gif, imagick, possible_dest_types);
-  IIFORMAT_USES_METHOD (gif, imagick, instantiate);
-  IIFORMAT_VALID_KEYWORD (gif, Q_data, check_valid_string);
-  IIFORMAT_VALID_KEYWORD (gif, Q_file, check_valid_string);
-
-  INITIALIZE_IMAGE_INSTANTIATOR_FORMAT (jpeg, "jpeg");
-  IIFORMAT_USES_METHOD (jpeg, imagick, validate);
-  IIFORMAT_USES_METHOD (jpeg, imagick, normalize);
-  IIFORMAT_USES_METHOD (jpeg, imagick, possible_dest_types);
-  IIFORMAT_USES_METHOD (jpeg, imagick, instantiate);
-  IIFORMAT_VALID_KEYWORD (jpeg, Q_data, check_valid_string);
-  IIFORMAT_VALID_KEYWORD (jpeg, Q_file, check_valid_string);
-
-#endif /* old compat */
-
 #endif
 
 #ifdef HAVE_XPM
@@ -2976,6 +3920,22 @@
 void
 vars_of_glyphs_x (void)
 {
+#ifdef HAVE_JPEG
+  Fprovide (Qjpeg);
+#endif
+
+#ifdef HAVE_GIF
+  Fprovide (Qgif);
+#endif
+
+#ifdef HAVE_PNG
+  Fprovide (Qpng);
+#endif
+
+#ifdef HAVE_TIFF
+  Fprovide (Qtiff);
+#endif
+
 #ifdef HAVE_XPM
   Fprovide (Qxpm);
 
@@ -2993,17 +3953,6 @@
   Vxpm_color_symbols = Qnil; /* initialized in x-faces.el */
 #endif
 
-#ifdef HAVE_IMAGEMAGICK
-  Fprovide (Qimagick);
-
-#ifdef OLDCOMPAT
-  Fprovide (Qtiff);
-  Fprovide (Qpng);
-  Fprovide (Qgif);
-  Fprovide (Qjpeg);
-#endif
-#endif
-
 #ifdef HAVE_XFACE
   Fprovide (Qxface);
 #endif