]> git.eshelyaron.com Git - emacs.git/commitdiff
Prune animation cache when images are no longer reachable
authorLars Ingebrigtsen <larsi@gnus.org>
Thu, 14 Jul 2022 16:58:12 +0000 (18:58 +0200)
committerLars Ingebrigtsen <larsi@gnus.org>
Thu, 14 Jul 2022 16:59:09 +0000 (18:59 +0200)
* lisp/image.el (image-animate-timeout): Eject cached animated
images that are no longer reachable (bug#56546).

* src/image.c (Fclear_image_cache): Allow specifying a cached
animated image to eject.
(gif_load, webp_load): Adjust what to use as the caching key --
the identity of the list itself is apparently changed by some
callers.

lisp/image.el
src/image.c

index bdaaec608efdea229b9d3ac40e9e1ec97198a39a..f4f73fd31f2060eaa6ca40a43360970e7c39540b 100644 (file)
@@ -966,9 +966,10 @@ for the animation speed.  A negative value means to animate in reverse."
   (plist-put (cdr image) :animate-tardiness
              (+ (* (plist-get (cdr image) :animate-tardiness) 0.9)
                 (float-time (time-since target-time))))
-  (let ((buffer (plist-get (cdr image) :animate-buffer))
-        (position (plist-get (cdr image) :animate-position)))
-    (when (and (buffer-live-p buffer)
+  (let* ((buffer (plist-get (cdr image) :animate-buffer))
+         (position (plist-get (cdr image) :animate-position))
+         (continue-animation
+          (and (buffer-live-p buffer)
                ;; If we have a :animate-position setting, the caller
                ;; has requested that the animation be stopped if the
                ;; image is no longer displayed in the buffer.
@@ -985,7 +986,13 @@ for the animation speed.  A negative value means to animate in reverse."
                (or (< (plist-get (cdr image) :animate-tardiness) 2)
                   (progn
                     (message "Stopping animation; animation possibly too big")
-                    nil)))
+                    nil)))))
+    (if (not continue-animation)
+        ;; Eject from the animation cache since we've decided not to
+        ;; keep updating it.  This helps stop unbounded RAM usage when
+        ;; doing, for instance, `g' in an eww buffer with animated
+        ;; images.
+        (clear-image-cache nil image)
       (let* ((time (prog1 (current-time)
                     (image-show-frame image n t)))
             (speed (image-animate-get-speed image))
index ba2a1f4294edba2074d5ad502f2ec872434734e3..f5004c2c4c778f4fd1800e5b60a89cfee256ae73 100644 (file)
@@ -186,6 +186,10 @@ static void free_color_table (void);
 static unsigned long *colors_in_color_table (int *n);
 #endif
 
+#if defined (HAVE_WEBP) || defined (HAVE_GIF)
+static void anim_prune_animation_cache (Lisp_Object);
+#endif
+
 #ifdef USE_CAIRO
 
 static Emacs_Pix_Container
@@ -2127,14 +2131,27 @@ clear_image_caches (Lisp_Object filter)
 }
 
 DEFUN ("clear-image-cache", Fclear_image_cache, Sclear_image_cache,
-       0, 1, 0,
+       0, 2, 0,
        doc: /* Clear the image cache.
 FILTER nil or a frame means clear all images in the selected frame.
 FILTER t means clear the image caches of all frames.
 Anything else means clear only those images that refer to FILTER,
-which is then usually a filename.  */)
-  (Lisp_Object filter)
+which is then usually a filename.
+
+This function also clears the image animation cache.  If
+ANIMATION-CACHE is non-nil, only the image spec `eq' with
+ANIMATION-CACHE is removed, and other image cache entries are not
+evicted.  */)
+  (Lisp_Object filter, Lisp_Object animation_cache)
 {
+  if (!NILP (animation_cache))
+    {
+#if defined (HAVE_WEBP) || defined (HAVE_GIF)
+      anim_prune_animation_cache (XCDR (animation_cache));
+#endif
+      return Qnil;
+    }
+
   if (! (NILP (filter) || FRAMEP (filter)))
     clear_image_caches (filter);
   else
@@ -3048,9 +3065,11 @@ anim_create_cache (Lisp_Object spec)
 }
 
 /* Discard cached images that haven't been used for a minute.  If
-   CLEAR, remove all animation cache entries.  */
+   CLEAR is t, remove all animation cache entries.  If CLEAR is
+   anything other than nil or t, only remove the entries that have a
+   spec `eq' to CLEAR.  */
 static void
-anim_prune_animation_cache (bool clear)
+anim_prune_animation_cache (Lisp_Object clear)
 {
   struct anim_cache **pcache = &anim_cache;
   struct timespec old = timespec_sub (current_timespec (),
@@ -3059,7 +3078,9 @@ anim_prune_animation_cache (bool clear)
   while (*pcache)
     {
       struct anim_cache *cache = *pcache;
-      if (clear || timespec_cmp (old, cache->update_time) > 0)
+      if (EQ (clear, Qt)
+         || (EQ (clear, Qnil) && timespec_cmp (old, cache->update_time) > 0)
+         || EQ (clear, cache->spec))
        {
          if (cache->handle)
            cache->destructor (cache);
@@ -3079,7 +3100,7 @@ anim_get_animation_cache (Lisp_Object spec)
   struct anim_cache *cache;
   struct anim_cache **pcache = &anim_cache;
 
-  anim_prune_animation_cache (false);
+  anim_prune_animation_cache (Qnil);
 
   while (1)
     {
@@ -9020,7 +9041,7 @@ gif_load (struct frame *f, struct image *img)
   if (!NILP (image_number))
     {
       /* If this is an animated image, create a cache for it.  */
-      cache = anim_get_animation_cache (img->spec);
+      cache = anim_get_animation_cache (XCDR (img->spec));
       /* We have an old cache entry, so use it.  */
       if (cache->handle)
        {
@@ -9722,7 +9743,7 @@ webp_load (struct frame *f, struct image *img)
       /* Animated image.  */
       int timestamp;
 
-      struct anim_cache* cache = anim_get_animation_cache (img->spec);
+      struct anim_cache* cache = anim_get_animation_cache (XCDR (img->spec));
       /* Get the next frame from the animation cache.  */
       if (cache->handle && cache->index == idx - 1)
        {
@@ -11998,7 +12019,7 @@ void
 image_prune_animation_caches (bool clear)
 {
 #if defined (HAVE_WEBP) || defined (HAVE_GIF)
-  anim_prune_animation_cache (clear);
+  anim_prune_animation_cache (clear? Qt: Qnil);
 #endif
 #ifdef HAVE_IMAGEMAGICK
   imagemagick_prune_animation_cache (clear);