]> git.eshelyaron.com Git - emacs.git/commitdiff
Support native image transforms on MS-Windows
authorEli Zaretskii <eliz@gnu.org>
Sat, 29 Jun 2019 11:51:41 +0000 (14:51 +0300)
committerEli Zaretskii <eliz@gnu.org>
Sat, 29 Jun 2019 11:51:41 +0000 (14:51 +0300)
This changeset also rearranges native image transform code
for other platforms to make it cleaner, and also removes
the support for native cropping.  For the discussions, see
https://lists.gnu.org/archive/html/emacs-devel/2019-06/msg00242.html

* src/w32term.c (w32_image_rotations_p, transform): New functions.
(w32_draw_image_foreground): If image rotation is requested
and supported, call PlgBlt to transform the image.
(w32_initialize): Populate the PlgBlt function pointer if it
is supported.
* src/w32term.h (w32_image_rotations_p): Add prototype.
* src/dispextern.h (struct image) [HAVE_NTGUI]: New member xform.
* src/image.c (compute_image_rotation): Renamed from
image_set_rotation.  Only compute and returns the rotation
angle; leave the matrix calculation for later.  Log an error
message if the :rotation parameter is not a number.
(image_set_crop): Function deleted.  We no longer support
native cropping, as one can display an image slice instead.
(image_set_transform): Compute the transform matrix in its
entirety here, in two variants: one for XRender and Cairo, the
other for NS and MS-Windows.  call compute_image_size and
compute_image_rotation internally.
(lookup_image) [HAVE_NATIVE_TRANSFORMS]: Call only
image_set_transform.  No need to pass the transform matrix to
image_set_transform.
(Fimage_transforms_p): Return a list of transform capabilities
rather than a simple boolean.  Support TTY frames as well.
* src/nsimage.m (setTransform:): Don't invert the matrix, as
it is already inverted in image.c.

* test/manual/image-transforms-tests.el (test-cropping): State
in the text that only ImageMagick supports cropping.

* doc/lispref/display.texi (Image Descriptors): Update the
documentation of native image transforms.
(ImageMagick Images): Move the description of ':crop' here.

* etc/NEWS: Minor copyedits of the feature announcement.

doc/lispref/display.texi
etc/NEWS
src/dispextern.h
src/image.c
src/nsimage.m
src/w32term.c
src/w32term.h
test/manual/image-transforms-tests.el

index 217df3b2cc2052cbab80f87a09a684168cf7e53a..c95f2df0fb80d4c5a4ccfdda4169ffdd6ee80e49 100644 (file)
@@ -5188,24 +5188,6 @@ values.  If both @code{:scale} and @code{:height}/@code{:width} are
 specified, the height/width will be adjusted by the specified scaling
 factor.
 
-@item :crop @var{geometry}
-This should be a list of the form @code{(@var{width} @var{height}
-@var{x} @var{y})}.  @var{width} and @var{height} specify the width
-and height of the cropped image.  If @var{x} is a positive number it
-specifies the offset of the cropped area from the left of the original
-image, and if negative the offset from the right.  If @var{y} is a
-positive number it specifies the offset from the top of the original
-image, and if negative from the bottom.  If @var{x} or @var{y} are
-@code{nil} or unspecified the crop area will be centred on the
-original image.
-
-If the crop area is outside or overlaps the edge of the image it will
-be reduced to exclude any areas outside of the image.  This means it
-is not possible to use @code{:crop} to increase the size of the image
-by entering large @var{width} or @var{height} values.
-
-Cropping is performed after scaling but before rotation.
-
 @item :rotation @var{angle}
 Specifies a rotation angle in degrees.  Only multiples of 90 degrees
 are supported, unless the image type is @code{imagemagick}.  Positive
@@ -5355,16 +5337,30 @@ This function returns @code{t} if image @var{spec} has a mask bitmap.
 @end defun
 
 @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}).
+This function returns non-@code{nil} if @var{frame} supports image
+scaling and rotation.  @var{frame} @code{nil} or omitted means to use
+the selected frame (@pxref{Input Focus}).  The returned list includes
+symbols that indicate which image transform operations are supported:
+
+@table @code
+@item scale
+Image scaling is supported by @var{frame} via the @code{:scale},
+@code{:width}, @code{:height}, @code{:max-width}, and
+@code{:max-height} properties.
+@item rotate90
+Image rotation is supported by @var{frame} if the rotation angle is an
+integral multiple of 90 degrees.
+@item rotate
+Image rotation by arbitrary angles is supported by @var{frame}.
+@item crop
+Image cropping is supported by @var{frame}.
+@end table
 
