Subject: Re: bug#47095: [PATCH] Add support for image mirroring (Bug#47095)

From: Timo Taipalus <[email-masked]>
Date: Wed, 29 Dec 2021 11:10:03 +0200

On 28.12.2021 13:22, Alan Third wrote:
> On Mon, Dec 27, 2021 at 10:36:43AM +0200, Timo Taipalus wrote:
>> It seems image-mode does not have capability for image mirroring, so
>> here's a patch for it.  Tested NS (macOS) and XRender/Cairo
>> implementations.  As it is, it has no effect if ImageMagick is
>> enabled.
> 
> I suspect this won't work at all on windows due to the way the
> transform is implemented there, but someone should probably check.
> 
>> I chose 'h' as the action key because 'm' and 'f' were reserved, and
>> 'h' is easy to memorize ("horizontal").
> 
> 'r' for reverse, maybe?
> 
> Is it possible to do the mirroring before the rotation instead of as
> part of it? I feel like it would make the code a little clearer,
> although I suspect you're doing it in the rotation to reduce the
> number of calculations? Would it be possible to have both horizontal
> and vertical mirroring?

As said elsewhere, "r" is reserved for rotation. :)

I added an elisp function to do a vertical flip. Also I renamed the
procedure flipping because that's what GIMP calls it. Attached is a
revised patch to replace the previous one.

As for the C code, I agree it adds some complexity to the transform
function, but this is probably the fastest way to achieve it.

Flipping before rotation would lead to strange results when the image
has been rotated 90/270 degrees. Then the flip commands seem reversed
– horizontal flip actually flips vertically and vice versa. This is
because the transform function always rotates the initial image,
which is what would be flipped.

Hopefully someone can test this patch on Windows.

From b58619431d6fa0f6b9f50a94aefc09be6121fa3e Mon Sep 17 00:00:00 2001
From: Timo <[email-masked]>
Date: Wed, 29 Dec 2021 10:55:06 +0200
Subject: [PATCH] Add support for image flipping (Bug#47095)

* 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 by 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.
---
 etc/NEWS      |  4 ++++
 lisp/image.el | 18 ++++++++++++++++++
 src/image.c   | 26 ++++++++++++++++++++++----
 3 files changed, 44 insertions(+), 4 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index c9466d0fef..ae894c5465 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -570,6 +570,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 702985f41f..05e50cc4ae 100644
--- a/lisp/image.el
+++ b/lisp/image.el
@@ -181,6 +181,8 @@ image-map
     (define-key map [C-wheel-up]   'image-mouse-increase-size)
     (define-key map [C-mouse-4]    'image-mouse-increase-size)
     (define-key map "r" 'image-rotate)
+    (define-key map "h" 'image-flip-horizontally)
+    (define-key map "v" 'image-flip-vertically)
     (define-key map "o" 'image-save)
     map))
 
@@ -1249,6 +1251,22 @@ image-save
       (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 1d83065cf7..5cbc85f357 100644
--- a/src/image.c
+++ b/src/image.c
@@ -2406,6 +2406,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
@@ -2445,14 +2450,20 @@ 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)
+	{
+          cos_r = 1;
+	  sin_r = 0;
+	  rotate_flag = 1;
+	}
+      else if (rotation == 90)
 	{
 	  width = img->height;
 	  height = img->width;
@@ -2493,9 +2504,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.  */
@@ -2513,9 +2529,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;
@@ -11363,6 +11380,7 @@ syms_of_image (void)
   DEFSYM (QCtransform_smoothing, ":transform-smoothing");
   DEFSYM (QCcolor_adjustment, ":color-adjustment");
   DEFSYM (QCmask, ":mask");
+  DEFSYM (QCflip, ":flip");
 
   /* Other symbols.  */
   DEFSYM (Qlaplace, "laplace");
-- 
2.32.0 (Apple Git-132)