Simplify EMACS_TIME-related code.
[bpt/emacs.git] / src / image.c
index 3c5ee4c..bcc0fcd 100644 (file)
@@ -145,7 +145,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 +153,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);
@@ -563,7 +563,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
@@ -1042,7 +1041,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.  */
@@ -1481,7 +1480,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;
 
@@ -1496,13 +1495,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;
@@ -1765,7 +1764,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;
@@ -2713,10 +2712,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) \
@@ -3976,10 +3978,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 \
@@ -4932,7 +4937,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)
@@ -4941,9 +4946,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
@@ -7869,36 +7873,127 @@ imagemagick_filename_hint (Lisp_Object spec, char hint_buffer[MaxTextExtent])
    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.  */
+   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. */
 
-static MagickWand *animation_cache = NULL;
-static int animation_index = 0;
+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 || animation_cache == NULL)
-    composite_wand = MagickGetImage (super_wand);
+  if (ino == 0 || cache->wand == NULL || cache->index > ino)
+    {
+      composite_wand = MagickGetImage (super_wand);
+      if (cache->wand)
+       DestroyMagickWand (cache->wand);
+    }
   else
-    composite_wand = animation_cache;
+    composite_wand = cache->wand;
 
-  for (i = max (1, animation_index + 1); i <= ino; i++)
+  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, dest_width;
+      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);
@@ -7906,6 +8001,7 @@ imagemagick_compute_animated_image (MagickWand *super_wand, int ino)
        {
          DestroyMagickWand (composite_wand);
          DestroyMagickWand (sub_wand);
+         cache->wand = NULL;
          image_error ("Imagemagick pixel iterator creation failed",
                       Qnil, Qnil);
          return NULL;
@@ -7917,34 +8013,48 @@ imagemagick_compute_animated_image (MagickWand *super_wand, int ino)
          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++)
            {
-             if (dispose == BackgroundDispose)
+             /* 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], &pixel);
-               }
-             else
-               {
-                 /* Copy over non-transparent pixels. */
-                 if (PixelGetAlpha (source[x]))
-                   {
-                     PixelGetMagickColor (source[x], &pixel);
-                     PixelSetMagickColor (dest[x], &pixel);
-                   }
+                 PixelSetMagickColor (dest[x + source_left], &pixel);
                }
            }
-         PixelSyncIterator(dest_iterator);
+         PixelSyncIterator (dest_iterator);
        }
 
       DestroyPixelIterator (source_iterator);
@@ -7954,8 +8064,8 @@ imagemagick_compute_animated_image (MagickWand *super_wand, int ino)
 
   /* Cache a copy for the next iteration.  The current wand will be
      destroyed by the caller. */
-  animation_cache = CloneMagickWand (composite_wand);
-  animation_index = ino;
+  cache->wand = CloneMagickWand (composite_wand);
+  cache->index = ino;
 
   return composite_wand;
 }
@@ -7983,7 +8093,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;
@@ -8006,60 +8115,38 @@ 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)  */
+  image_wand = NewMagickWand ();
 
   if (filename)
-    status = MagickPingImage (ping_wand, filename);
+    status = MagickReadImage (image_wand, filename);
   else
     {
       filename_hint = imagemagick_filename_hint (img->spec, hint_buffer);
-      MagickSetFilename (ping_wand, filename_hint);
-      status = MagickPingImageBlob (ping_wand, contents, size);
+      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)
-    status = MagickReadImage (image_wand, filename);
-  else
-    {
-      MagickSetFilename (image_wand, filename_hint);
-      status = MagickReadImageBlob (image_wand, contents, size);
-    }
-
-  if (status == MagickFalse)
-    {
-      imagemagick_error (image_wand);
-      goto imagemagick_error;
-    }
-
   /* If we have an animated image, get the new wand based on the
      "super-wand". */
   if (MagickGetNumberImages (image_wand) > 1)