changeset 27058:209b24ba8ddf

Changes to xfns.c to support reading images from a memory buffer instead of forcing them to be on disk. GIF/JPEG/PNG/TIFF currently support this.
author William M. Perry <wmperry@aventail.com>
date Fri, 31 Dec 1999 15:47:49 +0000
parents d2d418d5a1c0
children 6bb2a4a0413e
files src/ChangeLog src/xfns.c
diffstat 2 files changed, 365 insertions(+), 138 deletions(-) [+]
line wrap: on
line diff
Binary file src/ChangeLog has changed
--- a/src/xfns.c	Fri Dec 31 15:35:03 1999 +0000
+++ b/src/xfns.c	Fri Dec 31 15:47:49 1999 +0000
@@ -19,6 +19,9 @@
 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
+/* Ability to read images from memory instead of a file added by
+   William Perry <wmperry@gnu.org> */
+
 /* Image support (XBM, XPM, PBM, JPEG, TIFF, GIF, PNG, GS). tooltips,
    tool-bars, busy-cursor, file selection dialog added by Gerd
    Moellmann <gerd@gnu.org>.  */
@@ -7922,6 +7925,7 @@
 enum png_keyword_index
 {
   PNG_TYPE,
+  PNG_DATA,
   PNG_FILE,
   PNG_ASCENT,
   PNG_MARGIN,
@@ -7937,7 +7941,8 @@
 static struct image_keyword png_format[PNG_LAST] =
 {
   {":type",		IMAGE_SYMBOL_VALUE,			1},
-  {":file",		IMAGE_STRING_VALUE,			1},
+  {":data",     IMAGE_STRING_VALUE,         0},
+  {":file",		IMAGE_STRING_VALUE,			0},
   {":ascent",		IMAGE_NON_NEGATIVE_INTEGER_VALUE,	0},
   {":margin",		IMAGE_POSITIVE_INTEGER_VALUE,		0},
   {":relief",		IMAGE_INTEGER_VALUE,			0},
@@ -7970,6 +7975,13 @@
       || (fmt[PNG_ASCENT].count 
 	  && XFASTINT (fmt[PNG_ASCENT].value) > 100))
     return 0;
+
+  /* Must specify either the :data or :file keyword.  This should
+  ** probably be moved up into parse_image_spec, since it seems to be
+  ** a general requirement.
+  */
+  if (!fmt[PNG_FILE].count && !fmt[PNG_DATA].count)
+	return 0;
   return 1;
 }
 
@@ -7997,6 +8009,28 @@
   image_error ("PNG warning: %s", build_string (msg), Qnil);
 }
 
+/* Memory source for PNG decoding.  Originally written for XEmacs by
+   William Perry <wmperry@gnu.org>, who has paperwork on file, and so
+   it is safe to use. */
+struct png_memory_storage
+{
+  unsigned char *bytes;			/* The data       */
+  size_t len;					/* How big is it? */
+  int index;					/* Where are we?  */
+};
+
+static void
+png_read_from_memory(png_structp png_ptr, png_bytep data,
+		     png_size_t 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;
+}
 
 /* Load PNG image IMG for use on frame F.  Value is non-zero if
    successful.  */
