From 6fbea946bfd0119cf517211d058ea277c9209a87 Mon Sep 17 00:00:00 2001 From: Timo Taipalus Date: Fri, 24 Jun 2022 14:44:02 +0200 Subject: [PATCH] Add support for image flipping * lisp/image.el (image-map): Keybindings for flipping functions. (image-flip-horizontally): New function that toggles image flipping property. (image-flip-vertically): New function that toggles image flipping property and rotates image 180 degrees. * src/image.c (syms_of_image): Add property. (image_set_transform): Modify image rotation code to also horizontally flip the image when the property is set. * etc/NEWS: Add description. * doc/lispref/display.texi (Image Descriptors): Document :flip (bug#47095). --- doc/lispref/display.texi | 6 ++++++ etc/NEWS | 4 ++++ lisp/image.el | 18 ++++++++++++++++++ src/image.c | 31 +++++++++++++++++++++++++++---- 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 3d1d9e24dd5..e85d492bbb5 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -5717,6 +5717,12 @@ are supported, unless the image type is @code{imagemagick}. Positive values rotate clockwise, negative values counter-clockwise. Rotation is performed after scaling and cropping. +@item :flip @var{flip} +If this is @code{t}, the image will be horizontally flipped. +Currently it has no effect if the image type is @code{imagemagick}. +Vertical flipping can be achieved by rotating the image 180 degrees +and toggling this value. + @item :transform-smoothing @var{smooth} If this is @code{t}, any image transform will have smoothing applied; if @code{nil}, no smoothing will be applied. The exact algorithm used diff --git a/etc/NEWS b/etc/NEWS index 552c8d1d49e..cb9e7417b6e 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1545,6 +1545,10 @@ This controls whether or not to show a message when opening certain image formats saying how to edit it as text. The default is to show this message for SVG and XPM. ++++ +*** New commands: 'image-flip-horizontally' and 'image-flip-vertically'. +These commands horizontally and vertically flip the image under point. + ** Image-Dired +++ diff --git a/lisp/image.el b/lisp/image.el index 8c5cfa7c0ba..e90cccaa096 100644 --- a/lisp/image.el +++ b/lisp/image.el @@ -177,6 +177,8 @@ or \"ffmpeg\") is installed." "+" #'image-increase-size "r" #'image-rotate "o" #'image-save + "h" #'image-flip-horizontally + "v" #'image-flip-vertically "C-" #'image-mouse-decrease-size "C-" #'image-mouse-decrease-size "C-" #'image-mouse-increase-size @@ -1288,6 +1290,22 @@ changing the displayed image size does not affect the saved image." (write-region (point-min) (point-max) (read-file-name "Write image to file: "))))) +(defun image-flip-horizontally () + "Horizontally flip the image under point." + (interactive) + (let ((image (image--get-image))) + (image-flush image) + (setf (image-property image :flip) + (not (image-property image :flip))))) + +(defun image-flip-vertically () + "Vertically flip the image under point." + (interactive) + (let ((image (image--get-image))) + (image-rotate 180) + (setf (image-property image :flip) + (not (image-property image :flip))))) + (provide 'image) ;;; image.el ends here diff --git a/src/image.c b/src/image.c index 2e2f8fe3646..0e4b2e0f62a 100644 --- a/src/image.c +++ b/src/image.c @@ -2587,6 +2587,11 @@ image_set_transform (struct frame *f, struct image *img) double rotation = 0.0; compute_image_rotation (img, &rotation); + /* Determine flipping. */ + bool flip; + Lisp_Object m = image_spec_value (img->spec, QCflip, NULL); + flip = !NILP (m); + #ifndef HAVE_HAIKU # if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS /* We want scale up operations to use a nearest neighbor filter to @@ -2626,14 +2631,25 @@ image_set_transform (struct frame *f, struct image *img) /* Perform rotation transformation. */ int rotate_flag = -1; - if (rotation == 0) + if (rotation == 0 && !flip) rotate_flag = 0; else { # if (defined USE_CAIRO || defined HAVE_XRENDER \ || defined HAVE_NTGUI || defined HAVE_NS) int cos_r, sin_r; - if (rotation == 90) + if (rotation == 0) + { + /* FLIP is always true here. As this will rotate by 0 + degrees, it has no visible effect. Applying only + translation matrix to the image would be sufficient for + horizontal flipping, but writing special handling for + this case would increase code complexity somewhat. */ + cos_r = 1; + sin_r = 0; + rotate_flag = 1; + } + else if (rotation == 90) { width = img->height; height = img->width; @@ -2674,9 +2690,14 @@ image_set_transform (struct frame *f, struct image *img) matrix3x3 v; matrix3x3_mult (rot, u, v); - /* 3. Translate back. */ + /* 3. Translate back. Flip horizontally if requested. */ t[2][0] = width * -.5; t[2][1] = height * -.5; + if (flip) + { + t[0][0] = -t[0][0]; + t[2][0] = -t[2][0]; + } matrix3x3_mult (t, v, matrix); # else /* 1. Translate so (0, 0) is in the center of the image. */ @@ -2694,9 +2715,10 @@ image_set_transform (struct frame *f, struct image *img) matrix3x3 v; matrix3x3_mult (u, rot, v); - /* 3. Translate back. */ + /* 3. Translate back. Flip horizontally if requested. */ t[2][0] = width * .5; t[2][1] = height * .5; + if (flip) t[0][0] = -t[0][0]; matrix3x3_mult (v, t, matrix); # endif img->width = width; @@ -11940,6 +11962,7 @@ non-numeric, there is no explicit limit on the size of images. */); DEFSYM (QCtransform_smoothing, ":transform-smoothing"); DEFSYM (QCcolor_adjustment, ":color-adjustment"); DEFSYM (QCmask, ":mask"); + DEFSYM (QCflip, ":flip"); /* Other symbols. */ DEFSYM (Qlaplace, "laplace"); -- 2.39.2