unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Alan Third <alan@idiocy.org>
To: Eli Zaretskii <eliz@gnu.org>
Cc: emacs-devel@gnu.org
Subject: Re: Native image rotation
Date: Sat, 2 Mar 2019 13:29:11 +0000	[thread overview]
Message-ID: <20190302132911.GA20556@breton.holly.idiocy.org> (raw)
In-Reply-To: <20190225192102.GA3060@breton.holly.idiocy.org>

[-- Attachment #1: Type: text/plain, Size: 1428 bytes --]

On Mon, Feb 25, 2019 at 07:21:02PM +0000, Alan Third wrote:
> On Mon, Feb 25, 2019 at 05:36:46AM +0200, Eli Zaretskii wrote:
> > 
> > It's indeed the clearing that bothered me,
> 
> Would it be better if it was done by another XRender composite rather
> than x_clear_area?

I tried to replace this with XRenderFillRectangle, but realised I need
to construct an XRenderColor struct, and I’ve no idea how to get the
colour data held in img->background.

I’ve attached a patch containing the original two, and the ability to
crop.

I decided to use a different order of processing from the ImageMagick
code we have just now, as this new ordering seems more useful for the
use cases that were suggested here.

Imagemagick does resize ‐> crop ‐> rotate.

I’ve gone with rotate ‐> crop ‐> resize.

If that’s no good it’s simple enough to reorder the functions. The
main issue I can foresee is that some people may prefer resize to come
first, but I feel that reduces the usefulness of :max-width and
:max-height.

I’ve not yet documented crop, and it’s not already documented for
Imagemagick. But in short:

    :crop '(width height left top)
    
Where left and top can be negative to operate from the right and
bottom of the image. Crop cannot be used to enlarge the image. As
mentioned before, the crop occurs before the resize, so sizes are in
terms of the original image, not the final one.

-- 
Alan Third

[-- Attachment #2: 0001-Add-native-image-rotation-and-cropping.patch --]
[-- Type: text/plain, Size: 23388 bytes --]

From adbae7ca26484a1a617a7b3fe7d89bbb8ed07506 Mon Sep 17 00:00:00 2001
From: Alan Third <alan@idiocy.org>
Date: Sat, 23 Feb 2019 20:56:48 +0000
Subject: [PATCH] Add native image rotation and cropping

* lisp/image.el (image--get-imagemagick-and-warn): Only fallback to
ImageMagick if native transforms aren't available.
* src/dispextern.h (INIT_MATRIX, COPY_MATRIX, MULT_MATRICES): New
macros for matrix manipulation.
(HAVE_NATIVE_SCALING, HAVE_NATIVE_TRANSFORMS): Rename and change all
relevant locations.
* src/image.c (x_set_image_rotation):
(x_set_transform): New functions.
(x_set_image_size): Use transform matrix for resizing under X and NS.
(x_set_image_crop): New function.
(lookup_image): Use the new transform functions.
(Fimage_scaling_p, Fimage_transforms_p): Rename and update all
callers.
* src/nsimage.m (ns_load_image): Remove rotation code.
(ns_image_set_transform): New function.
([EmacsImage dealloc]): Release the saved transform.
([EmacsImage rotate:]): Remove unneeded method.
([EmacsImage setTransform:]): New method.
* src/nsterm.h (EmacsImage): Add transform property and update method
definitions.
* src/nsterm.m (ns_dumpglyphs_image): Use the transform to draw the
image correctly.
* src/xterm.c (x_composite_image): Clear under an image before
compositing it.
* doc/lispref/display.texi (Image Descriptors): Add :rotation.
(ImageMagick Images): Remove :rotation.
---
 doc/lispref/display.texi |  21 +--
 etc/NEWS                 |   6 +-
 lisp/image.el            |   5 +-
 src/dispextern.h         |  22 +++-
 src/image.c              | 268 +++++++++++++++++++++++++++++++++++----
 src/nsimage.m            |  64 +++-------
 src/nsterm.h             |   5 +-
 src/nsterm.m             |  41 ++++--
 src/xterm.c              |   5 +
 9 files changed, 333 insertions(+), 104 deletions(-)

diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 95379b342b..801ab9595a 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -5157,6 +5157,9 @@ Image Descriptors
 specified, the height/width will be adjusted by the specified scaling
 factor.
 
+@item :rotation @var{angle}
+Specifies a rotation angle in degrees.
+
 @item :index @var{frame}
 @xref{Multi-Frame Images}.
 
@@ -5299,14 +5302,15 @@ Image Descriptors
 (@pxref{Input Focus}).
 @end defun
 
-@defun image-scaling-p &optional frame
-This function returns @code{t} if @var{frame} supports image scaling.
-@var{frame} @code{nil} or omitted means to use the selected frame
-(@pxref{Input Focus}).
+@defun image-transforms-p &optional frame
+This function returns @code{t} if @var{frame} supports image scaling
+and rotation.  @var{frame} @code{nil} or omitted means to use the
+selected frame (@pxref{Input Focus}).
 
-If image scaling is not supported, @code{:width}, @code{:height},
-@code{:scale}, @code{:max-width} and @code{:max-height} will only be
-usable through ImageMagick, if available (@pxref{ImageMagick Images}).
+If image transforms are not supported, @code{:rotation},
+@code{:width}, @code{:height}, @code{:scale}, @code{:max-width} and
+@code{:max-height} will only be usable through ImageMagick, if
+available (@pxref{ImageMagick Images}).
 @end defun
 
 @node XBM Images
@@ -5450,9 +5454,6 @@ ImageMagick Images
 image data, as found in @code{image-format-suffixes}.  This is used
 when the image does not have an associated file name, to provide a
 hint to ImageMagick to help it detect the image type.
-
-@item :rotation @var{angle}
-Specifies a rotation angle in degrees.
 @end table
 
 @node SVG Images
diff --git a/etc/NEWS b/etc/NEWS
index 0cafbaae96..4cc0ccaf07 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1569,14 +1569,14 @@ buffer's 'default-directory' and invoke that file name handler to make
 the process.  That way 'make-process' can start remote processes.
 
 +++
-** Emacs now supports resizing (scaling) of images without ImageMagick.
+** Emacs now supports resizing and rotating images without ImageMagick.
 All modern systems are supported by this feature.  (On GNU and Unix
 systems, the XRender extension to X11 is required for this to be
 available; the configure script will test for it and, if found, enable
 scaling.)
 
-The new function 'image-scaling-p' can be used to test whether any
-given frame supports resizing.
+The new function 'image-transforms-p' can be used to test whether any
+given frame supports this capability.
 
 +++
 ** (locale-info 'paper) now returns the paper size on systems that support it.
diff --git a/lisp/image.el b/lisp/image.el
index 3aa3b0aa24..0139c5263a 100644
--- a/lisp/image.el
+++ b/lisp/image.el
@@ -989,11 +989,12 @@ image--get-image
     image))
 
 (defun image--get-imagemagick-and-warn ()
-  (unless (or (fboundp 'imagemagick-types) (image-scaling-p))
+  (unless (or (fboundp 'imagemagick-types) (image-transforms-p))
     (error "Cannot rescale images on this terminal"))
   (let ((image (image--get-image)))
     (image-flush image)
-    (when (fboundp 'imagemagick-types)
+    (when (and (fboundp 'imagemagick-types)
+               (not (image-transforms-p)))
       (plist-put (cdr image) :type 'imagemagick))
     image))
 
diff --git a/src/dispextern.h b/src/dispextern.h
index 894753669d..0eb7776056 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -2939,7 +2939,25 @@ struct redisplay_interface
 #ifdef HAVE_WINDOW_SYSTEM
 
 # if defined HAVE_XRENDER || defined HAVE_NS || defined HAVE_NTGUI
-#  define HAVE_NATIVE_SCALING
+#  define HAVE_NATIVE_TRANSFORMS
+
+#  define INIT_MATRIX(m)                          \
+  for (int i = 0 ; i < 3 ; i++)                   \
+    for (int j = 0 ; j < 3 ; j++)                 \
+      m[i][j] = (i == j) ? 1 : 0;
+
+#  define COPY_MATRIX(a, b)                       \
+  for (int i = 0 ; i < 3 ; i++)                   \
+    for (int j = 0 ; j < 3 ; j++)                 \
+      b[i][j] = a[i][j];
+
+#  define MULT_MATRICES(a, b, result)             \
+  for (int i = 0 ; i < 3 ; i++)                   \
+    for (int j = 0 ; j < 3 ; j++) {               \
+      double sum = 0;                             \
+      for (int k = 0 ; k < 3 ; k++)               \
+        sum += a[k][j] * b[i][k];                 \
+      result[i][j] = sum;}
 # endif
 
 /* Structure describing an image.  Specific image formats like XBM are
@@ -2966,7 +2984,7 @@ struct image
      synchronized to Pixmap.  */
   XImagePtr ximg, mask_img;
 
-# ifdef HAVE_NATIVE_SCALING
+# ifdef HAVE_NATIVE_TRANSFORMS
   /* Picture versions of pixmap and mask for compositing.  */
   Picture picture, mask_picture;
 # endif
diff --git a/src/image.c b/src/image.c
index 642bf67152..21db915e61 100644
--- a/src/image.c
+++ b/src/image.c
@@ -56,6 +56,10 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 #include <sys/types.h>
 #endif /* HAVE_SYS_TYPES_H */
 
+#ifdef HAVE_NATIVE_TRANSFORMS
+#include <math.h>
+#endif
+
 #ifdef HAVE_WINDOW_SYSTEM
 #include TERM_HEADER
 #endif /* HAVE_WINDOW_SYSTEM */
@@ -1761,7 +1765,7 @@ postprocess_image (struct frame *f, struct image *img)
     }
 }
 
-#if defined (HAVE_IMAGEMAGICK) || defined (HAVE_NATIVE_SCALING)
+#if defined (HAVE_IMAGEMAGICK) || defined (HAVE_NATIVE_TRANSFORMS)
 /* Scale an image size by returning SIZE / DIVISOR * MULTIPLIER,
    safely rounded and clipped to int range.  */
 
@@ -1860,46 +1864,221 @@ compute_image_size (size_t width, size_t height,
   *d_width = desired_width;
   *d_height = desired_height;
 }
-#endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_SCALING */
+#endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */
 
 static void
-x_set_image_size (struct frame *f, struct image *img)
+x_set_image_rotation (struct image *img, double tm[3][3])
 {
-#ifdef HAVE_NATIVE_SCALING
+#ifdef HAVE_NATIVE_TRANSFORMS
 # ifdef HAVE_IMAGEMAGICK
-  /* ImageMagick images are already the correct size.  */
+  /* ImageMagick images are already rotated.  */
   if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
     return;
 # endif
 
+# ifdef HAVE_XRENDER
+  if (!img->picture)
+    return;
+# endif
+
+  Lisp_Object value;
+  double rotation = 0;
+  double cos_r, sin_r;
+  double t[3][3], rot[3][3], tmp[3][3], tmp2[3][3];
   int width, height;
-  compute_image_size (img->width, img->height, img->spec, &width, &height);
 
-# ifdef HAVE_NS
-  ns_image_set_size (img->pixmap, width, height);
+  value = image_spec_value (img->spec, QCrotation, NULL);
+  if (NUMBERP (value))
+    rotation = M_PI * XFLOATINT (value) / 180; /* radians */
+
+  if (rotation == 0)
+    return;
+
+  cos_r = cos (rotation);
+  sin_r = sin (rotation);
+
+  if (rotation > 3 * M_PI && rotation <= M_PI)
+    {
+      width = fabs (img->height * cos_r)
+        + fabs (img->width * sin_r);
+      height = fabs (img->height * sin_r)
+        + fabs (img->width * cos_r);
+    }
+  else
+    {
+      width = fabs (img->height * sin_r)
+        + fabs (img->width * cos_r);
+      height = fabs (img->height * cos_r)
+        + fabs (img->width * sin_r);
+    }
+
+  /* Translate so (0, 0) is in the centre of the image.  */
+  INIT_MATRIX (t);
+  t[2][0] = img->width/2;
+  t[2][1] = img->height/2;
+
+  MULT_MATRICES (tm, t, tmp);
+
+  /* Rotate.  */
+  INIT_MATRIX (rot);
+  rot[0][0] = cos_r;
+  rot[1][0] = sin_r;
+  rot[0][1] = - sin_r;
+  rot[1][1] = cos_r;
+
+  MULT_MATRICES (tmp, rot, tmp2);
+
+  /* Translate back.  */
+  INIT_MATRIX (t);
+  t[2][0] = - width/2;
+  t[2][1] = - height/2;
+
+  MULT_MATRICES (tmp2, t, tm);
+
   img->width = width;
   img->height = height;
+#endif
+}
+
+static void
+x_set_image_crop (struct image *img, double tm[3][3])
+{
+#ifdef HAVE_NATIVE_TRANSFORMS
+# ifdef HAVE_IMAGEMAGICK
+  /* ImageMagick images are already rotated.  */
+  if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
+    return;
 # endif
 
 # ifdef HAVE_XRENDER
-  if (img->picture)
+  if (!img->picture)
+    return;
+# endif
+
+  double m[3][3], tmp[3][3];
+  int left, top, width, height;
+  Lisp_Object x = Qnil;
+  Lisp_Object y = Qnil;
+  Lisp_Object w = Qnil;
+  Lisp_Object h = Qnil;
+  Lisp_Object crop = image_spec_value (img->spec, QCcrop, NULL);
+
+  if (!CONSP (crop))
+    return;
+  else
     {
-      double xscale = img->width / (double) width;
-      double yscale = img->height / (double) height;
+      w = XCAR (crop);
+      crop = XCDR (crop);
+      if (CONSP (crop))
+	{
+          h = XCAR (crop);
+	  crop = XCDR (crop);
+	  if (CONSP (crop))
+	    {
+              x = XCAR (crop);
+	      crop = XCDR (crop);
+	      if (CONSP (crop))
+                y = XCAR (crop);
+	    }
+	}
+    }
 
-      XTransform tmat
-	= {{{XDoubleToFixed (xscale), XDoubleToFixed (0), XDoubleToFixed (0)},
-	    {XDoubleToFixed (0), XDoubleToFixed (yscale), XDoubleToFixed (0)},
-	    {XDoubleToFixed (0), XDoubleToFixed (0), XDoubleToFixed (1)}}};
+  if (FIXNATP (w) && XFIXNAT (w) < img->width)
+    width = XFIXNAT (w);
+  else
+    width = img->width;
 
-      XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, FilterBest,
-			       0, 0);
-      XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat);
+  if (TYPE_RANGED_FIXNUMP (int, x))
+    {
+      left = XFIXNUM (x);
+      if (left < 0)
+        left = img->width - width + left;
+    }
+  else
+    left = (img->width - width)/2;
 
-      img->width = width;
-      img->height = height;
+  if (FIXNATP (h) && XFIXNAT (h) < img->height)
+    height = XFIXNAT (h);
+  else
+    height = img->height;
+
+  if (TYPE_RANGED_FIXNUMP (int, y))
+    {
+      top = XFIXNUM (y);
+      if (top < 0)
+        top = img->height - height + top;
+    }
+  else
+    top = (img->height - height)/2;
+
+  /* Limit the output to the dimensions of the original image.  */
+  if (left < 0)
+    {
+      width = img->width + left;
+      left = 0;
     }
+
+  if (width + left > img->width)
+    width = img->width - left;
+
+  if (top < 0)
+    {
+      height = img->height + top;
+      top = 0;
+    }
+
+  if (height + top > img->height)
+    height = img->height - top;
+
+  INIT_MATRIX (m);
+  m[2][0] = left;
+  m[2][1] = top;
+
+  MULT_MATRICES (tm, m, tmp);
+  COPY_MATRIX (tmp, tm);
+
+  img->width = width;
+  img->height = height;
+#endif
+}
+
+static void
+x_set_image_size (struct image *img, double tm[3][3])
+{
+#ifdef HAVE_NATIVE_TRANSFORMS
+# ifdef HAVE_IMAGEMAGICK
+  /* ImageMagick images are already the correct size.  */
+  if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
+    return;
+# endif
+
+# ifdef HAVE_XRENDER
+  if (!img->picture)
+    return;
 # endif
+
+  int width, height;
+
+  compute_image_size (img->width, img->height, img->spec, &width, &height);
+
+# if defined (HAVE_NS) || defined (HAVE_XRENDER)
+  double rm[3][3], tmp[3][3];
+  double xscale, yscale;
+
+  xscale = img->width / (double) width;
+  yscale = img->height / (double) height;
+
+  INIT_MATRIX (rm);
+  rm[0][0] = xscale;
+  rm[1][1] = yscale;
+
+  MULT_MATRICES (tm, rm, tmp);
+  COPY_MATRIX (tmp, tm);
+
+  img->width = width;
+  img->height = height;
+# endif
+
 # ifdef HAVE_NTGUI
   /* Under HAVE_NTGUI, we will scale the image on the fly, when we
      draw it.  See w32term.c:x_draw_image_foreground.  */
@@ -1909,6 +2088,36 @@ x_set_image_size (struct frame *f, struct image *img)
 #endif
 }
 
+static void
+x_set_transform (struct frame *f, struct image *img, double matrix[3][3])
+{
+  /* TODO: Add MS Windows support.  */
+#ifdef HAVE_NATIVE_TRANSFORMS
+# if defined (HAVE_NS)
+  /* Under NS the transform is applied to the drawing surface at
+     drawing time, so store it for later.  */
+  ns_image_set_transform (img->pixmap, matrix);
+# elif defined (HAVE_XRENDER)
+  if (img->picture)
+    {
+      XTransform tmat
+	= {{{XDoubleToFixed (matrix[0][0]),
+             XDoubleToFixed (matrix[1][0]),
+             XDoubleToFixed (matrix[2][0])},
+	    {XDoubleToFixed (matrix[0][1]),
+             XDoubleToFixed (matrix[1][1]),
+             XDoubleToFixed (matrix[2][1])},
+	    {XDoubleToFixed (matrix[0][2]),
+             XDoubleToFixed (matrix[1][2]),
+             XDoubleToFixed (matrix[2][2])}}};
+
+      XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, FilterBest,
+			       0, 0);
+      XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat);
+    }
+# endif
+#endif
+}
 
 /* Return the id of image with Lisp specification SPEC on frame F.
    SPEC must be a valid Lisp image specification (see valid_image_p).  */
@@ -1964,7 +2173,16 @@ lookup_image (struct frame *f, Lisp_Object spec)
 	     `:background COLOR'.  */
 	  Lisp_Object ascent, margin, relief, bg;
 	  int relief_bound;
-          x_set_image_size (f, img);
+
+#ifdef HAVE_NATIVE_TRANSFORMS
+          double transform_matrix[3][3];
+
+          INIT_MATRIX (transform_matrix);
+          x_set_image_rotation (img, transform_matrix);
+          x_set_image_crop (img, transform_matrix);
+          x_set_image_size (img, transform_matrix);
+          x_set_transform (f, img, transform_matrix);
+#endif
 
 	  ascent = image_spec_value (spec, QCascent, NULL);
 	  if (FIXNUMP (ascent))
@@ -9916,9 +10134,9 @@ DEFUN ("lookup-image", Flookup_image, Slookup_image, 1, 1, 0,
 			    Initialization
  ***********************************************************************/
 
-DEFUN ("image-scaling-p", Fimage_scaling_p, Simage_scaling_p, 0, 1, 0,
-       doc: /* Test whether FRAME supports resizing images.
-Return t if FRAME supports native scaling, nil otherwise.  */)
+DEFUN ("image-transforms-p", Fimage_transforms_p, Simage_transforms_p, 0, 1, 0,
+       doc: /* Test whether FRAME supports image transformation.
+Return t if FRAME supports native transforms, nil otherwise.  */)
      (Lisp_Object frame)
 {
 #if defined (HAVE_NS) || defined (HAVE_NTGUI)
@@ -10179,7 +10397,7 @@ non-numeric, there is no explicit limit on the size of images.  */);
   defsubr (&Slookup_image);
 #endif
 
-  defsubr (&Simage_scaling_p);
+  defsubr (&Simage_transforms_p);
 
   DEFVAR_BOOL ("cross-disabled-images", cross_disabled_images,
     doc: /* Non-nil means always draw a cross over disabled images.
diff --git a/src/nsimage.m b/src/nsimage.m
index f16910de08..3a5dd24185 100644
--- a/src/nsimage.m
+++ b/src/nsimage.m
@@ -76,9 +76,8 @@ Updated by Christian Limpach (chris@nice.ch)
 {
   EmacsImage *eImg = nil;
   NSSize size;
-  Lisp_Object lisp_index, lisp_rotation;
+  Lisp_Object lisp_index;
   unsigned int index;
-  double rotation;
 
   NSTRACE ("ns_load_image");
 
@@ -87,9 +86,6 @@ Updated by Christian Limpach (chris@nice.ch)
   lisp_index = Fplist_get (XCDR (img->spec), QCindex);
   index = FIXNUMP (lisp_index) ? XFIXNAT (lisp_index) : 0;
 
-  lisp_rotation = Fplist_get (XCDR (img->spec), QCrotation);
-  rotation = NUMBERP (lisp_rotation) ? XFLOATINT (lisp_rotation) : 0;
-
   if (STRINGP (spec_file))
     {
       eImg = [EmacsImage allocInitFromFile: spec_file];
@@ -119,13 +115,6 @@ Updated by Christian Limpach (chris@nice.ch)
 
   img->lisp_data = [eImg getMetadata];
 
-  if (rotation != 0)
-    {
-      EmacsImage *temp = [eImg rotate:rotation];
-      [eImg release];
-      eImg = temp;
-    }
-
   size = [eImg size];
   img->width = size.width;
   img->height = size.height;
@@ -155,6 +144,12 @@ Updated by Christian Limpach (chris@nice.ch)
   [(EmacsImage *)img setSize:NSMakeSize (width, height)];
 }
 
+void
+ns_image_set_transform (void *img, double m[3][3])
+{
+  [(EmacsImage *)img setTransform:m];
+}
+
 unsigned long
 ns_get_pixel (void *img, int x, int y)
 {
@@ -225,6 +220,7 @@ - (void)dealloc
 {
   [stippleMask release];
   [bmRep release];
+  [transform release];
   [super dealloc];
 }
 
@@ -528,42 +524,16 @@ - (BOOL)setFrame: (unsigned int) index
   return YES;
 }
 
-- (instancetype)rotate: (double)rotation
+- (void)setTransform: (double[3][3]) m
 {
-  EmacsImage *new_image;
-  NSPoint new_origin;
-  NSSize new_size, size = [self size];
-  NSRect rect = { NSZeroPoint, [self size] };
-
-  /* Create a bezier path of the outline of the image and do the
-   * rotation on it.  */
-  NSBezierPath *bounds_path = [NSBezierPath bezierPathWithRect:rect];
-  NSAffineTransform *transform = [NSAffineTransform transform];
-  [transform rotateByDegrees: rotation * -1];
-  [bounds_path transformUsingAffineTransform:transform];
-
-  /* Now we can find out how large the rotated image needs to be.  */
-  new_size = [bounds_path bounds].size;
-  new_image = [[EmacsImage alloc] initWithSize:new_size];
-
-  new_origin = NSMakePoint((new_size.width - size.width)/2,
-                           (new_size.height - size.height)/2);
-
-  [new_image lockFocus];
-
-  /* Create the final transform.  */
-  transform = [NSAffineTransform transform];
-  [transform translateXBy:new_size.width/2 yBy:new_size.height/2];
-  [transform rotateByDegrees: rotation * -1];
-  [transform translateXBy:-new_size.width/2 yBy:-new_size.height/2];
-
-  [transform concat];
-  [self drawAtPoint:new_origin fromRect:NSZeroRect
-          operation:NSCompositingOperationCopy fraction:1];
-
-  [new_image unlockFocus];
-
-  return new_image;
+  transform = [[NSAffineTransform transform] retain];
+  NSAffineTransformStruct tm
+    = { m[0][0], m[0][1], m[1][0], m[1][1], m[2][0], m[2][1]};
+  [transform setTransformStruct:tm];
+
+  /* Because the transform is applied to the drawing surface, and not
+     the image itself, we need to invert it.  */
+  [transform invert];
 }
 
 @end
diff --git a/src/nsterm.h b/src/nsterm.h
index 78ce608554..2541b672bb 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -632,6 +632,8 @@ typedef id instancetype;
   unsigned char *pixmapData[5]; /* shortcut to access pixel data */
   NSColor *stippleMask;
   unsigned long xbm_fg;
+@public
+  NSAffineTransform *transform;
 }
 + (instancetype)allocInitFromFile: (Lisp_Object)file;
 - (void)dealloc;
@@ -648,7 +650,7 @@ typedef id instancetype;
 - (NSColor *)stippleMask;
 - (Lisp_Object)getMetadata;
 - (BOOL)setFrame: (unsigned int) index;
-- (instancetype)rotate: (double)rotation;
+- (void)setTransform: (double[3][3]) m;
 @end
 
 
@@ -1197,6 +1199,7 @@ extern bool ns_load_image (struct frame *f, struct image *img,
 extern int ns_image_width (void *img);
 extern int ns_image_height (void *img);
 extern void ns_image_set_size (void *img, int width, int height);
+extern void ns_image_set_transform (void *img, double m[3][3]);
 extern unsigned long ns_get_pixel (void *img, int x, int y);
 extern void ns_put_pixel (void *img, int x, int y, unsigned long argb);
 extern void ns_set_alpha (void *img, int x, int y, unsigned char a);
diff --git a/src/nsterm.m b/src/nsterm.m
index 2bf3e00786..3f227f525a 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -3868,21 +3868,34 @@ Function modeled after x_draw_glyph_string_box ().
   /* Draw the image... do we need to draw placeholder if img == nil?  */
   if (img != nil)
     {
-#ifdef NS_IMPL_COCOA
+      /* The idea here is that the clipped area is set in the normal
+         view coordinate system, then we transform the coordinate
+         system so that when we draw the image it is rotated, resized
+         or whatever as required.  This is kind of backwards, but
+         there's no way to apply the transform to the image without
+         creating a whole new bitmap.  */
       NSRect dr = NSMakeRect (x, y, s->slice.width, s->slice.height);
-      NSRect ir = NSMakeRect (s->slice.x,
-                              s->img->height - s->slice.y - s->slice.height,
-                              s->slice.width, s->slice.height);
-      [img drawInRect: dr
-             fromRect: ir
-             operation: NSCompositingOperationSourceOver
-              fraction: 1.0
-           respectFlipped: YES
-                hints: nil];
-#else
-      [img compositeToPoint: NSMakePoint (x, y + s->slice.height)
-                  operation: NSCompositingOperationSourceOver];
-#endif
+      NSRect ir = NSMakeRect (0, 0, [img size].width, [img size].height);
+
+      NSAffineTransform *setOrigin = [NSAffineTransform transform];
+
+      [[NSGraphicsContext currentContext] saveGraphicsState];
+
+      /* Because of the transforms it's far too difficult to work out
+         what portion of the original, untransformed, image will be
+         drawn, so the clipping area will ensure we draw only the
+         correct bit.  */
+      NSRectClip (dr);
+
+      [setOrigin translateXBy:x - s->slice.x yBy:y - s->slice.y];
+      [setOrigin concat];
+      [img->transform concat];
+
+      [img drawInRect:ir fromRect:ir
+            operation:NSCompositingOperationSourceOver
+             fraction:1.0 respectFlipped:YES hints:nil];
+
+      [[NSGraphicsContext currentContext] restoreGraphicsState];
     }
 
   if (s->hl == DRAW_CURSOR)
diff --git a/src/xterm.c b/src/xterm.c
index d8eb45a00c..a81efac5c8 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -2984,6 +2984,11 @@ x_composite_image (struct glyph_string *s, Pixmap dest,
       XRenderPictFormat *default_format;
       XRenderPictureAttributes attr;
 
+      /* A rotated image has no background in the "new" sections of
+         the image, so XRenderComposite makes them transparent with
+         PictOpOver.  Fill in the background before compositing.  */
+      x_clear_area (s->f, dstX, dstY, width, height);
+
       /* FIXME: Should we do this each time or would it make sense to
          store destination in the frame struct?  */
       default_format = XRenderFindVisualFormat (s->display,
-- 
2.20.1


  parent reply	other threads:[~2019-03-02 13:29 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-02-24 11:30 Native image rotation Alan Third
2019-02-24 16:14 ` Eli Zaretskii
2019-02-24 17:34   ` Clément Pit-Claudel
2019-02-24 17:49     ` Eli Zaretskii
2019-02-24 18:06       ` Clément Pit-Claudel
2019-02-24 18:28         ` Eli Zaretskii
2019-02-24 18:51           ` Stefan Monnier
2019-02-24 19:13             ` Eli Zaretskii
2019-02-24 19:23               ` Eli Zaretskii
2019-02-24 20:08                 ` Stefan Monnier
2019-02-24 23:00                   ` Alan Third
2019-02-25  3:32                   ` Eli Zaretskii
2019-02-24 23:22   ` Alan Third
2019-02-25  3:36     ` Eli Zaretskii
2019-02-25  5:11       ` Van L
2019-02-25 13:47       ` Stefan Monnier
2019-02-25 19:21       ` Alan Third
2019-02-26 17:01         ` Daniel Pittman
2019-03-02 13:29         ` Alan Third [this message]
2019-05-19 20:29           ` Basil L. Contovounesios
2019-05-20 18:18             ` Alan Third
2019-05-21 20:11               ` Alan Third
2019-06-02 18:11                 ` Alan Third
2019-06-02 18:24                   ` Lars Ingebrigtsen
2019-06-05 21:39                     ` Alan Third
2019-06-06  9:03                       ` Andy Moreton
2019-06-06 12:57                         ` Eli Zaretskii
2019-05-22  6:45             ` Eli Zaretskii
  -- strict thread matches above, loose matches on Subject: below --
2019-02-25 12:48 Evgeny Zajcev
2019-02-25 15:43 ` Clément Pit-Claudel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190302132911.GA20556@breton.holly.idiocy.org \
    --to=alan@idiocy.org \
    --cc=eliz@gnu.org \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).