-If image transforms are not supported, @code{:rotation},
+If image transforms are not supported, @code{:rotation}, @code{:crop},
 @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
 @subsection XBM Images
 @cindex XBM
@@ -5506,6 +5502,24 @@ The value, @var{type}, should be a symbol specifying the type of the
 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 :crop @var{geometry}
+The value of @var{geometry} should be a list of the form
+@code{(@var{width} @var{height} @var{x} @var{y})}.  @var{width} and
+@var{height} specify the width and height of the cropped image.  If
+@var{x} is a positive number it specifies the offset of the cropped
+area from the left of the original image, and if negative the offset
+from the right.  If @var{y} is a positive number it specifies the
+offset from the top of the original image, and if negative from the
+bottom.  If @var{x} or @var{y} are @code{nil} or unspecified the crop
+area will be centred on the original image.
+
+If the crop area is outside or overlaps the edge of the image it will
+be reduced to exclude any areas outside of the image.  This means it
+is not possible to use @code{:crop} to increase the size of the image
+by entering large @var{width} or @var{height} values.
+
+Cropping is performed after scaling but before rotation.
 @end table
 
 @node SVG Images
index 864eb8c1100aa9df1573bb1c768536d6b91028e5..abbece374a47402cef0443d38e253665c47c18cb 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2214,13 +2214,13 @@ the process.  That way 'make-process' can start remote processes.
 
 +++
 ** Emacs now supports resizing and rotating images without ImageMagick.
-All modern systems are supported by this feature.  (On GNU and Unix
-systems, Cairo drawing or the XRender extension to X11 is required for
-this to be available; the configure script will test for it and, if
-found, enable scaling.)
+All modern systems support this feature.  (On GNU and Unix systems,
+Cairo drawing or 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-transforms-p' can be used to test whether any
-given frame supports this capability.
+given frame supports these capabilities.
 
 +++
 ** '(locale-info 'paper)' now returns the paper size on systems that support it.
index cc2d963a96b4fe8aeca0a342aa3039469cb73081..5d66fd8a48984775243b5b35108c0858c47e913f 100644 (file)
@@ -3020,6 +3020,9 @@ struct image
   /* Picture versions of pixmap and mask for compositing.  */
   Picture picture, mask_picture;
 # endif
+#endif /* HAVE_X_WINDOWS */
+#ifdef HAVE_NTGUI
+  XFORM xform;
 #endif
 
   /* Colors allocated for this image, if any.  Allocated via xmalloc.  */
index f3d6508f46013e0de49b9ee0ff4c4730b6ea8f0b..02dd95caeda34275ab986b70e2bdcdcb7112e94a 100644 (file)
@@ -57,6 +57,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 <float.h>     /* for FLT_MIN */
+#endif
+
 #ifdef HAVE_WINDOW_SYSTEM
 #include TERM_HEADER
 #endif /* HAVE_WINDOW_SYSTEM */
@@ -1965,12 +1969,10 @@ compute_image_size (size_t width, size_t height,
   *d_width = desired_width;
   *d_height = desired_height;
 }
-#endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */
 
-/* image_set_rotation, image_set_crop, image_set_size and
-   image_set_transform use affine transformation matrices to perform
-   various transforms on the image.  The matrix is a 2D array of
-   doubles.  It is laid out like this:
+/* image_set_rotation and image_set_transform use affine
+   transformation matrices to perform various transforms on the image.
+   The matrix is a 2D array of doubles.  It is laid out like this:
 
    m[0][0] = m11 | m[1][0] = m12 | m[2][0] = tx
    --------------+---------------+-------------
@@ -2039,14 +2041,15 @@ compute_image_size (size_t width, size_t height,
    finally move the origin back to the top left of the image, which
    may now be a different corner.
 
-   Cropping is easier as we just move the origin to the top left of
-   where we want to crop and set the width and height accordingly.
-   The matrices don't know anything about width and height.
+   Note that different GUI backends (X, Cairo, w32, NS) want the
+   transform matrix defined as transform from the original image to
+   the transformed image, while others want the matrix to describe the
+   transform of the space, which boils down to inverting the matrix.
 
    It's possible to pre-calculate the matrix multiplications and just
    generate one transform matrix that will do everything we need in a
    single step, but the maths for each element is much more complex
-   and I thought it was better to perform the steps separately.  */
+   and performing the steps separately makes for more readable code.  */
 
 typedef double matrix3x3[3][3];
 
