* image.c: Pacify --enable-gcc-warnings.
[bpt/emacs.git] / src / image.c
index 4974903..86780c6 100644 (file)
@@ -30,13 +30,8 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #endif
 
 #include <setjmp.h>
-
 #include <c-ctype.h>
 
-/* 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"
@@ -92,10 +87,11 @@ typedef struct w32_bitmap_record Bitmap_Record;
 #define x_defined_color w32_defined_color
 #define DefaultDepthOfScreen(screen) (one_w32_display_info.n_cbits)
 
-/* Version of libpng that we were compiled with, or -1 if no PNG
-   support was compiled in.  This is tested by w32-win.el to correctly
-   set up the alist used to search for PNG libraries.  */
-Lisp_Object Qlibpng_version;
+/* Versions of libpng and libgif that we were compiled with, or -1 if
+   no PNG/GIF support was compiled in.  This is tested by w32-win.el
+   to correctly set up the alist used to search for the respective
+   image libraries.  */
+Lisp_Object Qlibpng_version, Qlibgif_version;
 #endif /* HAVE_NTGUI */
 
 #ifdef HAVE_NS
@@ -145,7 +141,7 @@ static Lisp_Object QCmax_width, QCmax_height;
 
 #ifdef HAVE_NS
 /* Use with images created by ns_image_for_XPM.  */
-unsigned long
+static unsigned long
 XGetPixel (XImagePtr ximage, int x, int y)
 {
   return ns_get_pixel (ximage, x, y);
@@ -153,7 +149,7 @@ XGetPixel (XImagePtr ximage, int x, int y)
 
 /* Use with images created by ns_image_for_XPM; alpha set to 1;
    pixel is assumed to be in RGB form.  */
-void
+static void
 XPutPixel (XImagePtr ximage, int x, int y, unsigned long pixel)
 {
   ns_put_pixel (ximage, x, y, pixel);
@@ -166,13 +162,13 @@ XPutPixel (XImagePtr ximage, int x, int y, unsigned long pixel)
 int
 x_bitmap_height (struct frame *f, ptrdiff_t id)
 {
-  return FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].height;
+  return FRAME_DISPLAY_INFO (f)->bitmaps[id - 1].height;
 }
 
 int
 x_bitmap_width (struct frame *f, ptrdiff_t id)
 {
-  return FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].width;
+  return FRAME_DISPLAY_INFO (f)->bitmaps[id - 1].width;
 }
 
 #if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI)
@@ -180,7 +176,7 @@ ptrdiff_t
 x_bitmap_pixmap (struct frame *f, ptrdiff_t id)
 {
   /* HAVE_NTGUI needs the explicit cast here.  */
-  return (ptrdiff_t) FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].pixmap;
+  return (ptrdiff_t) FRAME_DISPLAY_INFO (f)->bitmaps[id - 1].pixmap;
 }
 #endif
 
@@ -188,7 +184,7 @@ x_bitmap_pixmap (struct frame *f, ptrdiff_t id)
 int
 x_bitmap_mask (struct frame *f, ptrdiff_t id)
 {
-  return FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].mask;
+  return FRAME_DISPLAY_INFO (f)->bitmaps[id - 1].mask;
 }
 #endif
 
@@ -197,7 +193,7 @@ x_bitmap_mask (struct frame *f, ptrdiff_t id)
 static ptrdiff_t
 x_allocate_bitmap_record (struct frame *f)
 {
-  Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+  Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f);
   ptrdiff_t i;
 
   if (dpyinfo->bitmaps_last < dpyinfo->bitmaps_size)
@@ -218,7 +214,7 @@ x_allocate_bitmap_record (struct frame *f)
 void
 x_reference_bitmap (struct frame *f, ptrdiff_t id)
 {
-  ++FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].refcount;
+  ++FRAME_DISPLAY_INFO (f)->bitmaps[id - 1].refcount;
 }
 
 /* Create a bitmap for frame F from a HEIGHT x WIDTH array of bits at BITS.  */
@@ -226,7 +222,7 @@ x_reference_bitmap (struct frame *f, ptrdiff_t id)
 ptrdiff_t
 x_create_bitmap_from_data (struct frame *f, char *bits, unsigned int width, unsigned int height)
 {
-  Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+  Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f);
   ptrdiff_t id;
 
 #ifdef HAVE_X_WINDOWS
@@ -240,8 +236,8 @@ x_create_bitmap_from_data (struct frame *f, char *bits, unsigned int width, unsi
 #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,
+                        FRAME_DISPLAY_INFO (XFRAME (frame))->n_planes,
+                        FRAME_DISPLAY_INFO (XFRAME (frame))->n_cbits,
                         bits);
   if (! bitmap)
     return -1;
