aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEli Zaretskii2019-06-29 14:51:41 +0300
committerEli Zaretskii2019-06-29 14:51:41 +0300
commit74a5a332fee8a346cf65ed6656c1b08dc1df5fde (patch)
tree9b6488ab0dccc372c643f5b521e731f26886cfb4 /src
parent67b50770c050c55a26cd13b9568b01a80a449885 (diff)
downloademacs-74a5a332fee8a346cf65ed6656c1b08dc1df5fde.tar.gz
emacs-74a5a332fee8a346cf65ed6656c1b08dc1df5fde.zip
Support native image transforms on MS-Windows
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.
Diffstat (limited to 'src')
-rw-r--r--src/dispextern.h3
-rw-r--r--src/image.c371
-rw-r--r--src/nsimage.m4
-rw-r--r--src/w32term.c69
-rw-r--r--src/w32term.h2
5 files changed, 229 insertions, 220 deletions
diff --git a/src/dispextern.h b/src/dispextern.h
index cc2d963a96b..5d66fd8a489 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -3020,6 +3020,9 @@ struct image
3020 /* Picture versions of pixmap and mask for compositing. */ 3020 /* Picture versions of pixmap and mask for compositing. */
3021 Picture picture, mask_picture; 3021 Picture picture, mask_picture;
3022# endif 3022# endif
3023#endif /* HAVE_X_WINDOWS */
3024#ifdef HAVE_NTGUI
3025 XFORM xform;
3023#endif 3026#endif
3024 3027
3025 /* Colors allocated for this image, if any. Allocated via xmalloc. */ 3028 /* Colors allocated for this image, if any. Allocated via xmalloc. */
diff --git a/src/image.c b/src/image.c
index f3d6508f460..02dd95caeda 100644
--- a/src/image.c
+++ b/src/image.c
@@ -57,6 +57,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
57#include <sys/types.h> 57#include <sys/types.h>
58#endif /* HAVE_SYS_TYPES_H */ 58#endif /* HAVE_SYS_TYPES_H */
59 59
60#ifdef HAVE_NATIVE_TRANSFORMS
61#include <float.h> /* for FLT_MIN */
62#endif
63
60#ifdef HAVE_WINDOW_SYSTEM 64#ifdef HAVE_WINDOW_SYSTEM
61#include TERM_HEADER 65#include TERM_HEADER
62#endif /* HAVE_WINDOW_SYSTEM */ 66#endif /* HAVE_WINDOW_SYSTEM */
@@ -1965,12 +1969,10 @@ compute_image_size (size_t width, size_t height,
1965 *d_width = desired_width; 1969 *d_width = desired_width;
1966 *d_height = desired_height; 1970 *d_height = desired_height;
1967} 1971}
1968#endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */
1969 1972
1970/* image_set_rotation, image_set_crop, image_set_size and 1973/* image_set_rotation and image_set_transform use affine
1971 image_set_transform use affine transformation matrices to perform 1974 transformation matrices to perform various transforms on the image.
1972 various transforms on the image. The matrix is a 2D array of 1975 The matrix is a 2D array of doubles. It is laid out like this:
1973 doubles. It is laid out like this:
1974 1976
1975 m[0][0] = m11 | m[1][0] = m12 | m[2][0] = tx 1977 m[0][0] = m11 | m[1][0] = m12 | m[2][0] = tx
1976 --------------+---------------+------------- 1978 --------------+---------------+-------------
@@ -2039,14 +2041,15 @@ compute_image_size (size_t width, size_t height,
2039 finally move the origin back to the top left of the image, which 2041 finally move the origin back to the top left of the image, which
2040 may now be a different corner. 2042 may now be a different corner.
2041 2043
2042 Cropping is easier as we just move the origin to the top left of 2044 Note that different GUI backends (X, Cairo, w32, NS) want the
2043 where we want to crop and set the width and height accordingly. 2045 transform matrix defined as transform from the original image to
2044 The matrices don't know anything about width and height. 2046 the transformed image, while others want the matrix to describe the
2047 transform of the space, which boils down to inverting the matrix.
2045 2048
2046 It's possible to pre-calculate the matrix multiplications and just 2049 It's possible to pre-calculate the matrix multiplications and just
2047 generate one transform matrix that will do everything we need in a 2050 generate one transform matrix that will do everything we need in a
2048 single step, but the maths for each element is much more complex 2051 single step, but the maths for each element is much more complex
2049 and I thought it was better to perform the steps separately. */ 2052 and performing the steps separately makes for more readable code. */
2050 2053
2051typedef double matrix3x3[3][3]; 2054typedef double matrix3x3[3][3];
2052 2055
@@ -2070,102 +2073,27 @@ matrix3x3_mult (matrix3x3 a, matrix3x3 b, matrix3x3 result)
2070} 2073}
2071 2074
2072static void 2075static void
2073image_set_rotation (struct image *img, matrix3x3 tm) 2076compute_image_rotation (struct image *img, double *rotation)
2074{ 2077{
2075#ifdef HAVE_NATIVE_TRANSFORMS
2076# ifdef HAVE_IMAGEMAGICK
2077 /* ImageMagick images are already rotated. */
2078 if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
2079 return;
2080# endif
2081
2082# if !defined USE_CAIRO && defined HAVE_XRENDER
2083 if (!img->picture)
2084 return;
2085# endif
2086
2087 int rotation, cos_r, sin_r, width, height;
2088
2089 Lisp_Object value = image_spec_value (img->spec, QCrotation, NULL); 2078 Lisp_Object value = image_spec_value (img->spec, QCrotation, NULL);
2090 if (! NUMBERP (value)) 2079 if (! NUMBERP (value))
2091 return;
2092
2093 Lisp_Object reduced_angle = Fmod (value, make_fixnum (360));
2094 if (! FLOATP (reduced_angle))
2095 rotation = XFIXNUM (reduced_angle);
2096 else
2097 { 2080 {
2098 rotation = XFLOAT_DATA (reduced_angle); 2081 image_error ("Invalid image `:rotation' parameter");
2099 if (rotation != XFLOAT_DATA (reduced_angle))
2100 goto not_a_multiple_of_90;
2101 }
2102
2103 if (rotation == 0)
2104 return;
2105
2106 if (rotation == 90)
2107 {
2108 width = img->height;
2109 height = img->width;
2110
2111 cos_r = 0;
2112 sin_r = 1;
2113 }
2114 else if (rotation == 180)
2115 {
2116 width = img->width;
2117 height = img->height;
2118
2119 cos_r = -1;
2120 sin_r = 0;
2121 }
2122 else if (rotation == 270)
2123 {
2124 width = img->height;
2125 height = img->width;
2126
2127 cos_r = 0;
2128 sin_r = -1;
2129 }
2130 else
2131 {
2132 not_a_multiple_of_90:
2133 image_error ("Native image rotation supports "
2134 "only multiples of 90 degrees");
2135 return; 2082 return;
2136 } 2083 }
2137 2084
2138 /* Translate so (0, 0) is in the center of the image. */ 2085 Lisp_Object reduced_angle = Fmod (value, make_fixnum (360));
2139 matrix3x3 t 2086 if (FLOATP (reduced_angle))
2140 = { [0][0] = 1, 2087 *rotation = XFLOAT_DATA (reduced_angle);
2141 [1][1] = 1, 2088 else
2142 [2][0] = img->width * .5, [2][1] = img->height * .5, [2][2] = 1 }; 2089 *rotation = XFIXNUM (reduced_angle);
2143 matrix3x3 tmp;
2144 matrix3x3_mult (t, tm, tmp);
2145
2146 /* Rotate. */
2147 matrix3x3 rot = { [0][0] = cos_r, [0][1] = -sin_r,
2148 [1][0] = sin_r, [1][1] = cos_r,
2149 [2][2] = 1 };
2150 matrix3x3 tmp2;
2151 matrix3x3_mult (rot, tmp, tmp2);
2152
2153 /* Translate back. */
2154 t[2][0] = width * -.5;
2155 t[2][1] = height * -.5;
2156 matrix3x3_mult (t, tmp2, tm);
2157
2158 img->width = width;
2159 img->height = height;
2160#endif
2161} 2090}
2162 2091
2163static void 2092static void
2164image_set_crop (struct image *img, matrix3x3 tm) 2093image_set_transform (struct frame *f, struct image *img)
2165{ 2094{
2166#ifdef HAVE_NATIVE_TRANSFORMS
2167# ifdef HAVE_IMAGEMAGICK 2095# ifdef HAVE_IMAGEMAGICK
2168 /* ImageMagick images are already cropped. */ 2096 /* ImageMagick images already have the correct transform. */
2169 if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick)) 2097 if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
2170 return; 2098 return;
2171# endif 2099# endif
@@ -2175,119 +2103,112 @@ image_set_crop (struct image *img, matrix3x3 tm)
2175 return; 2103 return;
2176# endif 2104# endif
2177 2105
2178 Lisp_Object crop = image_spec_value (img->spec, QCcrop, NULL); 2106 matrix3x3 matrix = { [0][0] = 1, [1][1] = 1, [2][2] = 1 };
2179
2180 if (!CONSP (crop))
2181 return;
2182
2183 Lisp_Object w = XCAR (crop), h = Qnil, x = Qnil, y = Qnil;
2184 crop = XCDR (crop);
2185 if (CONSP (crop))
2186 {
2187 h = XCAR (crop);
2188 crop = XCDR (crop);
2189 if (CONSP (crop))
2190 {
2191 x = XCAR (crop);
2192 crop = XCDR (crop);
2193 if (CONSP (crop))
2194 y = XCAR (crop);
2195 }
2196 }
2197
2198 int width = img->width;
2199 if (FIXNATP (w) && XFIXNAT (w) < img->width)
2200 width = XFIXNAT (w);
2201 int left;
2202 if (TYPE_RANGED_FIXNUMP (int, x))
2203 {
2204 left = XFIXNUM (x);
2205 if (left < 0)
2206 left = img->width - width + left;
2207 }
2208 else
2209 left = (img->width - width) >> 1;
2210
2211 int height = img->height;
2212 if (FIXNATP (h) && XFIXNAT (h) < img->height)
2213 height = XFIXNAT (h);
2214 int top;
2215 if (TYPE_RANGED_FIXNUMP (int, y))
2216 {
2217 top = XFIXNUM (y);
2218 if (top < 0)
2219 top = img->height - height + top;
2220 }
2221 else
2222 top = (img->height - height) >> 1;
2223
2224 /* Negative values operate from the right and bottom of the image
2225 instead of the left and top. */
2226 if (left < 0)
2227 {
2228 width = img->width + left;
2229 left = 0;
2230 }
2231 2107
2232 if (width + left > img->width) 2108 /* Determine size. */
2233 width = img->width - left; 2109 int width, height;
2110 compute_image_size (img->width, img->height, img->spec, &width, &height);
2234 2111
2235 if (top < 0) 2112 /* Determine rotation. */
2236 { 2113 double rotation = 0.0;
2237 height = img->height + top; 2114 compute_image_rotation (img, &rotation);
2238 top = 0;
2239 }
2240 2115
2241 if (height + top > img->height) 2116 /* Perform scale transformation. */
2242 height = img->height - top;
2243 2117
2244 matrix3x3 tmp, m = { [0][0] = 1, 2118 double xscale = (double) width / img->width;
2245 [1][1] = 1, 2119 double yscale = (double) height / img->height;
2246 [2][0] = left, [2][1] = top, [2][2] = 1 };
2247 matrix3x3_mult (m, tm, tmp);
2248 matrix3x3_copy (tmp, tm);
2249 2120
2121# if defined USE_CAIRO || defined HAVE_XRENDER
2122 /* Avoid division by zero. */
2123 xscale = max (xscale, FLT_MIN);
2124 matrix3x3 tmp, rm =
2125 { [0][0] = 1.0 / xscale, [1][1] = 1.0 / yscale, [2][2] = 1 };
2126 matrix3x3_mult (rm, matrix, tmp);
2127# elif defined HAVE_NTGUI || defined HAVE_NS
2128 matrix3x3 tmp, rm = { [0][0] = xscale, [1][1] = yscale, [2][2] = 1 };
2129 matrix3x3_mult (matrix, rm, tmp);
2130# endif
2250 img->width = width; 2131 img->width = width;
2251 img->height = height; 2132 img->height = height;
2252#endif
2253}
2254 2133
2255static void 2134 /* Perform rotation transformation. */
2256image_set_size (struct image *img, matrix3x3 tm)
2257{
2258#ifdef HAVE_NATIVE_TRANSFORMS
2259# ifdef HAVE_IMAGEMAGICK
2260 /* ImageMagick images are already the correct size. */
2261 if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
2262 return;
2263# endif
2264 2135
2265# if !defined USE_CAIRO && defined HAVE_XRENDER 2136 int cos_r, sin_r;
2266 if (!img->picture) 2137 if (rotation == 0)
2267 return; 2138 matrix3x3_copy (tmp, matrix);
2268# endif 2139 else if (rotation == 90 || rotation == 180 || rotation == 270)
2269 2140 {
2270 int width, height; 2141 if (rotation == 90)
2271 2142 {
2272 compute_image_size (img->width, img->height, img->spec, &width, &height); 2143 width = img->height;
2144 height = img->width;
2273 2145
2274 double xscale = img->width / (double) width; 2146 cos_r = 0;
2275 double yscale = img->height / (double) height; 2147 sin_r = 1;
2148 }
2149 else if (rotation == 180)
2150 {
2151 width = img->width;
2152 height = img->height;
2276 2153
2277 matrix3x3 tmp, rm = { [0][0] = xscale, [1][1] = yscale, [2][2] = 1 }; 2154 cos_r = -1;
2278 matrix3x3_mult (rm, tm, tmp); 2155 sin_r = 0;
2279 matrix3x3_copy (tmp, tm); 2156 }
2157 else if (rotation == 270)
2158 {
2159 width = img->height;
2160 height = img->width;
2280 2161
2281 img->width = width; 2162 cos_r = 0;
2282 img->height = height; 2163 sin_r = -1;
2283#endif 2164 }
2284} 2165# if defined USE_CAIRO || defined HAVE_XRENDER
2166 /* 1. Translate so (0, 0) is in the center of the image. */
2167 matrix3x3 t
2168 = { [0][0] = 1,
2169 [1][1] = 1,
2170 [2][0] = img->width * .5, [2][1] = img->height * .5, [2][2] = 1 };
2171 matrix3x3 tmp2;
2172 matrix3x3_mult (t, tmp, tmp2);
2173
2174 /* 2. Rotate. */
2175 matrix3x3 rot = { [0][0] = cos_r, [0][1] = -sin_r,
2176 [1][0] = sin_r, [1][1] = cos_r,
2177 [2][2] = 1 };
2178 matrix3x3 tmp3;
2179 matrix3x3_mult (rot, tmp2, tmp3);
2180
2181 /* 3. Translate back. */
2182 t[2][0] = width * -.5;
2183 t[2][1] = height * -.5;
2184 matrix3x3_mult (t, tmp3, matrix);
2185# elif defined HAVE_NTGUI || defined HAVE_NS
2186 /* 1. Translate so (0, 0) is in the center of the image. */
2187 matrix3x3 t
2188 = { [0][0] = 1,
2189 [1][1] = 1,
2190 [2][0] = -img->width * .5, [2][1] = -img->height * .5, [2][2] = 1 };
2191 matrix3x3 tmp2;
2192 matrix3x3_mult (tmp, t, tmp2);
2193
2194 /* 2. Rotate. */
2195 matrix3x3 rot = { [0][0] = cos_r, [0][1] = sin_r,
2196 [1][0] = -sin_r, [1][1] = cos_r,
2197 [2][2] = 1 };
2198 matrix3x3 tmp3;
2199 matrix3x3_mult (tmp2, rot, tmp3);
2200
2201 /* 3. Translate back. */
2202 t[2][0] = width * .5;
2203 t[2][1] = height * .5;
2204 matrix3x3_mult (tmp3, t, matrix);
2205# endif
2206 img->width = width;
2207 img->height = height;
2208 }
2209 else
2210 image_error ("Native image rotation supports only multiples of 90 degrees");
2285 2211
2286static void
2287image_set_transform (struct frame *f, struct image *img, matrix3x3 matrix)
2288{
2289 /* TODO: Add MS Windows support. */
2290#ifdef HAVE_NATIVE_TRANSFORMS
2291# if defined (HAVE_NS) 2212# if defined (HAVE_NS)
2292 /* Under NS the transform is applied to the drawing surface at 2213 /* Under NS the transform is applied to the drawing surface at
2293 drawing time, so store it for later. */ 2214 drawing time, so store it for later. */
@@ -2317,10 +2238,19 @@ image_set_transform (struct frame *f, struct image *img, matrix3x3 matrix)
2317 0, 0); 2238 0, 0);
2318 XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat); 2239 XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat);
2319 } 2240 }
2241# elif defined HAVE_NTGUI
2242 /* Store the transform matrix for application at draw time. */
2243 img->xform.eM11 = matrix[0][0];
2244 img->xform.eM12 = matrix[0][1];
2245 img->xform.eM21 = matrix[1][0];
2246 img->xform.eM22 = matrix[1][1];
2247 img->xform.eDx = matrix[2][0];
2248 img->xform.eDy = matrix[2][1];
2320# endif 2249# endif
2321#endif
2322} 2250}
2323 2251
2252#endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */
2253
2324/* Return the id of image with Lisp specification SPEC on frame F. 2254/* Return the id of image with Lisp specification SPEC on frame F.
2325 SPEC must be a valid Lisp image specification (see valid_image_p). */ 2255 SPEC must be a valid Lisp image specification (see valid_image_p). */
2326 2256
@@ -2377,11 +2307,7 @@ lookup_image (struct frame *f, Lisp_Object spec)
2377 int relief_bound; 2307 int relief_bound;
2378 2308
2379#ifdef HAVE_NATIVE_TRANSFORMS 2309#ifdef HAVE_NATIVE_TRANSFORMS
2380 matrix3x3 transform_matrix = { [0][0] = 1, [1][1] = 1, [2][2] = 1 }; 2310 image_set_transform (f, img);
2381 image_set_size (img, transform_matrix);
2382 image_set_crop (img, transform_matrix);
2383 image_set_rotation (img, transform_matrix);
2384 image_set_transform (f, img, transform_matrix);
2385#endif 2311#endif
2386 2312
2387 ascent = image_spec_value (spec, QCascent, NULL); 2313 ascent = image_spec_value (spec, QCascent, NULL);
@@ -10010,19 +9936,38 @@ DEFUN ("lookup-image", Flookup_image, Slookup_image, 1, 1, 0,
10010 9936
10011DEFUN ("image-transforms-p", Fimage_transforms_p, Simage_transforms_p, 0, 1, 0, 9937DEFUN ("image-transforms-p", Fimage_transforms_p, Simage_transforms_p, 0, 1, 0,
10012 doc: /* Test whether FRAME supports image transformation. 9938 doc: /* Test whether FRAME supports image transformation.
10013Return t if FRAME supports native transforms, nil otherwise. */) 9939Return list of capabilities if FRAME supports native transforms, nil otherwise.
9940FRAME defaults to the selected frame.
9941The list of capabilities can include one or more of the following:
9942
9943 - the symbol `scale' if FRAME supports image scaling
9944 - the symbol `rotate' if FRAME supports image rotation by arbitrary angles
9945 - the symbol `rotate90' if FRAME supports image rotation only by angles
9946 that are integral multiples of 90 degrees
9947 - the symbol `crop' if FRAME supports image cropping. */)
10014 (Lisp_Object frame) 9948 (Lisp_Object frame)
10015{ 9949{
10016#if defined (USE_CAIRO) || defined (HAVE_NS) || defined (HAVE_NTGUI) 9950 struct frame *f = decode_live_frame (frame);
10017 return Qt; 9951 if (FRAME_WINDOW_P (f))
10018#elif defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER) 9952 {
10019 int event_basep, error_basep; 9953#ifdef HAVE_NATIVE_TRANSFORMS
10020 9954# if defined HAVE_IMAGEMAGICK
10021 if (XRenderQueryExtension 9955 return list4 (Qscale, Qrotate90, Qrotate, Qcrop);
10022 (FRAME_X_DISPLAY (decode_window_system_frame (frame)), 9956# elif defined (USE_CAIRO) || defined (HAVE_NS)
10023 &event_basep, &error_basep)) 9957 return list2 (Qscale, Qrotate90);
10024 return Qt; 9958# elif defined (HAVE_NTGUI)
9959 return (w32_image_rotations_p ()
9960 ? list2 (Qscale, Qrotate90)
9961 : list1 (Qscale));
9962# elif defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER)
9963 int event_basep, error_basep;
9964
9965 if (XRenderQueryExtension (FRAME_X_DISPLAY (f),
9966 &event_basep, &error_basep))
9967 return list2 (Qscale, Qrotate90);
9968# endif
10025#endif 9969#endif
9970 }
10026 9971
10027 return Qnil; 9972 return Qnil;
10028} 9973}
@@ -10168,6 +10113,14 @@ non-numeric, there is no explicit limit on the size of images. */);
10168 DEFSYM (Qpostscript, "postscript"); 10113 DEFSYM (Qpostscript, "postscript");
10169 DEFSYM (QCmax_width, ":max-width"); 10114 DEFSYM (QCmax_width, ":max-width");
10170 DEFSYM (QCmax_height, ":max-height"); 10115 DEFSYM (QCmax_height, ":max-height");
10116
10117#ifdef HAVE_NATIVE_TRANSFORMS
10118 DEFSYM (Qscale, "scale");
10119 DEFSYM (Qrotate, "rotate");
10120 DEFSYM (Qrotate90, "rotate90");
10121 DEFSYM (Qcrop, "crop");
10122#endif
10123
10171#ifdef HAVE_GHOSTSCRIPT 10124#ifdef HAVE_GHOSTSCRIPT
10172 add_image_type (Qpostscript); 10125 add_image_type (Qpostscript);
10173 DEFSYM (QCloader, ":loader"); 10126 DEFSYM (QCloader, ":loader");
diff --git a/src/nsimage.m b/src/nsimage.m
index 7268e662638..6f0340302ca 100644
--- a/src/nsimage.m
+++ b/src/nsimage.m
@@ -530,10 +530,6 @@ ns_set_alpha (void *img, int x, int y, unsigned char a)
530 NSAffineTransformStruct tm 530 NSAffineTransformStruct tm
531 = { m[0][0], m[0][1], m[1][0], m[1][1], m[2][0], m[2][1]}; 531 = { m[0][0], m[0][1], m[1][0], m[1][1], m[2][0], m[2][1]};
532 [transform setTransformStruct:tm]; 532 [transform setTransformStruct:tm];
533
534 /* Because the transform is applied to the drawing surface, and not
535 the image itself, we need to invert it. */
536 [transform invert];
537} 533}
538 534
539@end 535@end
diff --git a/src/w32term.c b/src/w32term.c
index 97a5fc63892..c6e175e7e5c 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -117,6 +117,10 @@ typedef struct tagGLYPHSET
117/* Dynamic linking to SetLayeredWindowAttribute (only since 2000). */ 117/* Dynamic linking to SetLayeredWindowAttribute (only since 2000). */
118BOOL (WINAPI *pfnSetLayeredWindowAttributes) (HWND, COLORREF, BYTE, DWORD); 118BOOL (WINAPI *pfnSetLayeredWindowAttributes) (HWND, COLORREF, BYTE, DWORD);
119 119
120/* PlgBlt is available since Windows 2000. */
121BOOL (WINAPI *pfnPlgBlt) (HDC, const POINT *, HDC, int, int, int, int, HBITMAP, int, int);
122
123
120#ifndef LWA_ALPHA 124#ifndef LWA_ALPHA
121#define LWA_ALPHA 0x02 125#define LWA_ALPHA 0x02
122#endif 126#endif
@@ -1753,6 +1757,26 @@ w32_draw_glyph_string_box (struct glyph_string *s)
1753 } 1757 }
1754} 1758}
1755 1759
1760bool
1761w32_image_rotations_p (void)
1762{
1763 return pfnPlgBlt != NULL;
1764}
1765
1766static POINT
1767transform (int x0, int y0, int x, int y, XFORM *xform)
1768{
1769 POINT pt;
1770
1771 /* See https://docs.microsoft.com/en-us/windows/desktop/api/Wingdi/nf-wingdi-setworldtransform */
1772 pt.x =
1773 x0 + (x - x0) * xform->eM11 + (y - y0) * xform->eM21 + xform->eDx + 0.5f;
1774 pt.y =
1775 y0 + (x - x0) * xform->eM12 + (y - y0) * xform->eM22 + xform->eDy + 0.5f;
1776
1777 return pt;
1778}
1779
1756 1780
1757/* Draw foreground of image glyph string S. */ 1781/* Draw foreground of image glyph string S. */
1758 1782
@@ -1798,20 +1822,31 @@ w32_draw_image_foreground (struct glyph_string *s)
1798 } 1822 }
1799 else 1823 else
1800 { 1824 {
1801 DebPrint (("w32_draw_image_foreground: GetObject failed!\n")); 1825 DebPrint (("w32_draw_image_foreground: GetObject(pixmap) failed!\n"));
1802 orig_width = s->slice.width; 1826 orig_width = s->slice.width;
1803 orig_height = s->slice.height; 1827 orig_height = s->slice.height;
1804 } 1828 }
1805 1829
1806 double w_factor = 1.0, h_factor = 1.0; 1830 double w_factor = 1.0, h_factor = 1.0;
1807 bool scaled = false; 1831 bool scaled = false, need_xform = false;
1808 int orig_slice_width = s->slice.width, 1832 int orig_slice_width = s->slice.width,
1809 orig_slice_height = s->slice.height; 1833 orig_slice_height = s->slice.height;
1810 int orig_slice_x = s->slice.x, orig_slice_y = s->slice.y; 1834 int orig_slice_x = s->slice.x, orig_slice_y = s->slice.y;
1811 /* For scaled images we need to restore the original slice's 1835 POINT corner[3];
1812 dimensions and origin coordinates, from before the scaling. */ 1836 if ((s->img->xform.eM12 != 0 || s->img->xform.eM21 != 0
1813 if (s->img->width != orig_width || s->img->height != orig_height) 1837 || s->img->xform.eDx != 0 || s->img->xform.eDy != 0)
1838 /* PlgBlt is not available on Windows 9X. */
1839 && pfnPlgBlt)
1814 { 1840 {
1841 need_xform = true;
1842 corner[0] = transform (x, y, x, y, &s->img->xform);
1843 corner[1] = transform (x, y, x + orig_width, y, &s->img->xform);
1844 corner[2] = transform (x, y, x, y + orig_height, &s->img->xform);
1845 }
1846 else if (s->img->width != orig_width || s->img->height != orig_height)
1847 {
1848 /* For scaled images we need to restore the original slice's
1849 dimensions and origin coordinates, from before the scaling. */
1815 scaled = true; 1850 scaled = true;
1816 w_factor = (double) orig_width / (double) s->img->width; 1851 w_factor = (double) orig_width / (double) s->img->width;
1817 h_factor = (double) orig_height / (double) s->img->height; 1852 h_factor = (double) orig_height / (double) s->img->height;
@@ -1828,7 +1863,15 @@ w32_draw_image_foreground (struct glyph_string *s)
1828 1863
1829 SetTextColor (s->hdc, RGB (255, 255, 255)); 1864 SetTextColor (s->hdc, RGB (255, 255, 255));
1830 SetBkColor (s->hdc, RGB (0, 0, 0)); 1865 SetBkColor (s->hdc, RGB (0, 0, 0));
1831 if (!scaled) 1866 if (need_xform)
1867 {
1868 if (!pfnPlgBlt (s->hdc, corner, compat_hdc,
1869 s->slice.x, s->slice.y,
1870 orig_width, orig_height,
1871 s->img->mask, s->slice.x, s->slice.y))
1872 DebPrint (("PlgBlt failed!"));
1873 }
1874 else if (!scaled)
1832 { 1875 {
1833 BitBlt (s->hdc, x, y, s->slice.width, s->slice.height, 1876 BitBlt (s->hdc, x, y, s->slice.width, s->slice.height,
1834 compat_hdc, s->slice.x, s->slice.y, SRCINVERT); 1877 compat_hdc, s->slice.x, s->slice.y, SRCINVERT);
@@ -1865,7 +1908,14 @@ w32_draw_image_foreground (struct glyph_string *s)
1865 { 1908 {
1866 SetTextColor (s->hdc, s->gc->foreground); 1909 SetTextColor (s->hdc, s->gc->foreground);
1867 SetBkColor (s->hdc, s->gc->background); 1910 SetBkColor (s->hdc, s->gc->background);
1868 if (!scaled) 1911 if (need_xform)
1912 {
1913 if (!pfnPlgBlt (s->hdc, corner, compat_hdc,
1914 s->slice.x, s->slice.y,
1915 orig_width, orig_height, NULL, 0, 0))
1916 DebPrint (("PlgBlt failed!"));
1917 }
1918 else if (!scaled)
1869 BitBlt (s->hdc, x, y, s->slice.width, s->slice.height, 1919 BitBlt (s->hdc, x, y, s->slice.width, s->slice.height,
1870 compat_hdc, s->slice.x, s->slice.y, SRCCOPY); 1920 compat_hdc, s->slice.x, s->slice.y, SRCCOPY);
1871 else 1921 else
@@ -7354,6 +7404,11 @@ w32_initialize (void)
7354 7404
7355 LOAD_PROC (user_lib, SetLayeredWindowAttributes); 7405 LOAD_PROC (user_lib, SetLayeredWindowAttributes);
7356 7406
7407 /* PlgBlt is not available on Windows 9X. */
7408 HMODULE hgdi = LoadLibrary ("gdi32.dll");
7409 if (hgdi)
7410 LOAD_PROC (hgdi, PlgBlt);
7411
7357#undef LOAD_PROC 7412#undef LOAD_PROC
7358 7413
7359 /* Ensure scrollbar handles are at least 5 pixels. */ 7414 /* Ensure scrollbar handles are at least 5 pixels. */
diff --git a/src/w32term.h b/src/w32term.h
index 729e8d0fd49..6133e100c17 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -743,6 +743,8 @@ extern int handle_file_notifications (struct input_event *);
743extern void w32_initialize_display_info (Lisp_Object); 743extern void w32_initialize_display_info (Lisp_Object);
744extern void initialize_w32_display (struct terminal *, int *, int *); 744extern void initialize_w32_display (struct terminal *, int *, int *);
745 745
746extern bool w32_image_rotations_p (void);
747
746#ifdef WINDOWSNT 748#ifdef WINDOWSNT
747/* Keyboard hooks. */ 749/* Keyboard hooks. */
748extern void setup_w32_kbdhook (void); 750extern void setup_w32_kbdhook (void);