@@ -2070,102 +2073,27 @@ matrix3x3_mult (matrix3x3 a, matrix3x3 b, matrix3x3 result)
 }
 
 static void
-image_set_rotation (struct image *img, matrix3x3 tm)
+compute_image_rotation (struct image *img, double *rotation)
 {
-#ifdef HAVE_NATIVE_TRANSFORMS
-# ifdef HAVE_IMAGEMAGICK
-  /* ImageMagick images are already rotated.  */
-  if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
-    return;
-# endif
-
-# if !defined USE_CAIRO && defined HAVE_XRENDER
-  if (!img->picture)
-    return;
-# endif
-
-  int rotation, cos_r, sin_r, width, height;
-
   Lisp_Object value = image_spec_value (img->spec, QCrotation, NULL);
   if (! NUMBERP (value))
-    return;
-
-  Lisp_Object reduced_angle = Fmod (value, make_fixnum (360));
-  if (! FLOATP (reduced_angle))
-    rotation = XFIXNUM (reduced_angle);
-  else
     {
-      rotation = XFLOAT_DATA (reduced_angle);
-      if (rotation != XFLOAT_DATA (reduced_angle))
-       goto not_a_multiple_of_90;
-    }
-
-  if (rotation == 0)
-    return;
-
-  if (rotation == 90)
-    {
-      width = img->height;
-      height = img->width;
-
-      cos_r = 0;
-      sin_r = 1;
-    }
-  else if (rotation == 180)
-    {
-      width = img->width;
-      height = img->height;
-
-      cos_r = -1;
-      sin_r = 0;
-    }
-  else if (rotation == 270)
-    {
-      width = img->height;
-      height = img->width;
-
-      cos_r = 0;
-      sin_r = -1;
-    }
-  else
-    {
-    not_a_multiple_of_90:
-      image_error ("Native image rotation supports "
-                  "only multiples of 90 degrees");
+      image_error ("Invalid image `:rotation' parameter");
       return;
     }
 
-  /* Translate so (0, 0) is in the center of the image.  */
-  matrix3x3 t
-    = { [0][0] = 1,
-                                 [1][1] = 1,
-       [2][0] = img->width * .5, [2][1] = img->height * .5, [2][2] = 1 };
-  matrix3x3 tmp;
-  matrix3x3_mult (t, tm, tmp);
-
-  /* Rotate.  */
-  matrix3x3 rot = { [0][0] = cos_r, [0][1] = -sin_r,
-                   [1][0] = sin_r, [1][1] = cos_r,
-                                                    [2][2] = 1 };
-  matrix3x3 tmp2;
-  matrix3x3_mult (rot, tmp, tmp2);
-
-  /* Translate back.  */
-  t[2][0] = width * -.5;
-  t[2][1] = height * -.5;
-  matrix3x3_mult (t, tmp2, tm);
-
-  img->width = width;
-  img->height = height;
-#endif
+  Lisp_Object reduced_angle = Fmod (value, make_fixnum (360));
+  if (FLOATP (reduced_angle))
+    *rotation = XFLOAT_DATA (reduced_angle);
+  else
+    *rotation = XFIXNUM (reduced_angle);
 }
 
 static void
