Function: image-transform-fit-width
image-transform-fit-width is a byte-compiled function defined in
image-mode.el.gz.
Signature
(image-transform-fit-width WIDTH HEIGHT LENGTH)
Documentation
Return (w . h) so that a rotated w x h image has exactly width LENGTH.
The rotation angle is the value of image-transform-rotation.
Write W for WIDTH and H for HEIGHT. Then the w x h rectangle is
an "approximately uniformly" scaled W x H rectangle, which
currently means that w is one of floor(s W) + {0, 1, -1} and h is
floor(s H), where s can be recovered as the value of image-transform-scale.
The value of image-transform-rotation may be replaced by
a slightly different angle. Currently this is done for values
close to a multiple of 90, see image-transform-right-angle-fudge.
Source Code
;; Defined in /usr/src/emacs/lisp/image-mode.el.gz
;; The following comment and code snippet are from
;; ImageMagick-6.7.4-4/magick/distort.c
;; /* Set the output image geometry to calculated 'best fit'.
;; Yes this tends to 'over do' the file image size, ON PURPOSE!
;; Do not do this for DePolar which needs to be exact for virtual tiling.
;; */
;; if ( fix_bounds ) {
;; geometry.x = (ssize_t) floor(min.x-0.5);
;; geometry.y = (ssize_t) floor(min.y-0.5);
;; geometry.width=(size_t) ceil(max.x-geometry.x+0.5);
;; geometry.height=(size_t) ceil(max.y-geometry.y+0.5);
;; }
;; Other parts of the same file show that here the origin is in the
;; left lower corner of the image rectangle, the center of the
;; rotation is the center of the rectangle and min.x and max.x
;; (resp. min.y and max.y) are the smallest and the largest of the
;; projections of the vertices onto the first (resp. second) axis.
(defun image-transform-fit-width (width height length)
"Return (w . h) so that a rotated w x h image has exactly width LENGTH.
The rotation angle is the value of `image-transform-rotation'.
Write W for WIDTH and H for HEIGHT. Then the w x h rectangle is
an \"approximately uniformly\" scaled W x H rectangle, which
currently means that w is one of floor(s W) + {0, 1, -1} and h is
floor(s H), where s can be recovered as the value of `image-transform-scale'.
The value of `image-transform-rotation' may be replaced by
a slightly different angle. Currently this is done for values
close to a multiple of 90, see `image-transform-right-angle-fudge'."
(cond ((< (abs (- (mod (+ image-transform-rotation 90) 180) 90))
image-transform-right-angle-fudge)
(cl-assert (not (zerop width)) t)
(setq image-transform-rotation
(float (round image-transform-rotation))
image-transform-scale (/ (float length) width))
(cons length nil))
((< (abs (- (mod (+ image-transform-rotation 45) 90) 45))
image-transform-right-angle-fudge)
(cl-assert (not (zerop height)) t)
(setq image-transform-rotation
(float (round image-transform-rotation))
image-transform-scale (/ (float length) height))
(cons nil length))
(t
(cl-assert (not (and (zerop width) (zerop height))) t)
(setq image-transform-scale
(/ (float (1- length)) (image-transform-width width height)))
;; Assume we have a w x h image and an angle A, and let l =
;; l(w, h) = w |cos A| + h |sin A|, which is the actual width
;; of the bounding box of the rotated image, as calculated by
;; `image-transform-width'. The code snippet quoted above
;; means that ImageMagick puts the rotated image in
;; a bounding box of width L = 2 ceil((w+l+1)/2) - w.
;; Elementary considerations show that this is equivalent to
;; L - w being even and L-3 < l(w, h) <= L-1. In our case, L is
;; the given `length' parameter and our job is to determine
;; reasonable values for w and h which satisfy these
;; conditions.
(let ((w (floor (* image-transform-scale width)))
(h (floor (* image-transform-scale height))))
;; Let w and h as bound above. Then l(w, h) <= l(s W, s H)
;; = L-1 < l(w+1, h+1) = l(w, h) + l(1, 1) <= l(w, h) + 2,
;; hence l(w, h) > (L-1) - 2 = L-3.
(cons
(cond ((= (mod w 2) (mod length 2))
w)
;; l(w+1, h) >= l(w, h) > L-3, but does l(w+1, h) <=
;; L-1 hold?
((<= (image-transform-width (1+ w) h) (1- length))
(1+ w))
;; No, it doesn't, but this implies that l(w-1, h) =
;; l(w+1, h) - l(2, 0) >= l(w+1, h) - 2 > (L-1) -
;; 2 = L-3. Clearly, l(w-1, h) <= l(w, h) <= L-1.
(t
(1- w)))
h)))))