@@ -8007,12 +8041,13 @@
      struct image *img;
 {
   Lisp_Object file, specified_file;
+  Lisp_Object specified_data;
   int x, y, i;
   XImage *ximg, *mask_img = NULL;
   struct gcpro gcpro1;
   png_struct *png_ptr = NULL;
   png_info *info_ptr = NULL, *end_info = NULL;
-  FILE *fp;
+  FILE *fp = NULL;
   png_byte sig[8];
   png_byte *pixels = NULL;
   png_byte **rows = NULL;
@@ -8024,44 +8059,69 @@
   char *gamma_str;
   double screen_gamma, image_gamma;
   int intent;
+  struct png_memory_storage tbr;  /* Data to be read */
 
   /* Find out what file to load.  */
   specified_file = image_spec_value (img->spec, QCfile, NULL);
-  file = x_find_image_file (specified_file);
-  GCPRO1 (file);
-  if (!STRINGP (file))
-    {
-      image_error ("Cannot find image file %s", specified_file, Qnil);
-      UNGCPRO;
-      return 0;
-    }
-
-  /* Open the image file.  */
-  fp = fopen (XSTRING (file)->data, "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
-      || !png_check_sig (sig, sizeof sig))
-    {
-      image_error ("Not a PNG file: %s", file, Qnil);
-      UNGCPRO;
-      fclose (fp);
-      return 0;
-    }
+  specified_data = image_spec_value (img->spec, QCdata, NULL);
+
+  if (NILP (specified_data))
+	{
+	  file = x_find_image_file (specified_file);
+	  GCPRO1 (file);
+	  if (!STRINGP (file))
+		{
+		  image_error ("Cannot find image file %s", specified_file, Qnil);
+		  UNGCPRO;
+		  return 0;
+		}
+
+	  /* Open the image file.  */
+	  fp = fopen (XSTRING (file)->data, "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
+		  || !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 = XSTRING (specified_data)->data;
+	  tbr.len = STRING_BYTES (XSTRING (specified_data));
+	  tbr.index = 0;
+
+	  /* Chekc PNG signature */
+	  if ((tbr.len < sizeof(sig)) ||
+		  !png_check_sig (tbr.bytes, sizeof(sig)))
+		{
+		  image_error ("Not a PNG file: %s", file, Qnil);
+		  UNGCPRO;
+		  return 0;
+		}
+
+	  /* Need to skip past the signature */
+	  tbr.bytes += sizeof(sig);
+	}
 
   /* Initialize read and info structs for PNG lib.  */
   png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL,
 				    my_png_error, my_png_warning);
   if (!png_ptr)
     {
-      fclose (fp);
+      if (fp) fclose (fp);
       UNGCPRO;
       return 0;
     }
@@ -8070,7 +8130,7 @@
   if (!info_ptr)
     {
       png_destroy_read_struct (&png_ptr, NULL, NULL);
-      fclose (fp);
+      if (fp) fclose (fp);
       UNGCPRO;
       return 0;
     }
@@ -8079,7 +8139,7 @@
   if (!end_info)
     {
       png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
-      fclose (fp);
+      if (fp) fclose (fp);
       UNGCPRO;
       return 0;
     }
@@ -8093,14 +8153,17 @@
         png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
       xfree (pixels);
       xfree (rows);
-      if (fp)
-	fclose (fp);
+      if (fp) fclose (fp);
       UNGCPRO;
       return 0;
     }
 
   /* Read image info.  */
-  png_init_io (png_ptr, fp);
+  if (!NILP (specified_data))
+	png_set_read_fn (png_ptr,(void *) &tbr, png_read_from_memory);
+  else
+	png_init_io (png_ptr, fp);
+
   png_set_sig_bytes (png_ptr, sizeof sig);
   png_read_info (png_ptr, info_ptr);
   png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
@@ -8209,7 +8272,7 @@
   /* Read the entire image.  */
   png_read_image (png_ptr, rows);
   png_read_end (png_ptr, info_ptr);
-  fclose (fp);
+  if (fp) fclose (fp);
   fp = NULL;
   
   BLOCK_INPUT;
@@ -8391,7 +8454,7 @@
   
   if (!parse_image_spec (object, fmt, JPEG_LAST, Qjpeg)
       || (fmt[JPEG_ASCENT].count 
-	  && XFASTINT (fmt[JPEG_ASCENT].value) > 100))
+		  && XFASTINT (fmt[JPEG_ASCENT].value) > 100))
     return 0;
 
   /* Must specify either the :data or :file keyword.  This should
@@ -8415,7 +8478,6 @@
   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.  */
@@ -8514,7 +8576,6 @@
   src->next_input_byte = data;
 }
 
-
 /* Load image IMG for use on frame F.  Patterned after example.c
    from the JPEG lib.  */
 