-image_set_crop (struct image *img, matrix3x3 tm)
+image_set_transform (struct frame *f, struct image *img)
 {
-#ifdef HAVE_NATIVE_TRANSFORMS
 # ifdef HAVE_IMAGEMAGICK
-  /* ImageMagick images are already cropped.  */
+  /* ImageMagick images already have the correct transform.  */
   if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
     return;
 # endif
@@ -2175,119 +2103,112 @@ image_set_crop (struct image *img, matrix3x3 tm)
     return;
 # endif
 
-  Lisp_Object crop = image_spec_value (img->spec, QCcrop, NULL);
-
-  if (!CONSP (crop))
-    return;
-
-  Lisp_Object w = XCAR (crop), h = Qnil, x = Qnil, y = Qnil;
-  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);
-       }
-    }
-
-  int width = img->width;
-  if (FIXNATP (w) && XFIXNAT (w) < img->width)
-    width = XFIXNAT (w);
-  int left;
-  if (TYPE_RANGED_FIXNUMP (int, x))
-    {
-      left = XFIXNUM (x);
-      if (left < 0)
-        left = img->width - width + left;
-    }
-  else
-    left = (img->width - width) >> 1;
-
-  int height = img->height;
-  if (FIXNATP (h) && XFIXNAT (h) < img->height)
-    height = XFIXNAT (h);
-  int top;
-  if (TYPE_RANGED_FIXNUMP (int, y))
-    {
-      top = XFIXNUM (y);
-      if (top < 0)
-        top = img->height - height + top;
-    }
-  else
-    top = (img->height - height) >> 1;
-
-  /* Negative values operate from the right and bottom of the image
-     instead of the left and top.  */
-  if (left < 0)
-    {
-      width = img->width + left;
-      left = 0;
-    }
+  matrix3x3 matrix = { [0][0] = 1, [1][1] = 1, [2][2] = 1 };
 
-  if (width + left > img->width)
-    width = img->width - left;
+  /* Determine size.  */
+  int width, height;
+  compute_image_size (img->width, img->height, img->spec, &width, &height);
 
-  if (top < 0)
-    {
-      height = img->height + top;
-      top = 0;
-    }
+  /* Determine rotation.  */
+  double rotation = 0.0;
+  compute_image_rotation (img, &rotation);
 
-  if (height + top > img->height)
-    height = img->height - top;
+  /* Perform scale transformation.  */
 
-  matrix3x3 tmp, m = { [0][0] = 1,
-                                     [1][1] = 1,
-                      [2][0] = left, [2][1] = top, [2][2] = 1 };
-  matrix3x3_mult (m, tm, tmp);
-  matrix3x3_copy (tmp, tm);
+  double xscale = (double) width / img->width;
+  double yscale =  (double) height / img->height;
 
+# if defined USE_CAIRO || defined HAVE_XRENDER
+  /* Avoid division by zero.  */
+  xscale = max (xscale, FLT_MIN);
+  matrix3x3 tmp, rm =
+    { [0][0] = 1.0 / xscale, [1][1] = 1.0 / yscale, [2][2] = 1 };
+  matrix3x3_mult (rm, matrix, tmp);
+# elif defined HAVE_NTGUI || defined HAVE_NS
+  matrix3x3 tmp, rm = { [0][0] = xscale, [1][1] = yscale, [2][2] = 1 };
+  matrix3x3_mult (matrix, rm, tmp);
+# endif
   img->width = width;
   img->height = height;
-#endif
-}
 