@@ -285,7 +281,7 @@ x_create_bitmap_from_data (struct frame *f, char *bits, unsigned int width, unsi
 ptrdiff_t
 x_create_bitmap_from_file (struct frame *f, Lisp_Object file)
 {
-  Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+  Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f);
 
 #ifdef HAVE_NTGUI
   return -1;  /* W32_TODO : bitmap support */
@@ -302,11 +298,10 @@ x_create_bitmap_from_file (struct frame *f, Lisp_Object file)
   id = x_allocate_bitmap_record (f);
   dpyinfo->bitmaps[id - 1].img = bitmap;
   dpyinfo->bitmaps[id - 1].refcount = 1;
-  dpyinfo->bitmaps[id - 1].file = xmalloc (SBYTES (file) + 1);
+  dpyinfo->bitmaps[id - 1].file = xlispstrdup (file);
   dpyinfo->bitmaps[id - 1].depth = 1;
   dpyinfo->bitmaps[id - 1].height = ns_image_width (bitmap);
   dpyinfo->bitmaps[id - 1].width = ns_image_height (bitmap);
-  strcpy (dpyinfo->bitmaps[id - 1].file, SSDATA (file));
   return id;
 #endif
 
@@ -345,11 +340,10 @@ x_create_bitmap_from_file (struct frame *f, Lisp_Object file)
   dpyinfo->bitmaps[id - 1].pixmap = bitmap;
   dpyinfo->bitmaps[id - 1].have_mask = 0;
   dpyinfo->bitmaps[id - 1].refcount = 1;
-  dpyinfo->bitmaps[id - 1].file = xmalloc (SBYTES (file) + 1);
+  dpyinfo->bitmaps[id - 1].file = xlispstrdup (file);
   dpyinfo->bitmaps[id - 1].depth = 1;
   dpyinfo->bitmaps[id - 1].height = height;
   dpyinfo->bitmaps[id - 1].width = width;
-  strcpy (dpyinfo->bitmaps[id - 1].file, SSDATA (file));
 
   return id;
 #endif /* HAVE_X_WINDOWS */
@@ -386,7 +380,7 @@ free_bitmap_record (Display_Info *dpyinfo, Bitmap_Record *bm)
 void
 x_destroy_bitmap (struct frame *f, ptrdiff_t id)
 {
-  Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+  Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f);
 
   if (id > 0)
     {
@@ -461,7 +455,7 @@ x_create_bitmap_mask (struct frame *f, ptrdiff_t id)
   unsigned long x, y, xp, xm, yp, ym;
   GC gc;
 
-  Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+  Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f);
 
   if (!(id > 0))
     return;
@@ -551,7 +545,6 @@ static Lisp_Object QCheuristic_mask;
 static Lisp_Object QCcolor_symbols;
 static Lisp_Object QCindex, QCmatrix, QCcolor_adjustment, QCmask, QCgeometry;
 static Lisp_Object QCcrop, QCrotation;
-static Lisp_Object QCcontent_type;
 
 /* Other symbols.  */
 
@@ -566,7 +559,6 @@ static void x_emboss (struct frame *, struct image *);
 static void x_build_heuristic_mask (struct frame *, struct image *,
                                     Lisp_Object);
 #ifdef WINDOWSNT
-extern Lisp_Object Vlibrary_cache;
 #define CACHE_IMAGE_TYPE(type, status) \
   do { Vlibrary_cache = Fcons (Fcons (type, status), Vlibrary_cache); } while (0)
 #else
@@ -1045,7 +1037,7 @@ void
 prepare_image_for_display (struct frame *f, struct image *img)
 {
   /* We're about to display IMG, so set its timestamp to `now'.  */
-  img->timestamp = current_emacs_time ();
+  img->timestamp = current_timespec ();
 
   /* If IMG doesn't have a pixmap yet, load it now, using the image
      type dependent loader function.  */
@@ -1363,14 +1355,12 @@ static void cache_image (struct frame *f, struct image *img);
 struct image_cache *
 make_image_cache (void)
 {
-  struct image_cache *c = xzalloc (sizeof *c);
-  int size;
+  struct image_cache *c = xmalloc (sizeof *c);
 
-  size = 50;
-  c->images = xmalloc (size * sizeof *c->images);
-  c->size = size;
-  size = IMAGE_CACHE_BUCKETS_SIZE * sizeof *c->buckets;
-  c->buckets = xzalloc (size);
+  c->size = 50;
+  c->used = c->refcount = 0;
+  c->images = xmalloc (c->size * sizeof *c->images);
+  c->buckets = xzalloc (IMAGE_CACHE_BUCKETS_SIZE * sizeof *c->buckets);
   return c;
 }
 
@@ -1486,7 +1476,7 @@ clear_image_cache (struct frame *f, Lisp_Object filter)
       else if (INTEGERP (Vimage_cache_eviction_delay))
        {
          /* Free cache based on timestamp.  */
-         EMACS_TIME old, t;
+         struct timespec old, t;
          double delay;
          ptrdiff_t nimages = 0;
 
@@ -1501,13 +1491,13 @@ clear_image_cache (struct frame *f, Lisp_Object filter)
            delay = 1600 * delay / nimages / nimages;
          delay = max (delay, 1);
 
-         t = current_emacs_time ();
-         old = sub_emacs_time (t, EMACS_TIME_FROM_DOUBLE (delay));
+         t = current_timespec ();
+         old = timespec_sub (t, dtotimespec (delay));
 
          for (i = 0; i < c->used; ++i)
            {
              struct image *img = c->images[i];
-             if (img && EMACS_TIME_LT (img->timestamp, old))
+             if (img && timespec_cmp (img->timestamp, old) < 0)
                {
                  free_image (f, img);
                  ++nfreed;
@@ -1770,7 +1760,7 @@ lookup_image (struct frame *f, Lisp_Object spec)
     }
 
   /* We're using IMG, so set its timestamp to `now'.  */
-  img->timestamp = current_emacs_time ();
+  img->timestamp = current_timespec ();
 
   /* Value is the image id.  */
   return img->id;
@@ -2718,10 +2708,13 @@ xbm_read_bitmap_data (struct frame *f, unsigned char *contents, unsigned char *e
      LA1 = xbm_scan (&s, end, buffer, &value)
 
 #define expect(TOKEN)          \
-     if (LA1 != (TOKEN))       \
-       goto failure;           \
-     else                      \
-       match ()
+  do                           \
+    {                          \
+      if (LA1 != (TOKEN))      \
+       goto failure;           \
+      match ();                        \
+    }                          \
+  while (0)
 
 #define expect_ident(IDENT)                                    \
      if (LA1 == XBM_TK_IDENT && strcmp (buffer, (IDENT)) == 0) \
@@ -3429,7 +3422,7 @@ xpm_image_p (Lisp_Object object)
 ptrdiff_t
 x_create_bitmap_from_xpm_data (struct frame *f, const char **bits)
 {
-  Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+  Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f);
   ptrdiff_t id;
   int rc;
   XpmAttributes attrs;
@@ -3981,10 +3974,13 @@ xpm_load_image (struct frame *f,
      LA1 = xpm_scan (&s, end, &beg, &len)
 
 #define expect(TOKEN)          \
-     if (LA1 != (TOKEN))       \
-       goto failure;           \
-     else                      \
-       match ()
+  do                           \
+    {                          \
+      if (LA1 != (TOKEN))      \
+       goto failure;           \
+      match ();                        \
+    }                          \
+  while (0)
 
 #define expect_ident(IDENT)                                    \
      if (LA1 == XPM_TK_IDENT \
@@ -4330,7 +4326,7 @@ lookup_rgb_color (struct frame *f, int r, int g, int b)
      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);
+  dpyinfo = FRAME_DISPLAY_INFO (f);
   if (dpyinfo->red_bits > 0)
     {
       unsigned long pr, pg, pb;
@@ -4825,7 +4821,7 @@ x_edge_detection (struct frame *f, struct image *img, Lisp_Object matrix,
 static void
 x_disable_image (struct frame *f, struct image *img)
 {
-  Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f);
+  Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f);
 #ifdef HAVE_NTGUI
   int n_planes = dpyinfo->n_planes * dpyinfo->n_cbits;
 #else
@@ -4937,7 +4933,7 @@ x_build_heuristic_mask (struct frame *f, struct image *img, Lisp_Object how)
   int row_width;
 #endif /* HAVE_NTGUI */
   int x, y;
-  bool rc, use_img_background;
+  bool use_img_background;
   unsigned long bg = 0;
 
   if (img->mask)
@@ -4946,9 +4942,8 @@ x_build_heuristic_mask (struct frame *f, struct image *img, Lisp_Object how)
 #ifndef HAVE_NTGUI
 #ifndef HAVE_NS
   /* Create an image and pixmap serving as mask.  */
-  rc = image_create_x_image_and_pixmap (f, img, img->width, img->height, 1,
-                                       &mask_img, 1);
-  if (!rc)
+  if (! image_create_x_image_and_pixmap (f, img, img->width, img->height, 1,
+                                        &mask_img, 1))
     return;
 #endif /* !HAVE_NS */
 #else
@@ -7208,7 +7203,21 @@ gif_image_p (Lisp_Object object)
 
 #ifdef HAVE_GIF
 
+/* Giflib before 5.0 didn't define these macros.  */
+#ifndef GIFLIB_MAJOR
+#define GIFLIB_MAJOR 4
+#endif
+
 #if defined (HAVE_NTGUI)
+
+/* Giflib before 5.0 didn't define these macros (used only if HAVE_NTGUI).  */
+#ifndef GIFLIB_MINOR
+#define GIFLIB_MINOR 0
+#endif
+#ifndef GIFLIB_RELEASE
+#define GIFLIB_RELEASE 0
+#endif
+
 /* winuser.h might define DrawText to DrawTextA or DrawTextW.
    Undefine before redefining to avoid a preprocessor warning.  */
 #ifdef DrawText
@@ -7225,14 +7234,19 @@ gif_image_p (Lisp_Object object)
 
 #endif /* HAVE_NTGUI */
 
-
 #ifdef WINDOWSNT
 
 /* GIF library details.  */
 DEF_IMGLIB_FN (int, DGifCloseFile, (GifFileType *));
 DEF_IMGLIB_FN (int, DGifSlurp, (GifFileType *));
+#if GIFLIB_MAJOR < 5
 DEF_IMGLIB_FN (GifFileType *, DGifOpen, (void *, InputFunc));
 DEF_IMGLIB_FN (GifFileType *, DGifOpenFileName, (const char *));
+#else
+DEF_IMGLIB_FN (GifFileType *, DGifOpen, (void *, InputFunc, int *));
+DEF_IMGLIB_FN (GifFileType *, DGifOpenFileName, (const char *, int *));
+DEF_IMGLIB_FN (char *, GifErrorString, (int));
+#endif
 
 static bool
 init_gif_functions (void)
@@ -7246,6 +7260,9 @@ init_gif_functions (void)
   LOAD_IMGLIB_FN (library, DGifSlurp);
   LOAD_IMGLIB_FN (library, DGifOpen);
   LOAD_IMGLIB_FN (library, DGifOpenFileName);
+#if GIFLIB_MAJOR >= 5
+  LOAD_IMGLIB_FN (library, GifErrorString);
+#endif
   return 1;
 }
 
@@ -7255,6 +7272,9 @@ init_gif_functions (void)
 #define fn_DGifSlurp           DGifSlurp
 #define fn_DGifOpen            DGifOpen
 #define fn_DGifOpenFileName    DGifOpenFileName
+#if 5 <= GIFLIB_MAJOR
+# define fn_GifErrorString     GifErrorString
+#endif
 
 #endif /* WINDOWSNT */
 
@@ -7311,6 +7331,9 @@ gif_load (struct frame *f, struct image *img)
   Lisp_Object specified_data = image_spec_value (img->spec, QCdata, NULL);
   unsigned long bgcolor = 0;
   EMACS_INT idx;
+#if GIFLIB_MAJOR >= 5
+  int gif_err;
+#endif
 
   if (NILP (specified_data))
     {
@@ -7322,12 +7345,22 @@ gif_load (struct frame *f, struct image *img)
        }
 
       /* Open the GIF file.  */
+#if GIFLIB_MAJOR < 5
       gif = fn_DGifOpenFileName (SSDATA (file));
       if (gif == NULL)
        {
          image_error ("Cannot open `%s'", file, Qnil);
          return 0;
        }
+#else
+      gif = fn_DGifOpenFileName (SSDATA (file), &gif_err);
+      if (gif == NULL)
+       {
+         image_error ("Cannot open `%s': %s",
+                      file, build_string (fn_GifErrorString (gif_err)));
+         return 0;
+       }
+#endif
     }
   else
     {
@@ -7343,12 +7376,22 @@ gif_load (struct frame *f, struct image *img)
       memsrc.len = SBYTES (specified_data);
       memsrc.index = 0;
 
+#if GIFLIB_MAJOR < 5
       gif = fn_DGifOpen (&memsrc, gif_read_from_memory);
       if (!gif)
        {
          image_error ("Cannot open memory source `%s'", img->spec, Qnil);
          return 0;
        }
+#else
+      gif = fn_DGifOpen (&memsrc, gif_read_from_memory, &gif_err);
+      if (!gif)
+       {
+         image_error ("Cannot open memory source `%s': %s",
+                      img->spec, build_string (fn_GifErrorString (gif_err)));
+         return 0;
+       }
+#endif
     }
 
   /* Before reading entire contents, check the declared image size. */
@@ -7529,7 +7572,7 @@ gif_load (struct frame *f, struct image *img)
            {
              while (subimg_height <= row)
                {
-                 lint_assume (pass < 3);
+                 assume (pass < 3);
                  row = interlace_start[++pass];
                }
 
@@ -7741,7 +7784,7 @@ enum imagemagick_keyword_index
     IMAGEMAGICK_WIDTH,
     IMAGEMAGICK_MAX_HEIGHT,
     IMAGEMAGICK_MAX_WIDTH,
-    IMAGEMAGICK_CONTENT_TYPE,
+    IMAGEMAGICK_FORMAT,
     IMAGEMAGICK_ROTATION,
     IMAGEMAGICK_CROP,
     IMAGEMAGICK_LAST
@@ -7766,7 +7809,7 @@ static struct image_keyword imagemagick_format[IMAGEMAGICK_LAST] =
     {":width",         IMAGE_INTEGER_VALUE,                    0},
     {":max-height",    IMAGE_INTEGER_VALUE,                    0},
     {":max-width",     IMAGE_INTEGER_VALUE,                    0},
-    {":content-type",  IMAGE_SYMBOL_VALUE,                     0},
+    {":format",                IMAGE_SYMBOL_VALUE,                     0},
     {":rotation",      IMAGE_NUMBER_VALUE,                     0},
     {":crop",          IMAGE_DONT_CHECK_VALUE_TYPE,            0}
   };
@@ -7846,37 +7889,232 @@ imagemagick_error (MagickWand *wand)
 }
 
 /* Possibly give ImageMagick some extra help to determine the image
-   type by supplying a "dummy" filename based on the Content-Type. */
+   type by supplying a "dummy" filename based on the Content-Type.  */
 
-static char*
-imagemagick_filename_hint (Lisp_Object spec)
+static char *
+imagemagick_filename_hint (Lisp_Object spec, char hint_buffer[MaxTextExtent])
 {
-  Lisp_Object content_type = image_spec_value (spec, QCcontent_type, NULL);
-  Lisp_Object symbol = intern ("image-content-type-suffixes");
-  Lisp_Object val;
-  char *name, *prefix = "/tmp/foo.";
-
-  if (NILP (Fboundp (symbol)))
-    return NULL;
+  Lisp_Object symbol = intern ("image-format-suffixes");
+  Lisp_Object val = find_symbol_value (symbol);
+  Lisp_Object format;
 
-  val = Fassq (content_type, Fsymbol_value (symbol));
   if (! CONSP (val))
     return NULL;
 
-  val = Fcdr (val);
-  if (! CONSP (val))
-    return NULL;
-
-  val = Fcar (val);
+  format = image_spec_value (spec, intern (":format"), NULL);
+  val = Fcar_safe (Fcdr_safe (Fassq (format, val)));
   if (! STRINGP (val))
     return NULL;
 
-  name = xmalloc (strlen (prefix) + SBYTES (val) + 1);
-  strcpy(name, prefix);
-  strcat(name, SDATA (val));
-  return name;
+  /* It's OK to truncate the hint if it has MaxTextExtent or more bytes,
+     as ImageMagick would ignore the extra bytes anyway.  */
+  snprintf (hint_buffer, MaxTextExtent, "/tmp/foo.%s", SSDATA (val));
+  return hint_buffer;
+}
+
+/* Animated images (e.g., GIF89a) are composed from one "master image"
+   (which is the first one, and then there's a number of images that
+   follow.  If following images have non-transparent colors, these are
+   composed "on top" of the master image.  So, in general, one has to
+   compute ann the preceding images to be able to display a particular
+   sub-image.
+
+   Computing all the preceding images is too slow, so we maintain a
+   cache of previously computed images.  We have to maintain a cache
+   separate from the image cache, because the images may be scaled
+   before display. */
+
+struct animation_cache
+{
+  MagickWand *wand;
+  int index;
+  struct timespec update_time;
+  struct animation_cache *next;
+  char signature[FLEXIBLE_ARRAY_MEMBER];
+};
+
+static struct animation_cache *animation_cache = NULL;
+
+static struct animation_cache *
+imagemagick_create_cache (char *signature)
+{
+  struct animation_cache *cache
+    = xmalloc (offsetof (struct animation_cache, signature)
+              + strlen (signature) + 1);
+  cache->wand = 0;
+  cache->index = 0;
+  cache->next = 0;
+  strcpy (cache->signature, signature);
+  return cache;
+}
+
+/* Discard cached images that haven't been used for a minute. */
+static void
+imagemagick_prune_animation_cache (void)
+{
+  struct animation_cache **pcache = &animation_cache;
+  struct timespec old = timespec_sub (current_timespec (),
+                                     make_timespec (60, 0));
+
+  while (*pcache)
+    {
+      struct animation_cache *cache = *pcache;
+      if (timespec_cmp (old, cache->update_time) <= 0)
+       pcache = &cache->next;
+      else
+       {
+         if (cache->wand)
+           DestroyMagickWand (cache->wand);
+         *pcache = cache->next;
+         xfree (cache);
+       }
+    }
+}
+
+static struct animation_cache *
+imagemagick_get_animation_cache (MagickWand *wand)
+{
+  char *signature = MagickGetImageSignature (wand);
+  struct animation_cache *cache;
+  struct animation_cache **pcache = &animation_cache;
+
+  imagemagick_prune_animation_cache ();
+
+  while (1)
+    {
+      cache = *pcache;
+      if (! cache)
+       {
+          *pcache = cache = imagemagick_create_cache (signature);
+          break;
+        }
+      if (strcmp (signature, cache->signature) == 0)
+       break;
+      pcache = &cache->next;
+    }
+
+  DestroyString (signature);
+  cache->update_time = current_timespec ();
+  return cache;
 }
 
+static MagickWand *
+imagemagick_compute_animated_image (MagickWand *super_wand, int ino)
+{
+  int i;
+  MagickWand *composite_wand;
+  size_t dest_width, dest_height;
+  struct animation_cache *cache = imagemagick_get_animation_cache (super_wand);
+
+  MagickSetIteratorIndex (super_wand, 0);
+
+  if (ino == 0 || cache->wand == NULL || cache->index > ino)
+    {
+      composite_wand = MagickGetImage (super_wand);
+      if (cache->wand)
+       DestroyMagickWand (cache->wand);
+    }
+  else
+    composite_wand = cache->wand;
+
+  dest_width = MagickGetImageWidth (composite_wand);
+  dest_height = MagickGetImageHeight (composite_wand);
+
+  for (i = max (1, cache->index + 1); i <= ino; i++)
+    {
+      MagickWand *sub_wand;
+      PixelIterator *source_iterator, *dest_iterator;
+      PixelWand **source, **dest;
+      size_t source_width, source_height;
+      ssize_t source_left, source_top;
+      MagickPixelPacket pixel;
+      DisposeType dispose;
+      ptrdiff_t lines = 0;
+
+      MagickSetIteratorIndex (super_wand, i);
+      sub_wand = MagickGetImage (super_wand);
+
+      MagickGetImagePage (sub_wand, &source_width, &source_height,
+                         &source_left, &source_top);
+
+      /* This flag says how to handle transparent pixels.  */
+      dispose = MagickGetImageDispose (sub_wand);
+
+      source_iterator = NewPixelIterator (sub_wand);
+      if (! source_iterator)
+       {
+         DestroyMagickWand (composite_wand);
+         DestroyMagickWand (sub_wand);
+         cache->wand = NULL;
+         image_error ("Imagemagick pixel iterator creation failed",
+                      Qnil, Qnil);
+         return NULL;
+       }
+
+      dest_iterator = NewPixelIterator (composite_wand);
+      if (! dest_iterator)
+       {
+         DestroyMagickWand (composite_wand);
+         DestroyMagickWand (sub_wand);
+         DestroyPixelIterator (source_iterator);
+         cache->wand = NULL;
+         image_error ("Imagemagick pixel iterator creation failed",
+                      Qnil, Qnil);
+         return NULL;
+       }
+
+      /* The sub-image may not start at origin, so move the destination
+        iterator to where the sub-image should start. */
+      if (source_top > 0)
+       {
+         PixelSetIteratorRow (dest_iterator, source_top);
+         lines = source_top;
+       }
+
+      while ((source = PixelGetNextIteratorRow (source_iterator, &source_width))
+            != NULL)
+       {
+         ptrdiff_t x;
+
+         /* Sanity check.  This shouldn't happen, but apparently
+            does in some pictures.  */
+         if (++lines >= dest_height)
+           break;
+
+         dest = PixelGetNextIteratorRow (dest_iterator, &dest_width);
+         for (x = 0; x < source_width; x++)
+           {
+             /* Sanity check.  This shouldn't happen, but apparently
+                also does in some pictures.  */
+             if (x + source_left > dest_width)
+               break;
+             /* Normally we only copy over non-transparent pixels,
+                but if the disposal method is "Background", then we
+                copy over all pixels.  */
+             if (dispose == BackgroundDispose ||
+                 PixelGetAlpha (source[x]))
+               {
+                 PixelGetMagickColor (source[x], &pixel);
+                 PixelSetMagickColor (dest[x + source_left], &pixel);
+               }
+           }
+         PixelSyncIterator (dest_iterator);
+       }
+
+      DestroyPixelIterator (source_iterator);
+      DestroyPixelIterator (dest_iterator);
+      DestroyMagickWand (sub_wand);
+    }
+
+  /* Cache a copy for the next iteration.  The current wand will be
+     destroyed by the caller. */
+  cache->wand = CloneMagickWand (composite_wand);
+  cache->index = ino;
+
+  return composite_wand;
+}
+
+
 /* Helper function for imagemagick_load, which does the actual loading
    given contents and size, apart from frame and image structures,
    passed from imagemagick_load.  Uses librimagemagick to do most of
@@ -7899,7 +8137,6 @@ imagemagick_load_image (struct frame *f, struct image *img,
   XImagePtr ximg;
   int x, y;
   MagickWand *image_wand;
-  MagickWand *ping_wand;
   PixelIterator *iterator;
   PixelWand **pixels, *bg_wand = NULL;
   MagickPixelPacket  pixel;
@@ -7910,6 +8147,7 @@ imagemagick_load_image (struct frame *f, struct image *img,
   int desired_width, desired_height;
   double rotation;
   int pixelwidth;
+  char hint_buffer[MaxTextExtent];
   char *filename_hint = NULL;
 
   /* Handle image index for image types who can contain more than one image.
@@ -7921,57 +8159,48 @@ imagemagick_load_image (struct frame *f, struct image *img,
   MagickWandGenesis ();
   image = image_spec_value (img->spec, QCindex, NULL);
   ino = INTEGERP (image) ? XFASTINT (image) : 0;
-  ping_wand = NewMagickWand ();
-  /* MagickSetResolution (ping_wand, 2, 2);   (Bug#10112)  */
-
-  if (! filename)
-    filename_hint = imagemagick_filename_hint (img->spec);
-
-  if (filename_hint)
-    MagickSetFilename (ping_wand, filename_hint);
+  image_wand = NewMagickWand ();
 
-  status = filename
-    ? MagickPingImage (ping_wand, filename)
-    : MagickPingImageBlob (ping_wand, contents, size);
+  if (filename)
+    status = MagickReadImage (image_wand, filename);
+  else
+    {
+      filename_hint = imagemagick_filename_hint (img->spec, hint_buffer);
+      MagickSetFilename (image_wand, filename_hint);
+      status = MagickReadImageBlob (image_wand, contents, size);
+    }
 
   if (status == MagickFalse)
     {
-      imagemagick_error (ping_wand);
-      DestroyMagickWand (ping_wand);
+      imagemagick_error (image_wand);
+      DestroyMagickWand (image_wand);
       return 0;
     }
 
-  if (ino < 0 || ino >= MagickGetNumberImages (ping_wand))
+  if (ino < 0 || ino >= MagickGetNumberImages (image_wand))
     {
       image_error ("Invalid image number `%s' in image `%s'",
                   image, img->spec);
-      DestroyMagickWand (ping_wand);
+      DestroyMagickWand (image_wand);
       return 0;
     }
 
-  if (MagickGetNumberImages (ping_wand) > 1)
+  if (MagickGetNumberImages (image_wand) > 1)
     img->lisp_data =
       Fcons (Qcount,
-             Fcons (make_number (MagickGetNumberImages (ping_wand)),
+             Fcons (make_number (MagickGetNumberImages (image_wand)),
                     img->lisp_data));
 
-  DestroyMagickWand (ping_wand);
-
-  /* Now we know how many images are inside the file.  If it's not a
-     bundle, the number is one.  Load the image data.  */
-
-  image_wand = NewMagickWand ();
-
-  if (filename_hint)
-    MagickSetFilename (image_wand, filename_hint);
-
-  if ((filename
-       ? MagickReadImage (image_wand, filename)
-       : MagickReadImageBlob (image_wand, contents, size))
-      == MagickFalse)
+  /* If we have an animated image, get the new wand based on the
+     "super-wand". */
+  if (MagickGetNumberImages (image_wand) > 1)
     {
-      imagemagick_error (image_wand);
-      goto imagemagick_error;
+      MagickWand *super_wand = image_wand;
+      image_wand = imagemagick_compute_animated_image (super_wand, ino);
+      if (! image_wand)
+       image_wand = super_wand;
+      else
+       DestroyMagickWand (super_wand);
     }
 
   /* Retrieve the frame's background color, for use later.  */
@@ -8208,16 +8437,11 @@ imagemagick_load_image (struct frame *f, struct image *img,
   /* `MagickWandTerminus' terminates the imagemagick environment.  */
   MagickWandTerminus ();
 
-  if (filename_hint)
-    free (filename_hint);
-
   return 1;
 
  imagemagick_error:
   DestroyMagickWand (image_wand);
   if (bg_wand) DestroyPixelWand (bg_wand);
-  if (filename_hint)
-    free (filename_hint);
 
   MagickWandTerminus ();
   /* TODO more cleanup.  */
@@ -8415,7 +8639,9 @@ DEF_IMGLIB_FN (int, gdk_pixbuf_get_n_channels, (const GdkPixbuf *));
 DEF_IMGLIB_FN (gboolean, gdk_pixbuf_get_has_alpha, (const GdkPixbuf *));
 DEF_IMGLIB_FN (int, gdk_pixbuf_get_bits_per_sample, (const GdkPixbuf *));
 
+#if ! GLIB_CHECK_VERSION (2, 36, 0)
 DEF_IMGLIB_FN (void, g_type_init, (void));
+#endif
 DEF_IMGLIB_FN (void, g_object_unref, (gpointer));
 DEF_IMGLIB_FN (void, g_error_free, (GError *));
 
@@ -8447,7 +8673,9 @@ init_svg_functions (void)
   LOAD_IMGLIB_FN (gdklib, gdk_pixbuf_get_has_alpha);
   LOAD_IMGLIB_FN (gdklib, gdk_pixbuf_get_bits_per_sample);
 
+#if ! GLIB_CHECK_VERSION (2, 36, 0)
   LOAD_IMGLIB_FN (gobject, g_type_init);
+#endif
   LOAD_IMGLIB_FN (gobject, g_object_unref);
   LOAD_IMGLIB_FN (glib, g_error_free);
 
@@ -8472,7 +8700,9 @@ init_svg_functions (void)
 #define fn_gdk_pixbuf_get_has_alpha      gdk_pixbuf_get_has_alpha
 #define fn_gdk_pixbuf_get_bits_per_sample gdk_pixbuf_get_bits_per_sample
 
+#if ! GLIB_CHECK_VERSION (2, 36, 0)
 #define fn_g_type_init                    g_type_init
+#endif
 #define fn_g_object_unref                 g_object_unref
 #define fn_g_error_free                   g_error_free
 #endif /* !WINDOWSNT  */
@@ -8557,9 +8787,12 @@ svg_load_image (struct frame *f,         /* Pointer to emacs frame structure.  *
   int x;
   int y;
 
-  /* g_type_init is a glib function that must be called prior to using
-     gnome type library functions.  */
+#if ! GLIB_CHECK_VERSION (2, 36, 0)
+  /* g_type_init is a glib function that must be called prior to
+     using gnome type library functions (obsolete since 2.36.0).  */
   fn_g_type_init ();
+#endif
+
   /* Make a handle to a new rsvg object.  */
   rsvg_handle = fn_rsvg_handle_new ();
 
@@ -9155,7 +9388,6 @@ non-numeric, there is no explicit limit on the size of images.  */);
   DEFSYM (Qpostscript, "postscript");
   DEFSYM (QCmax_width, ":max-width");
   DEFSYM (QCmax_height, ":max-height");
-  DEFSYM (QCcontent_type, ":content-type");
 #ifdef HAVE_GHOSTSCRIPT
   ADD_IMAGE_TYPE (Qpostscript);
   DEFSYM (QCloader, ":loader");
@@ -9171,6 +9403,16 @@ non-numeric, there is no explicit limit on the size of images.  */);
        make_number (PNG_LIBPNG_VER)
 #else
        make_number (-1)
+#endif
+       );
+  DEFSYM (Qlibgif_version, "libgif-version");
+  Fset (Qlibgif_version,
+#ifdef HAVE_GIF
+       make_number (GIFLIB_MAJOR * 10000
+                    + GIFLIB_MINOR * 100
+                    + GIFLIB_RELEASE)
+#else
+       make_number (-1)
 #endif
        );
 #endif