@@ -8562,41 +8623,41 @@
     }
 
   /* Customize libjpeg's error handling to call my_error_exit
-     when an error is detected.  This function will perform
-     a longjmp.  */
+	 when an error is detected.  This function will perform
+	 a longjmp.  */
   mgr.pub.error_exit = my_error_exit;
   cinfo.err = jpeg_std_error (&mgr.pub);
   
   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 file `%s': %s", file,
-		       build_string (buffer));
-	}
+	  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 file `%s': %s", file,
+					   build_string (buffer));
+		}
 	  
       /* Close the input file and destroy the JPEG object.  */
 	  if (fp) fclose (fp);
       jpeg_destroy_decompress (&cinfo);
 
-      BLOCK_INPUT;
+	  BLOCK_INPUT;
       
-      /* If we already have an XImage, free that.  */
-      x_destroy_x_image (ximg);
-
-      /* Free pixmap and colors.  */
-      x_clear_image (f, img);
+	  /* If we already have an XImage, free that.  */
+	  x_destroy_x_image (ximg);
+
+	  /* Free pixmap and colors.  */
+	  x_clear_image (f, img);
       
-      UNBLOCK_INPUT;
-      UNGCPRO;
-      return 0;
+	  UNBLOCK_INPUT;
+	  UNGCPRO;
+	  return 0;
     }
 
   /* Create the JPEG decompression object.  Let it read from fp.
-     Read the JPEG image header.  */
+	 Read the JPEG image header.  */
   jpeg_create_decompress (&cinfo);
 
   if (NILP (specified_data))
@@ -8604,11 +8665,11 @@
   else
     jpeg_memory_src (&cinfo, XSTRING (specified_data)->data,
 		     STRING_BYTES (XSTRING (specified_data)));
-  
+
   jpeg_read_header (&cinfo, TRUE);
 
   /* Customize decompression so that color quantization will be used.
-     Start decompression.  */
+	 Start decompression.  */
   cinfo.quantize_colors = TRUE;
   jpeg_start_decompress (&cinfo);
   width = img->width = cinfo.output_width;
@@ -8618,59 +8679,59 @@
 
   /* Create X image and pixmap.  */
   if (!x_create_x_image_and_pixmap (f, file, width, height, 0, &ximg,
-				    &img->pixmap))
-    {
-      UNBLOCK_INPUT;
-      longjmp (mgr.setjmp_buffer, 2);
+									&img->pixmap))
+    {
+	  UNBLOCK_INPUT;
+	  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.  */
+	 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);
+	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);
-      }
-
-    /* Remember those colors actually allocated.  */
-    img->colors = colors_in_color_table (&img->ncolors);
-    free_color_table ();
+	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);
+	  }
+
+	/* Remember those colors actually allocated.  */
+	img->colors = colors_in_color_table (&img->ncolors);
+	free_color_table ();
   }
 
   /* Read pixels.  */
   row_stride = width * cinfo.output_components;
   buffer = cinfo.mem->alloc_sarray ((j_common_ptr) &cinfo, JPOOL_IMAGE,
-				    row_stride, 1);
+									row_stride, 1);
   for (y = 0; y < height; ++y)
     {
-      jpeg_read_scanlines (&cinfo, buffer, 1);
-      for (x = 0; x < cinfo.output_width; ++x)
-	XPutPixel (ximg, x, y, colors[buffer[0][x]]);
+	  jpeg_read_scanlines (&cinfo, buffer, 1);
+	  for (x = 0; x < cinfo.output_width; ++x)
+		XPutPixel (ximg, x, y, colors[buffer[0][x]]);
     }
 
   /* Clean up.  */
