From: Eli Zaretskii Date: Sat, 29 Jun 2019 11:51:41 +0000 (+0300) Subject: Support native image transforms on MS-Windows X-Git-Tag: emacs-27.0.90~2152 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=74a5a332fee8a346cf65ed6656c1b08dc1df5fde;p=emacs.git 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. --- diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 217df3b2cc2..c95f2df0fb8 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -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 diff --git a/etc/NEWS b/etc/NEWS index 864eb8c1100..abbece374a4 100644 --- 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. 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 /* 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. */ 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 . */ #include #endif /* HAVE_SYS_TYPES_H */ +#ifdef HAVE_NATIVE_TRANSFORMS +#include /* 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"); 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) 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/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 /* 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. */ 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 *); 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); diff --git a/test/manual/image-transforms-tests.el b/test/manual/image-transforms-tests.el index d601b9397e3..e8b301e3331 100644 --- a/test/manual/image-transforms-tests.el +++ b/test/manual/image-transforms-tests.el @@ -67,7 +67,7 @@ ")) - (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)))