summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Ingebrigtsen <larsi@gnus.org>2022-07-14 18:58:12 +0200
committerLars Ingebrigtsen <larsi@gnus.org>2022-07-14 18:59:09 +0200
commit564f6c171eeaef4dea4b4fc2524c0b082dfbb531 (patch)
tree014595873d94947428e4a1a604f0d7602d5d51ea
parent4c542747bd40f3098a20aafe001889607f044188 (diff)
downloademacs-564f6c171eeaef4dea4b4fc2524c0b082dfbb531.tar.gz
Prune animation cache when images are no longer reachable
* 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.
-rw-r--r--lisp/image.el15
-rw-r--r--src/image.c41
2 files changed, 42 insertions, 14 deletions
diff --git a/lisp/image.el b/lisp/image.el
index bdaaec608ef..f4f73fd31f2 100644
--- a/lisp/image.el
+++ b/lisp/image.el
@@ -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))
diff --git a/src/image.c b/src/image.c
index ba2a1f4294e..f5004c2c4c7 100644
--- a/src/image.c
+++ b/src/image.c
@@ -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);