@@ -8710,6 +8771,7 @@
 enum tiff_keyword_index
 {
   TIFF_TYPE,
+  TIFF_DATA,
   TIFF_FILE,
   TIFF_ASCENT,
   TIFF_MARGIN,
@@ -8725,7 +8787,8 @@
 static struct image_keyword tiff_format[TIFF_LAST] =
 {
   {":type",		IMAGE_SYMBOL_VALUE,			1},
-  {":file",		IMAGE_STRING_VALUE,			1},
+  {":data",     IMAGE_STRING_VALUE,         0},
+  {":file",		IMAGE_STRING_VALUE,			0},
   {":ascent",		IMAGE_NON_NEGATIVE_INTEGER_VALUE,	0},
   {":margin",		IMAGE_POSITIVE_INTEGER_VALUE,		0},
   {":relief",		IMAGE_INTEGER_VALUE,			0},
@@ -8758,9 +8821,92 @@
       || (fmt[TIFF_ASCENT].count 
 	  && XFASTINT (fmt[TIFF_ASCENT].value) > 100))
     return 0;
+  /* Must specify either the :data or :file keyword.  This should
+  ** probably be moved up into parse_image_spec, since it seems to be
+  ** a general requirement.
+  */
+  if (!fmt[TIFF_FILE].count && !fmt[TIFF_DATA].count)
+	return 0;
   return 1;
 }
 
+/* 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(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;
+  memcpy(buf, src->bytes + src->index, size);
+  src->index += size;
+  return size;
+}
+
+static size_t tiff_write_from_memory(thandle_t data, tdata_t buf, tsize_t size)
+{
+  return (size_t) -1;
+}
+
+static toff_t tiff_seek_in_memory(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(thandle_t data)
+{
+  /* NOOP */
+  return(0);
+}
+
+static int tiff_mmap_memory(thandle_t data, tdata_t *pbase, toff_t *psize)
+{
+  /* It is already _IN_ memory. */
+  return(0);
+}
+
+static void tiff_unmap_memory(thandle_t data, tdata_t base, toff_t size)
+{
+  /* We don't need to do this. */
+  return;
+}
+
+static toff_t tiff_size_of_memory(thandle_t data)
+{
+  return(((tiff_memory_source *) data)->len);
+}
 
 /* Load TIFF image IMG for use on frame F.  Value is non-zero if
    successful.  */
