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:
parent
67b50770c0
commit
74a5a332fe
8 changed files with 275 additions and 252 deletions
|
@ -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
|
||||
|
|
10
etc/NEWS
10
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.
|
||||
|
|
|
@ -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. */
|
||||
|
|
377
src/image.c
377
src/image.c
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)))
|
||||
|
|
Loading…
Add table
Reference in a new issue