]> code.delx.au - gnu-emacs/blobdiff - lisp/image.el
Merge from origin/emacs-25
[gnu-emacs] / lisp / image.el
index b69d3b15a4323eb2020e754f59c45f2d91fd1448..2ae642a3e32b276adc32ba2eb1c67eadaa96169e 100644 (file)
@@ -139,6 +139,15 @@ based on the font pixel size."
   :group 'image
   :version "25.2")
 
+;; Map put into text properties on images.
+(defvar image-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "-" 'image-decrease-size)
+    (define-key map "+" 'image-increase-size)
+    (define-key map "r" 'image-rotate)
+    (define-key map "o" 'image-save)
+    map))
+
 (defun image-load-path-for-library (library image &optional path no-error)
   "Return a suitable search path for images used by LIBRARY.
 
@@ -426,6 +435,30 @@ Image file names that are not absolute are searched for in the
                        (image-compute-scaling-factor image-scaling-factor)))
            props)))
 
+(defun image--set-property (image property value)
+  "Set PROPERTY in IMAGE to VALUE.
+Internal use only."
+  (if (null value)
+      (while (cdr image)
+        ;; IMAGE starts with the symbol `image', and the rest is a
+        ;; plist.  Decouple plist entries where the key matches
+        ;; the property.
+        (if (eq (cadr image) property)
+            (setcdr image (cddr image))
+          (setq image (cddr image))))
+    ;; Just enter the new value.
+    (plist-put (cdr image) property value))
+  value)
+
+(defun image-property (image property)
+  "Return the value of PROPERTY in IMAGE.
+Properties can be set with
+
+  (setf (image-property IMAGE PROPERTY) VALUE)
+If VALUE is nil, PROPERTY is removed from IMAGE."
+  (declare (gv-setter image--set-property))
+  (plist-get (cdr image) property))
+
 (defun image-compute-scaling-factor (scaling)
   (cond
    ((numberp image-scaling-factor)
@@ -466,6 +499,7 @@ means display it in the right marginal area."
       (put-text-property 0 (length string) 'display prop string)
       (overlay-put overlay 'put-image t)
       (overlay-put overlay 'before-string string)
+      (overlay-put overlay 'map image-map)
       overlay)))
 
 
@@ -505,7 +539,9 @@ height of the image; integer values are taken as pixel values."
     (add-text-properties start (point)
                         `(display ,(if slice
                                        (list (cons 'slice slice) image)
-                                     image) rear-nonsticky (display)))))
+                                     image)
+                                   rear-nonsticky (display)
+                                   keymap ,image-map))))
 
 
 ;;;###autoload
@@ -541,7 +577,8 @@ The image is automatically split into ROWS x COLS slices."
          (insert string)
          (add-text-properties start (point)
                               `(display ,(list (list 'slice x y dx dy) image)
-                                        rear-nonsticky (display)))
+                                        rear-nonsticky (display)
+                                         keymap ,image-map))
          (setq x (+ x dx))))
       (setq x 0.0
            y (+ y dy))
@@ -918,29 +955,87 @@ has no effect."
 If N is 3, then the image size will be increased by 30%.  The
 default is 20%."
   (interactive "P")
-  (image-change-size (if n
-                         (1+ (/ n 10))
-                       1.2)))
+  (image--change-size (if n
+                          (1+ (/ n 10))
+                        1.2)))
 
 (defun image-decrease-size (n)
   "Decrease the image size by a factor of N.
 If N is 3, then the image size will be decreased by 30%.  The
 default is 20%."
   (interactive "P")
-  (image-change-size (if n
-                         (- 1 (/ n 10))
-                       0.8)))
+  (image--change-size (if n
+                          (- 1 (/ n 10))
+                        0.8)))
+
+(defun image--get-image ()
+  (let ((image (or (get-text-property (point) 'display)
+                   ;; `put-image' uses overlays, so find an image in
+                   ;; the overlays.
+                   (seq-find (lambda (overlay)
+                               (overlay-get overlay 'display))
+                             (overlays-at (point))))))
+    (when (or (not (consp image))
+              (not (eq (car image) 'image)))
+      (error "No image under point"))
+    image))
 
-(defun image-change-size (factor)
+(defun image--get-imagemagick-and-warn ()
   (unless (fboundp 'imagemagick-types)
     (error "Can't rescale images without ImageMagick support"))
+  (let ((image (image--get-image)))
+    (image-flush image)
+    (plist-put (cdr image) :type 'imagemagick)
+    image))
+
+(defun image--change-size (factor)
+  (let* ((image (image--get-imagemagick-and-warn))
+         (new-image (image--image-without-parameters image))
+         (scale (image--current-scaling image new-image)))
+    (setcdr image (cdr new-image))
+    (plist-put (cdr image) :scale (* scale factor))))
+
+(defun image--image-without-parameters (image)
+  (cons (pop image)
+        (let ((new nil))
+          (while image
+            (let ((key (pop image))
+                  (val (pop image)))
+              (unless (memq key '(:scale :width :height :max-width :max-height))
+              (setq new (nconc new (list key val))))))
+          new)))
+
+(defun image--current-scaling (image new-image)
+  ;; The image may be scaled due to many reasons (:scale, :max-width,
+  ;; etc), so find out what the current scaling is based on the
+  ;; original image size and the displayed size.
+  (let ((image-width (car (image-size new-image t)))
+        (display-width (car (image-size image t))))
+    (/ (float display-width) image-width)))
+
+(defun image-rotate ()
+  "Rotate the image under point by 90 degrees clockwise."
+  (interactive)
+  (let ((image (image--get-imagemagick-and-warn)))
+    (plist-put (cdr image) :rotation
+               (float (+ (or (plist-get (cdr image) :rotation) 0) 90)))))
+
+(defun image-save ()
+  "Save the image under point."
+  (interactive)
   (let ((image (get-text-property (point) 'display)))
     (when (or (not (consp image))
               (not (eq (car image) 'image)))
       (error "No image under point"))
-    (plist-put (cdr image) :type 'imagemagick)
-    (plist-put (cdr image) :scale
-               (* (or (plist-get (cdr image) :scale) 1) factor))))
+    (with-temp-buffer
+      (let ((file (plist-get (cdr image) :file)))
+        (if file
+            (if (not (file-exists-p file))
+                (error "File %s no longer exists" file)
+              (insert-file-contents-literally file))
+          (insert (plist-get (cdr image) :data))))
+      (write-region (point-min) (point-max)
+                    (read-file-name "Write image to file: ")))))
 
 (provide 'image)