-static void
-image_set_size (struct image *img, matrix3x3 tm)
-{
-#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
+  /* Perform rotation transformation.  */
 
-# if !defined USE_CAIRO && defined HAVE_XRENDER
-  if (!img->picture)
-    return;
-# endif
-
-  int width, height;
-
-  compute_image_size (img->width, img->height, img->spec, &width, &height);
+  int cos_r, sin_r;
+  if (rotation == 0)
+    matrix3x3_copy (tmp, matrix);
+  else if (rotation == 90 || rotation == 180 || rotation == 270)
+    {
+      if (rotation == 90)
+       {
+         width = img->height;
+         height = img->width;
 
-  double xscale = img->width / (double) width;
-  double yscale = img->height / (double) height;
+         cos_r = 0;
+         sin_r = 1;
+       }
+      else if (rotation == 180)
+       {
+         width = img->width;
+         height = img->height;
 
-  matrix3x3 tmp, rm = { [0][0] = xscale, [1][1] = yscale, [2][2] = 1 };
-  matrix3x3_mult (rm, tm, tmp);
-  matrix3x3_copy (tmp, tm);
+         cos_r = -1;
+         sin_r = 0;
+       }
+      else if (rotation == 270)
+       {
+         width = img->height;
+         height = img->width;
 
-  img->width = width;
-  img->height = height;
-#endif
-}
+         cos_r = 0;
+         sin_r = -1;
+       }
+# if defined USE_CAIRO || defined HAVE_XRENDER
+      /* 1. Translate so (0, 0) is in the center of the image.  */
+      matrix3x3 t
+       = { [0][0] = 1,
+                                     [1][1] = 1,
+           [2][0] = img->width * .5, [2][1] = img->height * .5, [2][2] = 1 };
+      matrix3x3 tmp2;
+      matrix3x3_mult (t, tmp, tmp2);
+
+      /* 2. Rotate.  */
+      matrix3x3 rot = { [0][0] = cos_r, [0][1] = -sin_r,
+                       [1][0] = sin_r, [1][1] = cos_r,
+                                                        [2][2] = 1 };
+      matrix3x3 tmp3;
+      matrix3x3_mult (rot, tmp2, tmp3);
+
+      /* 3. Translate back.  */
+      t[2][0] = width * -.5;
+      t[2][1] = height * -.5;
+      matrix3x3_mult (t, tmp3, matrix);
+# elif defined HAVE_NTGUI || defined HAVE_NS
+      /* 1. Translate so (0, 0) is in the center of the image.  */
+      matrix3x3 t
+       = { [0][0] = 1,
+                                      [1][1] = 1,
+           [2][0] = -img->width * .5, [2][1] = -img->height * .5, [2][2] = 1 };
+      matrix3x3 tmp2;
+      matrix3x3_mult (tmp, t, tmp2);
+
+      /* 2. Rotate.  */
+      matrix3x3 rot = { [0][0] = cos_r,  [0][1] = sin_r,
+                       [1][0] = -sin_r, [1][1] = cos_r,
+                                                         [2][2] = 1 };
+      matrix3x3 tmp3;
+      matrix3x3_mult (tmp2, rot, tmp3);
+
+      /* 3. Translate back.  */
+      t[2][0] = width * .5;
+      t[2][1] = height * .5;
+      matrix3x3_mult (tmp3, t, matrix);
+# endif
+      img->width = width;
+      img->height = height;
+    }
+  else
+    image_error ("Native image rotation supports only multiples of 90 degrees");
 
-static void
-image_set_transform (struct frame *f, struct image *img, matrix3x3 matrix)
-{
-  /* 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.  */
@@ -2317,10 +2238,19 @@ image_set_transform (struct frame *f, struct image *img, matrix3x3 matrix)
                               0, 0);
       XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat);
     }
+# elif defined HAVE_NTGUI
+  /* Store the transform matrix for application at draw time.  */
+  img->xform.eM11 = matrix[0][0];
+  img->xform.eM12 = matrix[0][1];
+  img->xform.eM21 = matrix[1][0];
+  img->xform.eM22 = matrix[1][1];
+  img->xform.eDx  = matrix[2][0];
+  img->xform.eDy  = matrix[2][1];
 # endif
-#endif
 }
 
+#endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */
+
 /* Return the id of image with Lisp specification SPEC on frame F.
    SPEC must be a valid Lisp image specification (see valid_image_p).  */
 
@@ -2377,11 +2307,7 @@ lookup_image (struct frame *f, Lisp_Object spec)
          int relief_bound;
 
 #ifdef HAVE_NATIVE_TRANSFORMS
-          matrix3x3 transform_matrix = { [0][0] = 1, [1][1] = 1, [2][2] = 1 };
-          image_set_size (img, transform_matrix);
-          image_set_crop (img, transform_matrix);
-          image_set_rotation (img, transform_matrix);
-          image_set_transform (f, img, transform_matrix);
+          image_set_transform (f, img);
 #endif
 
          ascent = image_spec_value (spec, QCascent, NULL);
@@ -10010,19 +9936,38 @@ DEFUN ("lookup-image", Flookup_image, Slookup_image, 1, 1, 0,
 
 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.  */)
