From 3a2ddc2d1824a17319fa053655fce4861c6cbb18 Mon Sep 17 00:00:00 2001 From: Glenn Morris Date: Thu, 15 Aug 2013 23:33:08 -0700 Subject: [PATCH] Add functions to change the speed of animated images * lisp/image-mode.el (image-mode-map): Add menu items to reverse, increase, decrease, reset animation speed. (image--set-speed, image-increase-speed, image-decrease-speed) (image-reverse-speed, image-reset-speed): New functions. (image-mode-map): Add bindings for speed commands. * lisp/image.el (image-animate-get-speed, image-animate-set-speed): New functions. (image-animate-timeout): Respect image :speed property. * etc/NEWS: Mention this. --- etc/NEWS | 4 +++ lisp/ChangeLog | 12 ++++++++ lisp/image-mode.el | 69 ++++++++++++++++++++++++++++++++++++++++++++-- lisp/image.el | 36 ++++++++++++++++++------ 4 files changed, 111 insertions(+), 10 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index ef76f308303..8bf72ddfedd 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -347,6 +347,8 @@ directory, respectively. `f' (`image-next-frame') and `b' (`image-previous-frame') visit the next or previous frame. `F' (`image-goto-frame') shows a specific frame. +*** New commands to speed up, slow down, or reverse animation. + --- *** The command `image-mode-fit-frame' deletes other windows. When toggling, it restores the frame's previous window configuration. @@ -683,6 +685,8 @@ Emacs uses `image-default-frame-delay'. *** New functions `image-current-frame' and `image-show-frame' for getting and setting the current frame of a multi-frame image. +*** You can change the speed of animated images. + ** Changes in encoding and decoding of text --- diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 9119b34d52c..cad6788f0e8 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,15 @@ +2013-08-16 Glenn Morris + + * image-mode.el (image-mode-map): Add menu items to reverse, + increase, decrease, reset animation speed. + (image--set-speed, image-increase-speed, image-decrease-speed) + (image-reverse-speed, image-reset-speed): New functions. + (image-mode-map): Add bindings for speed commands. + + * image.el (image-animate-get-speed, image-animate-set-speed): + New functions. + (image-animate-timeout): Respect image :speed property. + 2013-08-15 Stefan Monnier * emacs-lisp/debug.el (debugger-setup-buffer): Put point on the diff --git a/lisp/image-mode.el b/lisp/image-mode.el index 30dfd045b46..f9bbbcdb1ab 100644 --- a/lisp/image-mode.el +++ b/lisp/image-mode.el @@ -354,6 +354,10 @@ call." (define-key map "b" 'image-previous-frame) (define-key map "n" 'image-next-file) (define-key map "p" 'image-previous-file) + (define-key map "a+" 'image-increase-speed) + (define-key map "a-" 'image-decrease-speed) + (define-key map "a0" 'image-reset-speed) + (define-key map "ar" 'image-reverse-speed) (define-key map [remap forward-char] 'image-forward-hscroll) (define-key map [remap backward-char] 'image-backward-hscroll) (define-key map [remap right-char] 'image-forward-hscroll) @@ -412,7 +416,23 @@ call." (image-toggle-animation))) :style toggle :selected image-animate-loop :active image-multi-frame - :help "Animate images once, or forever?"] + :help "Animate images once, or forever?"] + ["Reverse Animation" image-reverse-speed + :style toggle :selected (let ((image (image-get-display-property))) + (and image (< + (image-animate-get-speed image) + 0))) + :active image-multi-frame + :help "Reverse direction of this image's animation?"] + ["Speed Up Animation" image-increase-speed + :active image-multi-frame + :help "Speed up this image's animation"] + ["Slow Down Animation" image-decrease-speed + :active image-multi-frame + :help "Slow down this image's animation"] + ["Reset Animation Speed" image-reset-speed + :active image-multi-frame + :help "Reset the speed of this image's animation"] ["Next Frame" image-next-frame :active image-multi-frame :help "Show the next frame of this image"] ["Previous Frame" image-previous-frame :active image-multi-frame @@ -437,7 +457,10 @@ call." (defun image-mode () "Major mode for image files. You can use \\\\[image-toggle-display] -to toggle between display as an image and display as text." +to toggle between display as an image and display as text. + +Key bindings: +\\{image-mode-map}" (interactive) (condition-case err (progn @@ -703,6 +726,48 @@ Otherwise it plays once, then stops." (image-animate image index (if image-animate-loop t))))))))) +(defun image--set-speed (speed &optional multiply) + "Set speed of an animated image to SPEED. +If MULTIPLY is non-nil, treat SPEED as a multiplication factor. +If SPEED is `reset', reset the magnitude of the speed to 1." + (let ((image (image-get-display-property))) + (cond + ((null image) + (error "No image is present")) + ((null image-multi-frame) + (message "No image animation.")) + (t + (if (eq speed 'reset) + (setq speed (if (< (image-animate-get-speed image) 0) + -1 1) + multiply nil)) + (image-animate-set-speed image speed multiply) + ;; FIXME Hack to refresh an active image. + (when (image-animate-timer image) + (image-toggle-animation) + (image-toggle-animation)) + (message "Image speed is now %s" (image-animate-get-speed image)))))) + +(defun image-increase-speed () + "Increase the speed of current animated image by a factor of 2." + (interactive) + (image--set-speed 2 t)) + +(defun image-decrease-speed () + "Decrease the speed of current animated image by a factor of 2." + (interactive) + (image--set-speed 0.5 t)) + +(defun image-reverse-speed () + "Reverse the animation of the current image." + (interactive) + (image--set-speed -1 t)) + +(defun image-reset-speed () + "Reset the animation speed of the current image." + (interactive) + (image--set-speed 'reset)) + (defun image-goto-frame (n &optional relative) "Show frame N of a multi-frame image. Optional argument OFFSET non-nil means interpret N as relative to the diff --git a/lisp/image.el b/lisp/image.el index d213c5d6d79..6c15a7d0b96 100644 --- a/lisp/image.el +++ b/lisp/image.el @@ -687,6 +687,19 @@ do not check N is within the range of frames present in the image." (plist-put (cdr image) :index n) (force-window-update)) +(defun image-animate-get-speed (image) + "Return the speed factor for animating IMAGE." + (or (plist-get (cdr image) :speed) 1)) + +(defun image-animate-set-speed (image value &optional multiply) + "Set the speed factor for animating IMAGE to VALUE. +With optional argument MULTIPLY non-nil, treat VALUE as a +multiplication factor for the current value." + (plist-put (cdr image) :speed + (if multiply + (* value (image-animate-get-speed image)) + value))) + ;; FIXME? The delay may not be the same for different sub-images, ;; hence we need to call image-multi-frame-p to return it. ;; But it also returns count, so why do we bother passing that as an @@ -700,21 +713,28 @@ TIME-ELAPSED is the total time that has elapsed since LIMIT determines when to stop. If t, loop forever. If nil, stop after displaying the last animation frame. Otherwise, stop after LIMIT seconds have elapsed. -The minimum delay between successive frames is `image-minimum-frame-delay'." +The minimum delay between successive frames is `image-minimum-frame-delay'. + +If the image has a non-nil :speed property, it acts as a multiplier +for the animation speed. A negative value means to animate in reverse." (image-show-frame image n t) - (setq n (1+ n)) - (let* ((time (float-time)) + (let* ((speed (image-animate-get-speed image)) + (time (float-time)) (animation (image-multi-frame-p image)) ;; Subtract off the time we took to load the image from the ;; stated delay time. - (delay (max (+ (or (cdr animation) image-default-frame-delay) + (delay (max (+ (* (or (cdr animation) image-default-frame-delay) + (/ 1 (abs speed))) time (- (float-time))) image-minimum-frame-delay)) done) - (if (>= n count) - (if limit - (setq n 0) - (setq done t))) + (setq n (if (< speed 0) + (1- n) + (1+ n))) + (if limit + (cond ((>= n count) (setq n 0)) + ((< n 0) (setq n (1- count)))) + (and (or (>= n count) (< n 0)) (setq done t))) (setq time-elapsed (+ delay time-elapsed)) (if (numberp limit) (setq done (>= time-elapsed limit))) -- 2.39.2