@@ -8771,31 +8917,62 @@
      struct image *img;
 {
   Lisp_Object file, specified_file;
+  Lisp_Object specified_data;
   TIFF *tiff;
   int width, height, x, y;
   uint32 *buf;
   int rc;
   XImage *ximg;
   struct gcpro gcpro1;
+  tiff_memory_source memsrc;
 
   specified_file = image_spec_value (img->spec, QCfile, NULL);
-  file = x_find_image_file (specified_file);
-  GCPRO1 (file);
-  if (!STRINGP (file))
-    {
-      image_error ("Cannot find image file %s", file, Qnil);
-      UNGCPRO;
-      return 0;
-    }
-  
-  /* Try to open the image file.  */
-  tiff = TIFFOpen (XSTRING (file)->data, "r");
-  if (tiff == NULL)
-    {
-      image_error ("Cannot open `%s'", file, Qnil);
-      UNGCPRO;
-      return 0;
-    }
+  specified_data = image_spec_value (img->spec, QCdata, NULL);
+
+  if (NILP (specified_data))
+	{
+	  /* Read from a file */
+	  file = x_find_image_file (specified_file);
+	  GCPRO1 (file);
+	  if (!STRINGP (file))
+		{
+		  image_error ("Cannot find image file %s", file, Qnil);
+		  UNGCPRO;
+		  return 0;
+		}
+	  
+	  /* Try to open the image file.  */
+	  tiff = TIFFOpen (XSTRING (file)->data, "r");
+	  if (tiff == NULL)
+		{
+		  image_error ("Cannot open `%s'", file, Qnil);
+		  UNGCPRO;
+		  return 0;
+		}
+	}
+  else
+	{
+	  /* Memory source! */
+	  memsrc.bytes = XSTRING (specified_data)->data;
+	  memsrc.len = STRING_BYTES (XSTRING (specified_data));
+	  memsrc.index = 0;
+
+	  tiff = 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 `%s'. ", specified_data, Qnil);
+		  UNGCPRO;
+		  return 0;
+		}
+	}
 
   /* Get width and height of the image, and allocate a raster buffer
      of width x height 32-bit values.  */
@@ -8884,6 +9061,7 @@
 enum gif_keyword_index
 {
   GIF_TYPE,
+  GIF_DATA,
   GIF_FILE,
   GIF_ASCENT,
   GIF_MARGIN,
@@ -8900,7 +9078,8 @@
 static struct image_keyword gif_format[GIF_LAST] =
 {
   {":type",		IMAGE_SYMBOL_VALUE,			1},
-  {":file",		IMAGE_STRING_VALUE,			1},
+  {":data",     IMAGE_STRING_VALUE,         0},
+  {":file",		IMAGE_STRING_VALUE,			0},
   {":ascent",		IMAGE_NON_NEGATIVE_INTEGER_VALUE,	0},
   {":margin",		IMAGE_POSITIVE_INTEGER_VALUE,		0},
   {":relief",		IMAGE_INTEGER_VALUE,			0},
@@ -8934,9 +9113,35 @@
       || (fmt[GIF_ASCENT].count 
 	  && XFASTINT (fmt[GIF_ASCENT].value) > 100))
     return 0;
+  /* Must specify either the :data or :file keyword.  This should
+  ** probably be moved up into parse_image_spec, since it seems to be
+  ** a general requirement.
+  */
+  if (!fmt[GIF_FILE].count && !fmt[GIF_DATA].count)
+	return 0;
   return 1;
 }
 
+/* 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;
+
+static int gif_read_from_memory(GifFileType *file, GifByteType *buf, int len)
+{
+  gif_memory_source *src = (gif_memory_source *) file->UserData;
+
+  if (len > (src->len - src->index))
+	return -1;
+
+  memcpy(buf, src->bytes + src->index, len);
+  src->index += len;
+  return len;
+}
 
 /* Load GIF image IMG for use on frame F.  Value is non-zero if
    successful.  */
@@ -8947,6 +9152,7 @@
      struct image *img;
 {
   Lisp_Object file, specified_file;
+  Lisp_Object specified_data;
   int rc, width, height, x, y, i;
   XImage *ximg;
   ColorMapObject *gif_color_map;
@@ -8955,25 +9161,46 @@
   struct gcpro gcpro1;
   Lisp_Object image;
   int ino, image_left, image_top, image_width, image_height;
+  gif_memory_source memsrc;
 
   specified_file = image_spec_value (img->spec, QCfile, NULL);
-  file = x_find_image_file (specified_file);
-  GCPRO1 (file);
-  if (!STRINGP (file))
-    {
-      image_error ("Cannot find image file %s", specified_file, Qnil);
-      UNGCPRO;
-      return 0;
-    }
+  specified_data = image_spec_value (img->spec, QCdata, NULL);
+
+  if (NILP (specified_data))
+	{
+	  file = x_find_image_file (specified_file);
+	  GCPRO1 (file);
+	  if (!STRINGP (file))
+		{
+		  image_error ("Cannot find image file %s", specified_file, Qnil);
+		  UNGCPRO;
+		  return 0;
+		}
   
-  /* Open the GIF file.  */
-  gif = DGifOpenFileName (XSTRING (file)->data);
-  if (gif == NULL)
-    {
-      image_error ("Cannot open `%s'", file, Qnil);
-      UNGCPRO;
-      return 0;
-    }
+	  /* Open the GIF file.  */
+	  gif = DGifOpenFileName (XSTRING (file)->data);
+	  if (gif == NULL)
+		{
+		  image_error ("Cannot open `%s'", file, Qnil);
+		  UNGCPRO;
+		  return 0;
+		}
+	}
+  else
+	{
+	  /* Read from memory! */
+	  memsrc.bytes = XSTRING (specified_data)->data;
+	  memsrc.len = STRING_BYTES (XSTRING (specified_data));
+	  memsrc.index = 0;
+
+	  gif = DGifOpen(&memsrc, gif_read_from_memory);
+	  if (!gif)
+		{
+		  image_error ("Cannot open memory source `%s'",specified_data, Qnil);
+		  UNGCPRO;
+		  return 0;
+		}
+	}
 
   /* Read entire contents.  */
   rc = DGifSlurp (gif);