+Return list of capabilities if FRAME supports native transforms, nil otherwise.
+FRAME defaults to the selected frame.
+The list of capabilities can include one or more of the following:
+
+ - the symbol `scale' if FRAME supports image scaling
+ - the symbol `rotate' if FRAME supports image rotation by arbitrary angles
+ - the symbol `rotate90' if FRAME supports image rotation only by angles
+    that are integral multiples of 90 degrees
+ - the symbol `crop' if FRAME supports image cropping.  */)
      (Lisp_Object frame)
 {
-#if defined (USE_CAIRO) || defined (HAVE_NS) || defined (HAVE_NTGUI)
-  return Qt;
-#elif defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER)
-  int event_basep, error_basep;
-
-  if (XRenderQueryExtension
-      (FRAME_X_DISPLAY (decode_window_system_frame (frame)),
-       &event_basep, &error_basep))
-    return Qt;
+  struct frame *f = decode_live_frame (frame);
+  if (FRAME_WINDOW_P (f))
+    {
+#ifdef HAVE_NATIVE_TRANSFORMS
+# if defined HAVE_IMAGEMAGICK
+      return list4 (Qscale, Qrotate90, Qrotate, Qcrop);
+# elif defined (USE_CAIRO) || defined (HAVE_NS)
+      return list2 (Qscale, Qrotate90);
+# elif defined (HAVE_NTGUI)
+      return (w32_image_rotations_p ()
+             ? list2 (Qscale, Qrotate90)
+             : list1 (Qscale));
+# elif defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER)
+      int event_basep, error_basep;
+
+      if (XRenderQueryExtension (FRAME_X_DISPLAY (f),
+                                &event_basep, &error_basep))
+       return list2 (Qscale, Qrotate90);
+# endif
 #endif
+    }
 
   return Qnil;
 }
@@ -10168,6 +10113,14 @@ non-numeric, there is no explicit limit on the size of images.  */);
   DEFSYM (Qpostscript, "postscript");
   DEFSYM (QCmax_width, ":max-width");
   DEFSYM (QCmax_height, ":max-height");
+
+#ifdef HAVE_NATIVE_TRANSFORMS
+  DEFSYM (Qscale, "scale");
+  DEFSYM (Qrotate, "rotate");
+  DEFSYM (Qrotate90, "rotate90");
+  DEFSYM (Qcrop, "crop");
+#endif
+
 #ifdef HAVE_GHOSTSCRIPT
   add_image_type (Qpostscript);
   DEFSYM (QCloader, ":loader");
index 7268e662638c9ff8b723c0e3f63e90bd855cea55..6f0340302ca0754fa7f50351a62c7ad0e3ade2f6 100644 (file)
@@ -530,10 +530,6 @@ ns_set_alpha (void *img, int x, int y, unsigned char a)
   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
index 97a5fc638920b7629421730ca7aefe647d8179f9..c6e175e7e5c5708f188e453f349bfd2324e973fa 100644 (file)
@@ -117,6 +117,10 @@ typedef struct tagGLYPHSET
 /* Dynamic linking to SetLayeredWindowAttribute (only since 2000).  */
 BOOL (WINAPI *pfnSetLayeredWindowAttributes) (HWND, COLORREF, BYTE, DWORD);
 
+/* PlgBlt is available since Windows 2000.  */
+BOOL (WINAPI *pfnPlgBlt) (HDC, const POINT *, HDC, int, int, int, int, HBITMAP, int, int);
+
+
 #ifndef LWA_ALPHA
 #define LWA_ALPHA 0x02
 #endif
@@ -1753,6 +1757,26 @@ w32_draw_glyph_string_box (struct glyph_string *s)
     }
 }
 
+bool
+w32_image_rotations_p (void)
+{
+  return pfnPlgBlt != NULL;
+}
+
+static POINT
+transform (int x0, int y0, int x, int y, XFORM *xform)
+{
+  POINT pt;
+
+  /* See https://docs.microsoft.com/en-us/windows/desktop/api/Wingdi/nf-wingdi-setworldtransform */
+  pt.x =
+    x0 + (x - x0) * xform->eM11 + (y - y0) * xform->eM21 + xform->eDx + 0.5f;
+  pt.y =
+    y0 + (x - x0) * xform->eM12 + (y - y0) * xform->eM22 + xform->eDy + 0.5f;
+
+  return pt;
+}
+
 
 /* Draw foreground of image glyph string S.  */
 
@@ -1798,20 +1822,31 @@ w32_draw_image_foreground (struct glyph_string *s)
        }
       else
        {
-         DebPrint (("w32_draw_image_foreground: GetObject failed!\n"));
+         DebPrint (("w32_draw_image_foreground: GetObject(pixmap) failed!\n"));
          orig_width = s->slice.width;
          orig_height = s->slice.height;
        }
 
       double w_factor = 1.0, h_factor = 1.0;
-      bool scaled = false;
+      bool scaled = false, need_xform = false;
       int orig_slice_width  = s->slice.width,
          orig_slice_height = s->slice.height;
       int orig_slice_x = s->slice.x, orig_slice_y = s->slice.y;
