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.
This commit is contained in:
Eli Zaretskii 2019-06-29 14:51:41 +03:00
parent 67b50770c0
commit 74a5a332fe
8 changed files with 275 additions and 252 deletions

View 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:
If image transforms are not supported, @code{:rotation},
@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}, @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

View file

@ -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.

View 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. */

View 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;
}
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;
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);
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
# if !defined USE_CAIRO && defined HAVE_XRENDER
if (!img->picture)
return;
# endif
matrix3x3 matrix = { [0][0] = 1, [1][1] = 1, [2][2] = 1 };
/* Determine size. */
int width, height;
compute_image_size (img->width, img->height, img->spec, &width, &height);
double xscale = img->width / (double) width;
double yscale = img->height / (double) height;
/* Determine rotation. */
double rotation = 0.0;
compute_image_rotation (img, &rotation);
/* Perform scale transformation. */
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 (rm, tm, tmp);
matrix3x3_copy (tmp, tm);
matrix3x3_mult (matrix, rm, tmp);
# endif
img->width = width;
img->height = height;
#endif
}
static void
image_set_transform (struct frame *f, struct image *img, matrix3x3 matrix)
{
/* TODO: Add MS Windows support. */
#ifdef HAVE_NATIVE_TRANSFORMS
/* Perform rotation transformation. */
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;
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;
}
# 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");
# 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;
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 (decode_window_system_frame (frame)),
&event_basep, &error_basep))
return Qt;
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");

View file

@ -530,10 +530,6 @@ - (void)setTransform: (double[3][3]) m
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

View 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. */

View 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);

View 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)))