aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWolfgang Jenkner2012-05-14 01:19:46 -0400
committerStefan Monnier2012-05-14 01:19:46 -0400
commit2d21d7f6764a7885aac8ac91a4b64a8fa0e0a084 (patch)
treef738f5e0ce102dfbb0c3554152c9d8cc04c0c1be
parent7102e6d0a782c2bc4e74ced93daa27aa0d821bb8 (diff)
downloademacs-2d21d7f6764a7885aac8ac91a4b64a8fa0e0a084.tar.gz
emacs-2d21d7f6764a7885aac8ac91a4b64a8fa0e0a084.zip
* lisp/image-mode.el: Fit to width/height for rotated images.
(image-transform-scale, image-transform-right-angle-fudge): New vars. (image-transform-width, image-transform-fit-width): New functions. (image-transform-properties): Use them. (image-transform-check-size): New function. (image-toggle-display-image): Use it (for testing). (image-transform-set-rotation): Reduce angle mod 360. Delete obsolete comment. Fixes: debbugs:11431
-rw-r--r--lisp/ChangeLog11
-rw-r--r--lisp/image-mode.el152
2 files changed, 147 insertions, 16 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index d0811170b4e..75fc7f5884d 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,5 +1,16 @@
12012-05-14 Wolfgang Jenkner <wjenkner@inode.at> 12012-05-14 Wolfgang Jenkner <wjenkner@inode.at>
2 2
3 * image-mode.el: Fit to width/height for rotated images (bug#11431).
4 (image-transform-scale, image-transform-right-angle-fudge): New vars.
5 (image-transform-width, image-transform-fit-width): New functions.
6 (image-transform-properties): Use them.
7 (image-transform-check-size): New function.
8 (image-toggle-display-image): Use it (for testing).
9 (image-transform-set-rotation): Reduce angle mod 360.
10 Delete obsolete comment.
11
122012-05-14 Wolfgang Jenkner <wjenkner@inode.at>
13
3 * image-mode.el: Fix scaling (bug#11399). 14 * image-mode.el: Fix scaling (bug#11399).
4 (image-transform-resize): Doc fix. 15 (image-transform-resize): Doc fix.
5 (image-transform-properties): Default scale is 1 and height should 16 (image-transform-properties): Default scale is 1 and height should
diff --git a/lisp/image-mode.el b/lisp/image-mode.el
index 80321d6c94d..8329c02fb0d 100644
--- a/lisp/image-mode.el
+++ b/lisp/image-mode.el
@@ -532,6 +532,7 @@ was inserted."
532 (setq image-type type) 532 (setq image-type type)
533 (if (eq major-mode 'image-mode) 533 (if (eq major-mode 'image-mode)
534 (setq mode-name (format "Image[%s]" type))) 534 (setq mode-name (format "Image[%s]" type)))
535 (image-transform-check-size)
535 (if (called-interactively-p 'any) 536 (if (called-interactively-p 'any)
536 (message "Repeat this command to go back to displaying the file as text")))) 537 (message "Repeat this command to go back to displaying the file as text"))))
537 538
@@ -636,9 +637,122 @@ Its value should be one of the following:
636 - `fit-width', meaning to fit the image to the window width. 637 - `fit-width', meaning to fit the image to the window width.
637 - A number, which is a scale factor (the default size is 1).") 638 - A number, which is a scale factor (the default size is 1).")
638 639
640(defvar image-transform-scale 1.0
641 "The scale factor of the image being displayed.")
642
639(defvar image-transform-rotation 0.0 643(defvar image-transform-rotation 0.0
640 "Rotation angle for the image in the current Image mode buffer.") 644 "Rotation angle for the image in the current Image mode buffer.")
641 645
646(defvar image-transform-right-angle-fudge 0.0001
647 "Snap distance to a multiple of a right angle.
648There's no deep theory behind the default value, it should just
649be somewhat larger than ImageMagick's MagickEpsilon.")
650
651(defsubst image-transform-width (width height)
652 "Return the bounding box width of a rotated WIDTH x HEIGHT rectangle.
653The rotation angle is the value of `image-transform-rotation' in degrees."
654 (let ((angle (degrees-to-radians image-transform-rotation)))
655 ;; Assume, w.l.o.g., that the vertices of the rectangle have the
656 ;; coordinates (+-w/2, +-h/2) and that (0, 0) is the center of the
657 ;; rotation by the angle A. The projections onto the first axis
658 ;; of the vertices of the rotated rectangle are +- (w/2) cos A +-
659 ;; (h/2) sin A, and the difference between the largest and the
660 ;; smallest of the four values is the expression below.
661 (+ (* width (abs (cos angle))) (* height (abs (sin angle))))))
662
663;; The following comment and code snippet are from
664;; ImageMagick-6.7.4-4/magick/distort.c
665
666;; /* Set the output image geometry to calculated 'bestfit'.
667;; Yes this tends to 'over do' the file image size, ON PURPOSE!
668;; Do not do this for DePolar which needs to be exact for virtual tiling.
669;; */
670;; if ( fix_bounds ) {
671;; geometry.x = (ssize_t) floor(min.x-0.5);
672;; geometry.y = (ssize_t) floor(min.y-0.5);
673;; geometry.width=(size_t) ceil(max.x-geometry.x+0.5);
674;; geometry.height=(size_t) ceil(max.y-geometry.y+0.5);
675;; }
676
677;; Other parts of the same file show that here the origin is in the
678;; left lower corner of the image rectangle, the center of the
679;; rotation is the center of the rectangle and min.x and max.x
680;; (resp. min.y and max.y) are the smallest and the largest of the
681;; projections of the vertices onto the first (resp. second) axis.
682
683(defun image-transform-fit-width (width height length)
684 "Return (w . h) so that a rotated w x h image has exactly width LENGTH.
685The rotation angle is the value of `image-transform-rotation'.
686Write W for WIDTH and H for HEIGHT. Then the w x h rectangle is
687an \"approximately uniformly\" scaled W x H rectangle, which
688currently means that w is one of floor(s W) + {0, 1, -1} and h is
689floor(s H), where s can be recovered as the value of `image-transform-scale'.
690The value of `image-transform-rotation' may be replaced by
691a slightly different angle. Currently this is done for values
692close to a multiple of 90, see `image-transform-right-angle-fudge'."
693 (cond ((< (abs (- (mod (+ image-transform-rotation 90) 180) 90))
694 image-transform-right-angle-fudge)
695 (assert (not (zerop width)) t)
696 (setq image-transform-rotation
697 (float (round image-transform-rotation))
698 image-transform-scale (/ (float length) width))
699 (cons length nil))
700 ((< (abs (- (mod (+ image-transform-rotation 45) 90) 45))
701 image-transform-right-angle-fudge)
702 (assert (not (zerop height)) t)
703 (setq image-transform-rotation
704 (float (round image-transform-rotation))
705 image-transform-scale (/ (float length) height))
706 (cons nil length))
707 (t
708 (assert (not (and (zerop width) (zerop height))) t)
709 (setq image-transform-scale
710 (/ (float (1- length)) (image-transform-width width height)))
711 ;; Assume we have a w x h image and an angle A, and let l =
712 ;; l(w, h) = w |cos A| + h |sin A|, which is the actual width
713 ;; of the bounding box of the rotated image, as calculated by
714 ;; `image-transform-width'. The code snippet quoted above
715 ;; means that ImageMagick puts the rotated image in
716 ;; a bounding box of width L = 2 ceil((w+l+1)/2) - w.
717 ;; Elementary considerations show that this is equivalent to
718 ;; L - w being even and L-3 < l(w, h) <= L-1. In our case, L is
719 ;; the given `length' parameter and our job is to determine
720 ;; reasonable values for w and h which satisfy these
721 ;; conditions.
722 (let ((w (floor (* image-transform-scale width)))
723 (h (floor (* image-transform-scale height))))
724 ;; Let w and h as bound above. Then l(w, h) <= l(s W, s H)
725 ;; = L-1 < l(w+1, h+1) = l(w, h) + l(1, 1) <= l(w, h) + 2,
726 ;; hence l(w, h) > (L-1) - 2 = L-3.
727 (cons
728 (cond ((= (mod w 2) (mod length 2))
729 w)
730 ;; l(w+1, h) >= l(w, h) > L-3, but does l(w+1, h) <=
731 ;; L-1 hold?
732 ((<= (image-transform-width (1+ w) h) (1- length))
733 (1+ w))
734 ;; No, it doesn't, but this implies that l(w-1, h) =
735 ;; l(w+1, h) - l(2, 0) >= l(w+1, h) - 2 > (L-1) -
736 ;; 2 = L-3. Clearly, l(w-1, h) <= l(w, h) <= L-1.
737 (t
738 (1- w)))
739 h)))))
740
741(defun image-transform-check-size ()
742 "Check that the image exactly fits the width/height of the window."
743 (unless (numberp image-transform-resize)
744 (let ((size (image-display-size (image-get-display-property) t)))
745 (cond ((eq image-transform-resize 'fit-width)
746 (assert (= (car size)
747 (- (nth 2 (window-inside-pixel-edges))
748 (nth 0 (window-inside-pixel-edges))))
749 t))
750 ((eq image-transform-resize 'fit-height)
751 (assert (= (cdr size)
752 (- (nth 3 (window-inside-pixel-edges))
753 (nth 1 (window-inside-pixel-edges))))
754 t))))))
755
642(defun image-transform-properties (spec) 756(defun image-transform-properties (spec)
643 "Return rescaling/rotation properties for image SPEC. 757 "Return rescaling/rotation properties for image SPEC.
644These properties are determined by the Image mode variables 758These properties are determined by the Image mode variables
@@ -647,27 +761,35 @@ return value is suitable for appending to an image spec.
647 761
648Rescaling and rotation properties only take effect if Emacs is 762Rescaling and rotation properties only take effect if Emacs is
649compiled with ImageMagick support." 763compiled with ImageMagick support."
764 (setq image-transform-scale 1.0)
650 (when (or image-transform-resize 765 (when (or image-transform-resize
651 (not (equal image-transform-rotation 0.0))) 766 (/= image-transform-rotation 0.0))
652 ;; Note: `image-size' looks up and thus caches the untransformed 767 ;; Note: `image-size' looks up and thus caches the untransformed
653 ;; image. There's no easy way to prevent that. 768 ;; image. There's no easy way to prevent that.
654 (let* ((size (image-size spec t)) 769 (let* ((size (image-size spec t))
655 (height 770 (resized
656 (cond 771 (cond
657 ((numberp image-transform-resize) 772 ((numberp image-transform-resize)
658 (unless (= image-transform-resize 1) 773 (unless (= image-transform-resize 1)
659 (floor (* image-transform-resize (cdr size))))) 774 (setq image-transform-scale image-transform-resize)
775 (cons nil (floor (* image-transform-resize (cdr size))))))
776 ((eq image-transform-resize 'fit-width)
777 (image-transform-fit-width
778 (car size) (cdr size)
779 (- (nth 2 (window-inside-pixel-edges))
780 (nth 0 (window-inside-pixel-edges)))))
660 ((eq image-transform-resize 'fit-height) 781 ((eq image-transform-resize 'fit-height)
661 (- (nth 3 (window-inside-pixel-edges)) 782 (let ((res (image-transform-fit-width
662 (nth 1 (window-inside-pixel-edges)))))) 783 (cdr size) (car size)
663 (width (if (eq image-transform-resize 'fit-width) 784 (- (nth 3 (window-inside-pixel-edges))
664 (- (nth 2 (window-inside-pixel-edges)) 785 (nth 1 (window-inside-pixel-edges))))))
665 (nth 0 (window-inside-pixel-edges)))))) 786 (cons (cdr res) (car res)))))))
666 ;;TODO fit-to-* should consider the rotation angle 787 `(,@(when (car resized)
667 `(,@(if height (list :height height)) 788 (list :width (car resized)))
668 ,@(if width (list :width width)) 789 ,@(when (cdr resized)
669 ,@(if (not (equal 0.0 image-transform-rotation)) 790 (list :height (cdr resized)))
670 (list :rotation image-transform-rotation)))))) 791 ,@(unless (= 0.0 image-transform-rotation)
792 (list :rotation image-transform-rotation))))))
671 793
672(defun image-transform-set-scale (scale) 794(defun image-transform-set-scale (scale)
673 "Prompt for a number, and resize the current image by that amount. 795 "Prompt for a number, and resize the current image by that amount.
@@ -698,9 +820,7 @@ ImageMagick support."
698ROTATION should be in degrees. This command has no effect unless 820ROTATION should be in degrees. This command has no effect unless
699Emacs is compiled with ImageMagick support." 821Emacs is compiled with ImageMagick support."
700 (interactive "nRotation angle (in degrees): ") 822 (interactive "nRotation angle (in degrees): ")
701 ;;TODO 0 90 180 270 degrees are the only reasonable angles here 823 (setq image-transform-rotation (float (mod rotation 360)))
702 ;;otherwise combining with rescaling will get very awkward
703 (setq image-transform-rotation (float rotation))
704 (image-toggle-display-image)) 824 (image-toggle-display-image))
705 825
706(provide 'image-mode) 826(provide 'image-mode)