-      /* For scaled images we need to restore the original slice's
-        dimensions and origin coordinates, from before the scaling.  */
-      if (s->img->width != orig_width || s->img->height != orig_height)
+      POINT corner[3];
+      if ((s->img->xform.eM12 != 0 || s->img->xform.eM21 != 0
+          || s->img->xform.eDx != 0 || s->img->xform.eDy != 0)
+         /* PlgBlt is not available on Windows 9X.  */
+         && pfnPlgBlt)
        {
+         need_xform = true;
+         corner[0] = transform (x, y, x, y, &s->img->xform);
+         corner[1] = transform (x, y, x + orig_width, y, &s->img->xform);
+         corner[2] = transform (x, y, x, y + orig_height, &s->img->xform);
+       }
+      else if (s->img->width != orig_width || s->img->height != orig_height)
+       {
+         /* For scaled images we need to restore the original slice's
+            dimensions and origin coordinates, from before the scaling.  */
          scaled = true;
          w_factor = (double) orig_width  / (double) s->img->width;
          h_factor = (double) orig_height / (double) s->img->height;
@@ -1828,7 +1863,15 @@ w32_draw_image_foreground (struct glyph_string *s)
 
          SetTextColor (s->hdc, RGB (255, 255, 255));
          SetBkColor (s->hdc, RGB (0, 0, 0));
-         if (!scaled)
+         if (need_xform)
+           {
+             if (!pfnPlgBlt (s->hdc, corner, compat_hdc,
+                             s->slice.x, s->slice.y,
+                             orig_width, orig_height,
+                             s->img->mask, s->slice.x, s->slice.y))
+               DebPrint (("PlgBlt failed!"));
+           }
+         else if (!scaled)
            {
              BitBlt (s->hdc, x, y, s->slice.width, s->slice.height,
                      compat_hdc, s->slice.x, s->slice.y, SRCINVERT);
@@ -1865,7 +1908,14 @@ w32_draw_image_foreground (struct glyph_string *s)
        {
          SetTextColor (s->hdc, s->gc->foreground);
          SetBkColor (s->hdc, s->gc->background);
-         if (!scaled)
+         if (need_xform)
+           {
+             if (!pfnPlgBlt (s->hdc, corner, compat_hdc,
+                             s->slice.x, s->slice.y,
+                             orig_width, orig_height, NULL, 0, 0))
+               DebPrint (("PlgBlt failed!"));
+           }
+         else if (!scaled)
            BitBlt (s->hdc, x, y, s->slice.width, s->slice.height,
                    compat_hdc, s->slice.x, s->slice.y, SRCCOPY);
          else
@@ -7354,6 +7404,11 @@ w32_initialize (void)
 
     LOAD_PROC (user_lib, SetLayeredWindowAttributes);
 
+    /* PlgBlt is not available on Windows 9X.  */
+    HMODULE hgdi = LoadLibrary ("gdi32.dll");
+    if (hgdi)
+      LOAD_PROC (hgdi, PlgBlt);
+
 #undef LOAD_PROC
 
     /* Ensure scrollbar handles are at least 5 pixels.  */
index 729e8d0fd497fa89d4a3fb0c55ad88686d965b8b..6133e100c178f8e6e37eebb5b4d037ae8a97cd6c 100644 (file)
@@ -743,6 +743,8 @@ extern int handle_file_notifications (struct input_event *);
 extern void w32_initialize_display_info (Lisp_Object);
 extern void initialize_w32_display (struct terminal *, int *, int *);
 
+extern bool w32_image_rotations_p (void);
+
 #ifdef WINDOWSNT
 /* Keyboard hooks.  */
 extern void setup_w32_kbdhook (void);
index d601b9397e33d7757c38147cb76105c708ed04a1..e8b301e3331d0b2830d4523c73f00cdc1c6f4bb7 100644 (file)
@@ -67,7 +67,7 @@
                          <rect x='0' y='0' width='10' height='10'
                                style='fill:none;stroke-width:1;stroke:#000'/>
                        </svg>"))
-    (insert-header "Test Crop: cropping an image")
+    (insert-header "Test Crop: cropping an image (only works with ImageMagick)")
     (insert-test "all params" top-left image '(:crop (10 10 0 0)))
     (insert-test "width/height only" middle image '(:crop (10 10)))
     (insert-test "negative x y" middle image '(:crop (10 10 -10 -10)))