Some changes and re-organization for animated gif support.
* lisp/image.el (image-animate-max-time): Moved to image-mode.el. (create-animated-image): Remove unnecessary function. (image-animate): Rename from image-animate-start. New arg. (image-animate-stop): Removed; just use image-animate-timer. (image-animate-timer): Use car-safe. (image-animate-timeout): Rename argument. * lisp/image-mode.el (image-toggle-animation): New command. (image-mode-map): Bind it to RET. (image-mode): Update message. (image-toggle-display-image): Avoid a spurious cache flush. (image-transform-rotation): Doc fix. (image-transform-properties): Return quickly in the normal case. (image-animate-loop): Rename from image-animate-max-time.
This commit is contained in:
parent
2c631e0e82
commit
18af70d025
4 changed files with 171 additions and 141 deletions
22
etc/NEWS
22
etc/NEWS
|
@ -693,6 +693,14 @@ listing object name completions when being sent text via
|
|||
|
||||
*** An API for manipulating SQL product definitions has been added.
|
||||
|
||||
** Image mode
|
||||
|
||||
*** RET (`image-toggle-animation') toggles animation, if the displayed
|
||||
image can be animated.
|
||||
|
||||
*** Option `image-animate-loop', if non-nil, loops the animation.
|
||||
If nil, `image-toggle-animation' plays the animation once.
|
||||
|
||||
** sregex.el is now obsolete, since rx.el is a strict superset.
|
||||
|
||||
** s-region.el and pc-select are now declared obsolete,
|
||||
|
@ -980,12 +988,14 @@ i.e. via menu entries of the form `(menu-item "--")'.
|
|||
|
||||
** Image API
|
||||
|
||||
*** When the image type is one of listed in `image-animated-types'
|
||||
and the number of sub-images in the image is more than one, then the
|
||||
new function `create-animated-image' creates an animated image where
|
||||
sub-images are displayed successively with the duration defined by
|
||||
`image-animate-max-time' and the delay between sub-images defined
|
||||
by the Graphic Control Extension of the image.
|
||||
*** Animated images support (currently animated gifs only).
|
||||
|
||||
**** `image-animated-p' returns non-nil if an image can be animated.
|
||||
|
||||
**** `image-animate' animates a supplied image spec.
|
||||
|
||||
**** `image-animate-timer' returns the timer object for an image that
|
||||
is being animated.
|
||||
|
||||
*** `image-extension-data' is renamed to `image-metadata'.
|
||||
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
2011-06-07 Chong Yidong <cyd@stupidchicken.com>
|
||||
|
||||
* image-mode.el (image-toggle-animation): New command.
|
||||
(image-mode-map): Bind it to RET.
|
||||
(image-mode): Update message.
|
||||
(image-toggle-display-image): Avoid a spurious cache flush.
|
||||
(image-transform-rotation): Doc fix.
|
||||
(image-transform-properties): Return quickly in the normal case.
|
||||
(image-animate-loop): Rename from image-animate-max-time.
|
||||
|
||||
* image.el (image-animate-max-time): Moved to image-mode.el.
|
||||
(create-animated-image): Remove unnecessary function.
|
||||
(image-animate): Rename from image-animate-start. New arg.
|
||||
(image-animate-stop): Removed; just use image-animate-timer.
|
||||
(image-animate-timer): Use car-safe.
|
||||
(image-animate-timeout): Rename argument.
|
||||
|
||||
2011-06-07 Martin Rudalics <rudalics@gmx.at>
|
||||
|
||||
* window.el (get-lru-window, get-largest-window): Move here from
|
||||
|
|
|
@ -308,6 +308,7 @@ This function assumes the current frame has only one window."
|
|||
(define-key map "\C-c\C-c" 'image-toggle-display)
|
||||
(define-key map (kbd "SPC") 'image-scroll-up)
|
||||
(define-key map (kbd "DEL") 'image-scroll-down)
|
||||
(define-key map (kbd "RET") 'image-toggle-animation)
|
||||
(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)
|
||||
|
@ -373,16 +374,26 @@ to toggle between display as an image and display as text."
|
|||
(add-hook 'change-major-mode-hook 'image-toggle-display-text nil t)
|
||||
(add-hook 'after-revert-hook 'image-after-revert-hook nil t)
|
||||
(run-mode-hooks 'image-mode-hook)
|
||||
(message "%s" (concat
|
||||
(substitute-command-keys
|
||||
"Type \\[image-toggle-display] to view the image as ")
|
||||
(if (image-get-display-property)
|
||||
"text" "an image") ".")))
|
||||
(let ((image (image-get-display-property))
|
||||
(msg1 (substitute-command-keys
|
||||
"Type \\[image-toggle-display] to view the image as ")))
|
||||
(cond
|
||||
((null image)
|
||||
(message "%s" (concat msg1 "an image.")))
|
||||
((image-animated-p image)
|
||||
(message "%s"
|
||||
(concat msg1 "text, or "
|
||||
(substitute-command-keys
|
||||
"\\[image-toggle-animation] to animate."))))
|
||||
(t
|
||||
(message "%s" (concat msg1 "text."))))))
|
||||
|
||||
(error
|
||||
(image-mode-as-text)
|
||||
(funcall
|
||||
(if (called-interactively-p 'any) 'error 'message)
|
||||
"Cannot display image: %s" (cdr err)))))
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode image-minor-mode
|
||||
"Toggle Image minor mode.
|
||||
|
@ -484,25 +495,20 @@ was inserted."
|
|||
(buffer-substring-no-properties (point-min) (point-max)))
|
||||
filename))
|
||||
(type (image-type file-or-data nil data-p))
|
||||
;; Don't use create-animated-image here; that would start the
|
||||
;; timer, which works by altering the spec destructively.
|
||||
;; But we still need to append the transformation properties,
|
||||
;; which would make a new list.
|
||||
(image (create-image file-or-data type data-p))
|
||||
(inhibit-read-only t)
|
||||
(buffer-undo-list t)
|
||||
(modified (buffer-modified-p))
|
||||
props)
|
||||
|
||||
;; Discard any stale image data before looking it up again.
|
||||
(image-flush image)
|
||||
(setq image (append image (image-transform-properties image)))
|
||||
(setq props
|
||||
`(display ,image
|
||||
intangible ,image
|
||||
rear-nonsticky (display intangible)
|
||||
read-only t front-sticky (read-only)))
|
||||
(image-flush image)
|
||||
;; Begin the animation, if any.
|
||||
(image-animate-start image)
|
||||
|
||||
(let ((buffer-file-truename nil)) ; avoid changing dir mtime by lock_file
|
||||
(add-text-properties (point-min) (point-max) props)
|
||||
|
@ -544,6 +550,37 @@ the image by calling `image-mode'."
|
|||
(get-buffer-window-list (current-buffer) 'nomini 'visible))
|
||||
(image-toggle-display-image)))
|
||||
|
||||
|
||||
;;; Animated images
|
||||
|
||||
(defcustom image-animate-loop nil
|
||||
"Whether to play animated images on a loop in Image mode."
|
||||
:type 'boolean
|
||||
:version "24.1"
|
||||
:group 'image)
|
||||
|
||||
(defun image-toggle-animation ()
|
||||
"Start or stop animating the current image."
|
||||
(interactive)
|
||||
(let ((image (image-get-display-property))
|
||||
animation)
|
||||
(cond
|
||||
((null image)
|
||||
(error "No image is present"))
|
||||
((null (setq animation (image-animated-p image)))
|
||||
(message "No image animation."))
|
||||
(t
|
||||
(let ((timer (image-animate-timer image)))
|
||||
(if timer
|
||||
(cancel-timer timer)
|
||||
(let ((index (plist-get (cdr image) :index)))
|
||||
;; If we're at the end, restart.
|
||||
(and index
|
||||
(>= index (1- (car animation)))
|
||||
(setq index nil))
|
||||
(image-animate image index
|
||||
(if image-animate-loop t)))))))))
|
||||
|
||||
|
||||
;;; Support for bookmark.el
|
||||
(declare-function bookmark-make-record-default
|
||||
|
@ -589,33 +626,38 @@ Its value should be one of the following:
|
|||
- `fit-width', meaning to fit the image to the window width.
|
||||
- A number, which is a scale factor (the default size is 100).")
|
||||
|
||||
(defvar image-transform-rotation 0.0)
|
||||
(defvar image-transform-rotation 0.0
|
||||
"Rotation angle for the image in the current Image mode buffer.")
|
||||
|
||||
(defun image-transform-properties (display)
|
||||
"Return rescaling/rotation properties for the Image mode buffer.
|
||||
These properties are suitable for appending to an image spec;
|
||||
they are determined by the variables `image-transform-resize' and
|
||||
`image-transform-rotation'.
|
||||
(defun image-transform-properties (spec)
|
||||
"Return rescaling/rotation properties for image SPEC.
|
||||
These properties are determined by the Image mode variables
|
||||
`image-transform-resize' and `image-transform-rotation'. The
|
||||
return value is suitable for appending to an image spec.
|
||||
|
||||
Recaling and rotation properties only take effect if Emacs is
|
||||
compiled with ImageMagick support."
|
||||
(let* ((size (image-size display t))
|
||||
(height
|
||||
(cond
|
||||
((numberp image-transform-resize)
|
||||
(unless (= image-transform-resize 100)
|
||||
(* image-transform-resize (cdr size))))
|
||||
((eq image-transform-resize 'fit-height)
|
||||
(- (nth 3 (window-inside-pixel-edges))
|
||||
(nth 1 (window-inside-pixel-edges))))))
|
||||
(width (if (eq image-transform-resize 'fit-width)
|
||||
(- (nth 2 (window-inside-pixel-edges))
|
||||
(nth 0 (window-inside-pixel-edges))))))
|
||||
;;TODO fit-to-* should consider the rotation angle
|
||||
`(,@(if height (list :height height))
|
||||
,@(if width (list :width width))
|
||||
,@(if (not (equal 0.0 image-transform-rotation))
|
||||
(list :rotation image-transform-rotation)))))
|
||||
(when (or image-transform-resize
|
||||
(not (equal image-transform-rotation 0.0)))
|
||||
;; Note: `image-size' looks up and thus caches the untransformed
|
||||
;; image. There's no easy way to prevent that.
|
||||
(let* ((size (image-size spec t))
|
||||
(height
|
||||
(cond
|
||||
((numberp image-transform-resize)
|
||||
(unless (= image-transform-resize 100)
|
||||
(* image-transform-resize (cdr size))))
|
||||
((eq image-transform-resize 'fit-height)
|
||||
(- (nth 3 (window-inside-pixel-edges))
|
||||
(nth 1 (window-inside-pixel-edges))))))
|
||||
(width (if (eq image-transform-resize 'fit-width)
|
||||
(- (nth 2 (window-inside-pixel-edges))
|
||||
(nth 0 (window-inside-pixel-edges))))))
|
||||
;;TODO fit-to-* should consider the rotation angle
|
||||
`(,@(if height (list :height height))
|
||||
,@(if width (list :width width))
|
||||
,@(if (not (equal 0.0 image-transform-rotation))
|
||||
(list :rotation image-transform-rotation))))))
|
||||
|
||||
(defun image-transform-set-scale (scale)
|
||||
"Prompt for a number, and resize the current image by that amount.
|
||||
|
|
161
lisp/image.el
161
lisp/image.el
|
@ -590,110 +590,14 @@ Example:
|
|||
|
||||
;;; Animated image API
|
||||
|
||||
(defcustom image-animate-max-time nil
|
||||
"Time in seconds to animate images.
|
||||
If the value is nil, play animations once.
|
||||
If the value is t, loop forever."
|
||||
:type '(choice (const :tag "Play once" nil)
|
||||
(const :tag "Loop forever" t)
|
||||
integer)
|
||||
:version "24.1"
|
||||
:group 'image)
|
||||
|
||||
(defconst image-animated-types '(gif)
|
||||
"List of supported animated image types.")
|
||||
|
||||
;;;###autoload
|
||||
(defun create-animated-image (file-or-data &optional type data-p &rest props)
|
||||
"Create an animated image, and begin animating it.
|
||||
FILE-OR-DATA is an image file name or image data.
|
||||
Optional TYPE is a symbol describing the image type. If TYPE is omitted
|
||||
or nil, try to determine the image type from its first few bytes
|
||||
of image data. If that doesn't work, and FILE-OR-DATA is a file name,
|
||||
use its file extension as image type.
|
||||
Optional DATA-P non-nil means FILE-OR-DATA is a string containing image data.
|
||||
Optional PROPS are additional image attributes to assign to the image,
|
||||
like, e.g. `:mask MASK'.
|
||||
Value is the image created, or nil if images of type TYPE are not supported.
|
||||
|
||||
Images should not be larger than specified by `max-image-size'."
|
||||
(setq type (image-type file-or-data type data-p))
|
||||
(when (image-type-available-p type)
|
||||
(let* ((animate (memq type image-animated-types))
|
||||
(image
|
||||
(append (list 'image :type type (if data-p :data :file) file-or-data)
|
||||
(if animate '(:index 0))
|
||||
props)))
|
||||
(if animate
|
||||
(image-animate-start image))
|
||||
image)))
|
||||
|
||||
(defun image-animate-timer (image)
|
||||
"Return the animation timer for image IMAGE."
|
||||
;; See cancel-function-timers
|
||||
(let ((tail timer-list) timer)
|
||||
(while tail
|
||||
(setq timer (car tail)
|
||||
tail (cdr tail))
|
||||
(if (and (eq (aref timer 5) #'image-animate-timeout)
|
||||
(consp (aref timer 6))
|
||||
(eq (car (aref timer 6)) image))
|
||||
(setq tail nil)
|
||||
(setq timer nil)))
|
||||
timer))
|
||||
|
||||
(defun image-animate-start (image)
|
||||
"Start animating the image IMAGE.
|
||||
The variable `image-animate-max-time' determines how long to
|
||||
animate for."
|
||||
(let ((anim (image-animated-p image))
|
||||
delay ; in seconds
|
||||
timer)
|
||||
(when anim
|
||||
(if (setq timer (image-animate-timer image))
|
||||
(cancel-timer timer))
|
||||
(setq delay (max (* (cdr anim) 0.01) 0.025))
|
||||
(run-with-timer 0.2 nil #'image-animate-timeout
|
||||
image 0 (car anim)
|
||||
delay 0 image-animate-max-time))))
|
||||
|
||||
(defun image-animate-stop (image)
|
||||
"Stop animation of image."
|
||||
(let ((timer (image-animate-timer image)))
|
||||
(when timer
|
||||
(cancel-timer timer))))
|
||||
|
||||
(defun image-animate-timeout (image n count delay time-elapsed max)
|
||||
"Display animation frame N of IMAGE.
|
||||
N=0 refers to the initial animation frame.
|
||||
COUNT is the total number of frames in the animation.
|
||||
DELAY is the time between animation frames, in seconds.
|
||||
TIME-ELAPSED is the total time that has elapsed since
|
||||
`image-animate-start' was called.
|
||||
MAX determines when to stop. If t, loop forever. If nil, stop
|
||||
after displaying the last animation frame. Otherwise, stop
|
||||
after MAX seconds have elapsed."
|
||||
(let (done)
|
||||
(plist-put (cdr image) :index n)
|
||||
(force-window-update)
|
||||
(setq n (1+ n))
|
||||
(if (>= n count)
|
||||
(if max
|
||||
(setq n 0)
|
||||
(setq done t)))
|
||||
(setq time-elapsed (+ delay time-elapsed))
|
||||
(if (numberp max)
|
||||
(setq done (>= time-elapsed max)))
|
||||
(unless done
|
||||
(run-with-timer delay nil 'image-animate-timeout
|
||||
image n count delay
|
||||
time-elapsed max))))
|
||||
|
||||
(defun image-animated-p (image)
|
||||
"Return non-nil if image is animated.
|
||||
Actually, return value is a cons (IMAGES . DELAY) where IMAGES
|
||||
is the number of sub-images in the animated image, and DELAY
|
||||
is the delay in 100ths of a second until the next sub-image
|
||||
"Return non-nil if image can be animated.
|
||||
Actually, the return value is a cons (NIMAGES . DELAY), where
|
||||
NIMAGES is the number of sub-images in the animated image and
|
||||
DELAY is the delay in 100ths of a second until the next sub-image
|
||||
shall be displayed."
|
||||
(cond
|
||||
((eq (plist-get (cdr image) :type) 'gif)
|
||||
|
@ -708,6 +612,63 @@ shall be displayed."
|
|||
(if (eq tmo 0) (setq tmo 10))
|
||||
(cons images tmo))))))
|
||||
|
||||
(defun image-animate (image &optional index limit)
|
||||
"Start animating IMAGE.
|
||||
Animation occurs by destructively altering the IMAGE spec list.
|
||||
|
||||
With optional INDEX, begin animating from that animation frame.
|
||||
LIMIT specifies how long to animate the image. If omitted or
|
||||
nil, play the animation until the end. If t, loop forever. If a
|
||||
number, play until that number of seconds has elapsed."
|
||||
(let ((anim (image-animated-p image))
|
||||
delay timer)
|
||||
(when anim
|
||||
(if (setq timer (image-animate-timer image))
|
||||
(cancel-timer timer))
|
||||
(setq delay (max (* (cdr anim) 0.01) 0.025))
|
||||
(run-with-timer 0.2 nil #'image-animate-timeout
|
||||
image (or index 0) (car anim)
|
||||
delay 0 limit))))
|
||||
|
||||
(defun image-animate-timer (image)
|
||||
"Return the animation timer for image IMAGE."
|
||||
;; See cancel-function-timers
|
||||
(let ((tail timer-list) timer)
|
||||
(while tail
|
||||
(setq timer (car tail)
|
||||
tail (cdr tail))
|
||||
(if (and (eq (aref timer 5) 'image-animate-timeout)
|
||||
(eq (car-safe (aref timer 6)) image))
|
||||
(setq tail nil)
|
||||
(setq timer nil)))
|
||||
timer))
|
||||
|
||||
(defun image-animate-timeout (image n count delay time-elapsed limit)
|
||||
"Display animation frame N of IMAGE.
|
||||
N=0 refers to the initial animation frame.
|
||||
COUNT is the total number of frames in the animation.
|
||||
DELAY is the time between animation frames, in seconds.
|
||||
TIME-ELAPSED is the total time that has elapsed since
|
||||
`image-animate-start' was called.
|
||||
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."
|
||||
(let (done)
|
||||
(plist-put (cdr image) :index n)
|
||||
(force-window-update)
|
||||
(setq n (1+ n))
|
||||
(if (>= n count)
|
||||
(if limit
|
||||
(setq n 0)
|
||||
(setq done t)))
|
||||
(setq time-elapsed (+ delay time-elapsed))
|
||||
(if (numberp limit)
|
||||
(setq done (>= time-elapsed limit)))
|
||||
(unless done
|
||||
(run-with-timer delay nil 'image-animate-timeout
|
||||
image n count delay
|
||||
time-elapsed limit))))
|
||||
|
||||
|
||||
(defcustom imagemagick-types-inhibit
|
||||
'(C HTML HTM TXT PDF)
|
||||
|
|
Loading…
Add table
Reference in a new issue