Add native image rotation and cropping
* lisp/image.el (image--get-imagemagick-and-warn): Only fallback to ImageMagick if native transforms aren't available. * src/dispextern.h (INIT_MATRIX, COPY_MATRIX, MULT_MATRICES): New macros for matrix manipulation. (HAVE_NATIVE_SCALING, HAVE_NATIVE_TRANSFORMS): Rename and change all relevant locations. * src/image.c (x_set_image_rotation): (x_set_transform): New functions. (x_set_image_size): Use transform matrix for resizing under X and NS. (x_set_image_crop): New function. (lookup_image): Use the new transform functions. (Fimage_scaling_p, Fimage_transforms_p): Rename and update all callers. * src/nsimage.m (ns_load_image): Remove rotation code. (ns_image_set_transform): New function. ([EmacsImage dealloc]): Release the saved transform. ([EmacsImage rotate:]): Remove unneeded method. ([EmacsImage setTransform:]): New method. * src/nsterm.h (EmacsImage): Add transform property and update method definitions. * src/nsterm.m (ns_dumpglyphs_image): Use the transform to draw the image correctly. * src/xterm.c (x_composite_image): Use PictOpSrc as we don't care about alpha values here. * doc/lispref/display.texi (Image Descriptors): Add :rotation. (ImageMagick Images): Remove :rotation.
This commit is contained in:
parent
9201cf62ce
commit
610fb73ab6
9 changed files with 347 additions and 113 deletions
|
@ -5181,6 +5181,9 @@ values. If both @code{:scale} and @code{:height}/@code{:width} are
|
|||
specified, the height/width will be adjusted by the specified scaling
|
||||
factor.
|
||||
|
||||
@item :rotation @var{angle}
|
||||
Specifies a rotation angle in degrees.
|
||||
|
||||
@item :index @var{frame}
|
||||
@xref{Multi-Frame Images}.
|
||||
|
||||
|
@ -5323,14 +5326,15 @@ This function returns @code{t} if image @var{spec} has a mask bitmap.
|
|||
(@pxref{Input Focus}).
|
||||
@end defun
|
||||
|
||||
@defun image-scaling-p &optional frame
|
||||
This function returns @code{t} if @var{frame} supports image scaling.
|
||||
@var{frame} @code{nil} or omitted means to use the selected frame
|
||||
(@pxref{Input Focus}).
|
||||
@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}).
|
||||
|
||||
If image scaling is not supported, @code{:width}, @code{:height},
|
||||
@code{:scale}, @code{:max-width} and @code{:max-height} will only be
|
||||
usable through ImageMagick, if available (@pxref{ImageMagick Images}).
|
||||
If image transforms are not supported, @code{:rotation},
|
||||
@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
|
||||
|
@ -5474,9 +5478,6 @@ 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 :rotation @var{angle}
|
||||
Specifies a rotation angle in degrees.
|
||||
@end table
|
||||
|
||||
@node SVG Images
|
||||
|
|
6
etc/NEWS
6
etc/NEWS
|
@ -2051,14 +2051,14 @@ buffer's 'default-directory' and invoke that file name handler to make
|
|||
the process. That way 'make-process' can start remote processes.
|
||||
|
||||
+++
|
||||
** Emacs now supports resizing (scaling) of images without ImageMagick.
|
||||
** 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.)
|
||||
|
||||
The new function 'image-scaling-p' can be used to test whether any
|
||||
given frame supports resizing.
|
||||
The new function 'image-transforms-p' can be used to test whether any
|
||||
given frame supports this capability.
|
||||
|
||||
+++
|
||||
** '(locale-info 'paper)' now returns the paper size on systems that support it.
|
||||
|
|
|
@ -992,11 +992,12 @@ default is 20%."
|
|||
image))
|
||||
|
||||
(defun image--get-imagemagick-and-warn ()
|
||||
(unless (or (fboundp 'imagemagick-types) (image-scaling-p))
|
||||
(unless (or (fboundp 'imagemagick-types) (image-transforms-p))
|
||||
(error "Cannot rescale images on this terminal"))
|
||||
(let ((image (image--get-image)))
|
||||
(image-flush image)
|
||||
(when (fboundp 'imagemagick-types)
|
||||
(when (and (fboundp 'imagemagick-types)
|
||||
(not (image-transforms-p)))
|
||||
(plist-put (cdr image) :type 'imagemagick))
|
||||
image))
|
||||
|
||||
|
|
|
@ -2989,7 +2989,25 @@ struct redisplay_interface
|
|||
#ifdef HAVE_WINDOW_SYSTEM
|
||||
|
||||
# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS || defined HAVE_NTGUI
|
||||
# define HAVE_NATIVE_SCALING
|
||||
# define HAVE_NATIVE_TRANSFORMS
|
||||
|
||||
# define INIT_MATRIX(m) \
|
||||
for (int i = 0 ; i < 3 ; i++) \
|
||||
for (int j = 0 ; j < 3 ; j++) \
|
||||
m[i][j] = (i == j) ? 1 : 0;
|
||||
|
||||
# define COPY_MATRIX(a, b) \
|
||||
for (int i = 0 ; i < 3 ; i++) \
|
||||
for (int j = 0 ; j < 3 ; j++) \
|
||||
b[i][j] = a[i][j];
|
||||
|
||||
# define MULT_MATRICES(a, b, result) \
|
||||
for (int i = 0 ; i < 3 ; i++) \
|
||||
for (int j = 0 ; j < 3 ; j++) { \
|
||||
double sum = 0; \
|
||||
for (int k = 0 ; k < 3 ; k++) \
|
||||
sum += a[k][j] * b[i][k]; \
|
||||
result[i][j] = sum;}
|
||||
# endif
|
||||
|
||||
/* Structure describing an image. Specific image formats like XBM are
|
||||
|
@ -3015,7 +3033,7 @@ struct image
|
|||
synchronized to Pixmap. */
|
||||
XImage *ximg, *mask_img;
|
||||
|
||||
# ifdef HAVE_NATIVE_SCALING
|
||||
# ifdef HAVE_NATIVE_TRANSFORMS
|
||||
/* Picture versions of pixmap and mask for compositing. */
|
||||
Picture picture, mask_picture;
|
||||
# endif
|
||||
|
|
293
src/image.c
293
src/image.c
|
@ -1841,7 +1841,7 @@ postprocess_image (struct frame *f, struct image *img)
|
|||
}
|
||||
}
|
||||
|
||||
#if defined (HAVE_IMAGEMAGICK) || defined (HAVE_NATIVE_SCALING)
|
||||
#if defined (HAVE_IMAGEMAGICK) || defined (HAVE_NATIVE_TRANSFORMS)
|
||||
/* Scale an image size by returning SIZE / DIVISOR * MULTIPLIER,
|
||||
safely rounded and clipped to int range. */
|
||||
|
||||
|
@ -1940,49 +1940,241 @@ compute_image_size (size_t width, size_t height,
|
|||
*d_width = desired_width;
|
||||
*d_height = desired_height;
|
||||
}
|
||||
#endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_SCALING */
|
||||
#endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */
|
||||
|
||||
static void
|
||||
image_set_image_size (struct frame *f, struct image *img)
|
||||
image_set_rotation (struct image *img, double tm[3][3])
|
||||
{
|
||||
#ifdef HAVE_NATIVE_SCALING
|
||||
#ifdef HAVE_NATIVE_TRANSFORMS
|
||||
# ifdef HAVE_IMAGEMAGICK
|
||||
/* ImageMagick images are already the correct size. */
|
||||
/* ImageMagick images are already rotated. */
|
||||
if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
|
||||
return;
|
||||
# endif
|
||||
|
||||
int width, height;
|
||||
compute_image_size (img->width, img->height, img->spec, &width, &height);
|
||||
# ifdef HAVE_XRENDER
|
||||
if (!img->picture)
|
||||
return;
|
||||
# endif
|
||||
|
||||
Lisp_Object value;
|
||||
double t[3][3], rot[3][3], tmp[3][3], tmp2[3][3];
|
||||
int rotation, cos_r, sin_r, width, height;
|
||||
|
||||
value = image_spec_value (img->spec, QCrotation, NULL);
|
||||
if (! NUMBERP (value))
|
||||
return;
|
||||
|
||||
rotation = XFLOATINT (value);
|
||||
rotation = rotation % 360;
|
||||
|
||||
if (rotation < 0)
|
||||
rotation += 360;
|
||||
|
||||
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
|
||||
{
|
||||
image_error ("Native image rotation only supports multiples of 90 degrees");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Translate so (0, 0) is in the centre of the image. */
|
||||
INIT_MATRIX (t);
|
||||
t[2][0] = img->width/2;
|
||||
t[2][1] = img->height/2;
|
||||
|
||||
MULT_MATRICES (tm, t, tmp);
|
||||
|
||||
/* Rotate. */
|
||||
INIT_MATRIX (rot);
|
||||
rot[0][0] = cos_r;
|
||||
rot[1][0] = sin_r;
|
||||
rot[0][1] = - sin_r;
|
||||
rot[1][1] = cos_r;
|
||||
|
||||
MULT_MATRICES (tmp, rot, tmp2);
|
||||
|
||||
/* Translate back. */
|
||||
INIT_MATRIX (t);
|
||||
t[2][0] = - width/2;
|
||||
t[2][1] = - height/2;
|
||||
|
||||
MULT_MATRICES (tmp2, t, tm);
|
||||
|
||||
# ifdef HAVE_NS
|
||||
ns_image_set_size (img->pixmap, width, height);
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
image_set_crop (struct image *img, double tm[3][3])
|
||||
{
|
||||
#ifdef HAVE_NATIVE_TRANSFORMS
|
||||
# ifdef HAVE_IMAGEMAGICK
|
||||
/* ImageMagick images are already cropped. */
|
||||
if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
|
||||
return;
|
||||
# endif
|
||||
|
||||
# ifdef USE_CAIRO
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
# elif defined HAVE_XRENDER
|
||||
if (img->picture)
|
||||
{
|
||||
double xscale = img->width / (double) width;
|
||||
double yscale = img->height / (double) height;
|
||||
|
||||
XTransform tmat
|
||||
= {{{XDoubleToFixed (xscale), XDoubleToFixed (0), XDoubleToFixed (0)},
|
||||
{XDoubleToFixed (0), XDoubleToFixed (yscale), XDoubleToFixed (0)},
|
||||
{XDoubleToFixed (0), XDoubleToFixed (0), XDoubleToFixed (1)}}};
|
||||
|
||||
XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, FilterBest,
|
||||
0, 0);
|
||||
XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat);
|
||||
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
}
|
||||
if (!img->picture)
|
||||
return;
|
||||
# endif
|
||||
|
||||
double m[3][3], tmp[3][3];
|
||||
int left, top, width, height;
|
||||
Lisp_Object x = Qnil;
|
||||
Lisp_Object y = Qnil;
|
||||
Lisp_Object w = Qnil;
|
||||
Lisp_Object h = Qnil;
|
||||
Lisp_Object crop = image_spec_value (img->spec, QCcrop, NULL);
|
||||
|
||||
if (!CONSP (crop))
|
||||
return;
|
||||
else
|
||||
{
|
||||
w = XCAR (crop);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FIXNATP (w) && XFIXNAT (w) < img->width)
|
||||
width = XFIXNAT (w);
|
||||
else
|
||||
width = img->width;
|
||||
|
||||
if (TYPE_RANGED_FIXNUMP (int, x))
|
||||
{
|
||||
left = XFIXNUM (x);
|
||||
if (left < 0)
|
||||
left = img->width - width + left;
|
||||
}
|
||||
else
|
||||
left = (img->width - width)/2;
|
||||
|
||||
if (FIXNATP (h) && XFIXNAT (h) < img->height)
|
||||
height = XFIXNAT (h);
|
||||
else
|
||||
height = img->height;
|
||||
|
||||
if (TYPE_RANGED_FIXNUMP (int, y))
|
||||
{
|
||||
top = XFIXNUM (y);
|
||||
if (top < 0)
|
||||
top = img->height - height + top;
|
||||
}
|
||||
else
|
||||
top = (img->height - height)/2;
|
||||
|
||||
/* 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;
|
||||
|
||||
INIT_MATRIX (m);
|
||||
m[2][0] = left;
|
||||
m[2][1] = top;
|
||||
|
||||
MULT_MATRICES (tm, m, tmp);
|
||||
COPY_MATRIX (tmp, tm);
|
||||
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
image_set_size (struct image *img, double tm[3][3])
|
||||
{
|
||||
#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
|
||||
|
||||
# ifdef HAVE_XRENDER
|
||||
if (!img->picture)
|
||||
return;
|
||||
# endif
|
||||
|
||||
int width, height;
|
||||
|
||||
compute_image_size (img->width, img->height, img->spec, &width, &height);
|
||||
|
||||
# if defined (HAVE_NS) || defined (HAVE_XRENDER)
|
||||
double rm[3][3], tmp[3][3];
|
||||
double xscale, yscale;
|
||||
|
||||
xscale = img->width / (double) width;
|
||||
yscale = img->height / (double) height;
|
||||
|
||||
INIT_MATRIX (rm);
|
||||
rm[0][0] = xscale;
|
||||
rm[1][1] = yscale;
|
||||
|
||||
MULT_MATRICES (tm, rm, tmp);
|
||||
COPY_MATRIX (tmp, tm);
|
||||
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
# endif
|
||||
|
||||
# ifdef HAVE_NTGUI
|
||||
/* Under HAVE_NTGUI, we will scale the image on the fly, when we
|
||||
draw it. See w32term.c:x_draw_image_foreground. */
|
||||
|
@ -1992,6 +2184,36 @@ image_set_image_size (struct frame *f, struct image *img)
|
|||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
image_set_transform (struct frame *f, struct image *img, double matrix[3][3])
|
||||
{
|
||||
/* 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. */
|
||||
ns_image_set_transform (img->pixmap, matrix);
|
||||
# elif defined (HAVE_XRENDER)
|
||||
if (img->picture)
|
||||
{
|
||||
XTransform tmat
|
||||
= {{{XDoubleToFixed (matrix[0][0]),
|
||||
XDoubleToFixed (matrix[1][0]),
|
||||
XDoubleToFixed (matrix[2][0])},
|
||||
{XDoubleToFixed (matrix[0][1]),
|
||||
XDoubleToFixed (matrix[1][1]),
|
||||
XDoubleToFixed (matrix[2][1])},
|
||||
{XDoubleToFixed (matrix[0][2]),
|
||||
XDoubleToFixed (matrix[1][2]),
|
||||
XDoubleToFixed (matrix[2][2])}}};
|
||||
|
||||
XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, FilterBest,
|
||||
0, 0);
|
||||
XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Return the id of image with Lisp specification SPEC on frame F.
|
||||
SPEC must be a valid Lisp image specification (see valid_image_p). */
|
||||
|
@ -2047,7 +2269,16 @@ lookup_image (struct frame *f, Lisp_Object spec)
|
|||
`:background COLOR'. */
|
||||
Lisp_Object ascent, margin, relief, bg;
|
||||
int relief_bound;
|
||||
image_set_image_size (f, img);
|
||||
|
||||
#ifdef HAVE_NATIVE_TRANSFORMS
|
||||
double transform_matrix[3][3];
|
||||
|
||||
INIT_MATRIX (transform_matrix);
|
||||
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);
|
||||
#endif
|
||||
|
||||
ascent = image_spec_value (spec, QCascent, NULL);
|
||||
if (FIXNUMP (ascent))
|
||||
|
@ -9673,9 +9904,9 @@ DEFUN ("lookup-image", Flookup_image, Slookup_image, 1, 1, 0,
|
|||
Initialization
|
||||
***********************************************************************/
|
||||
|
||||
DEFUN ("image-scaling-p", Fimage_scaling_p, Simage_scaling_p, 0, 1, 0,
|
||||
doc: /* Test whether FRAME supports resizing images.
|
||||
Return t if FRAME supports native scaling, nil otherwise. */)
|
||||
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. */)
|
||||
(Lisp_Object frame)
|
||||
{
|
||||
#if defined (USE_CAIRO) || defined (HAVE_NS) || defined (HAVE_NTGUI)
|
||||
|
@ -9935,7 +10166,7 @@ non-numeric, there is no explicit limit on the size of images. */);
|
|||
defsubr (&Slookup_image);
|
||||
#endif
|
||||
|
||||
defsubr (&Simage_scaling_p);
|
||||
defsubr (&Simage_transforms_p);
|
||||
|
||||
DEFVAR_BOOL ("cross-disabled-images", cross_disabled_images,
|
||||
doc: /* Non-nil means always draw a cross over disabled images.
|
||||
|
|
|
@ -76,9 +76,8 @@ Updated by Christian Limpach (chris@nice.ch)
|
|||
{
|
||||
EmacsImage *eImg = nil;
|
||||
NSSize size;
|
||||
Lisp_Object lisp_index, lisp_rotation;
|
||||
Lisp_Object lisp_index;
|
||||
unsigned int index;
|
||||
double rotation;
|
||||
|
||||
NSTRACE ("ns_load_image");
|
||||
|
||||
|
@ -87,9 +86,6 @@ Updated by Christian Limpach (chris@nice.ch)
|
|||
lisp_index = Fplist_get (XCDR (img->spec), QCindex);
|
||||
index = FIXNUMP (lisp_index) ? XFIXNAT (lisp_index) : 0;
|
||||
|
||||
lisp_rotation = Fplist_get (XCDR (img->spec), QCrotation);
|
||||
rotation = NUMBERP (lisp_rotation) ? XFLOATINT (lisp_rotation) : 0;
|
||||
|
||||
if (STRINGP (spec_file))
|
||||
{
|
||||
eImg = [EmacsImage allocInitFromFile: spec_file];
|
||||
|
@ -119,13 +115,6 @@ Updated by Christian Limpach (chris@nice.ch)
|
|||
|
||||
img->lisp_data = [eImg getMetadata];
|
||||
|
||||
if (rotation != 0)
|
||||
{
|
||||
EmacsImage *temp = [eImg rotate:rotation];
|
||||
[eImg release];
|
||||
eImg = temp;
|
||||
}
|
||||
|
||||
size = [eImg size];
|
||||
img->width = size.width;
|
||||
img->height = size.height;
|
||||
|
@ -155,6 +144,12 @@ Updated by Christian Limpach (chris@nice.ch)
|
|||
[(EmacsImage *)img setSize:NSMakeSize (width, height)];
|
||||
}
|
||||
|
||||
void
|
||||
ns_image_set_transform (void *img, double m[3][3])
|
||||
{
|
||||
[(EmacsImage *)img setTransform:m];
|
||||
}
|
||||
|
||||
unsigned long
|
||||
ns_get_pixel (void *img, int x, int y)
|
||||
{
|
||||
|
@ -225,6 +220,7 @@ - (void)dealloc
|
|||
{
|
||||
[stippleMask release];
|
||||
[bmRep release];
|
||||
[transform release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
@ -528,42 +524,16 @@ - (BOOL)setFrame: (unsigned int) index
|
|||
return YES;
|
||||
}
|
||||
|
||||
- (instancetype)rotate: (double)rotation
|
||||
- (void)setTransform: (double[3][3]) m
|
||||
{
|
||||
EmacsImage *new_image;
|
||||
NSPoint new_origin;
|
||||
NSSize new_size, size = [self size];
|
||||
NSRect rect = { NSZeroPoint, [self size] };
|
||||
transform = [[NSAffineTransform transform] retain];
|
||||
NSAffineTransformStruct tm
|
||||
= { m[0][0], m[0][1], m[1][0], m[1][1], m[2][0], m[2][1]};
|
||||
[transform setTransformStruct:tm];
|
||||
|
||||
/* Create a bezier path of the outline of the image and do the
|
||||
* rotation on it. */
|
||||
NSBezierPath *bounds_path = [NSBezierPath bezierPathWithRect:rect];
|
||||
NSAffineTransform *transform = [NSAffineTransform transform];
|
||||
[transform rotateByDegrees: rotation * -1];
|
||||
[bounds_path transformUsingAffineTransform:transform];
|
||||
|
||||
/* Now we can find out how large the rotated image needs to be. */
|
||||
new_size = [bounds_path bounds].size;
|
||||
new_image = [[EmacsImage alloc] initWithSize:new_size];
|
||||
|
||||
new_origin = NSMakePoint((new_size.width - size.width)/2,
|
||||
(new_size.height - size.height)/2);
|
||||
|
||||
[new_image lockFocus];
|
||||
|
||||
/* Create the final transform. */
|
||||
transform = [NSAffineTransform transform];
|
||||
[transform translateXBy:new_size.width/2 yBy:new_size.height/2];
|
||||
[transform rotateByDegrees: rotation * -1];
|
||||
[transform translateXBy:-new_size.width/2 yBy:-new_size.height/2];
|
||||
|
||||
[transform concat];
|
||||
[self drawAtPoint:new_origin fromRect:NSZeroRect
|
||||
operation:NSCompositingOperationCopy fraction:1];
|
||||
|
||||
[new_image unlockFocus];
|
||||
|
||||
return new_image;
|
||||
/* Because the transform is applied to the drawing surface, and not
|
||||
the image itself, we need to invert it. */
|
||||
[transform invert];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -632,6 +632,8 @@ typedef id instancetype;
|
|||
unsigned char *pixmapData[5]; /* shortcut to access pixel data */
|
||||
NSColor *stippleMask;
|
||||
unsigned long xbm_fg;
|
||||
@public
|
||||
NSAffineTransform *transform;
|
||||
}
|
||||
+ (instancetype)allocInitFromFile: (Lisp_Object)file;
|
||||
- (void)dealloc;
|
||||
|
@ -648,7 +650,7 @@ typedef id instancetype;
|
|||
- (NSColor *)stippleMask;
|
||||
- (Lisp_Object)getMetadata;
|
||||
- (BOOL)setFrame: (unsigned int) index;
|
||||
- (instancetype)rotate: (double)rotation;
|
||||
- (void)setTransform: (double[3][3]) m;
|
||||
@end
|
||||
|
||||
|
||||
|
@ -1201,6 +1203,7 @@ extern bool ns_load_image (struct frame *f, struct image *img,
|
|||
extern int ns_image_width (void *img);
|
||||
extern int ns_image_height (void *img);
|
||||
extern void ns_image_set_size (void *img, int width, int height);
|
||||
extern void ns_image_set_transform (void *img, double m[3][3]);
|
||||
extern unsigned long ns_get_pixel (void *img, int x, int y);
|
||||
extern void ns_put_pixel (void *img, int x, int y, unsigned long argb);
|
||||
extern void ns_set_alpha (void *img, int x, int y, unsigned char a);
|
||||
|
|
41
src/nsterm.m
41
src/nsterm.m
|
@ -3813,21 +3813,34 @@ Function modeled after x_draw_glyph_string_box ().
|
|||
/* Draw the image... do we need to draw placeholder if img == nil? */
|
||||
if (img != nil)
|
||||
{
|
||||
#ifdef NS_IMPL_COCOA
|
||||
/* The idea here is that the clipped area is set in the normal
|
||||
view coordinate system, then we transform the coordinate
|
||||
system so that when we draw the image it is rotated, resized
|
||||
or whatever as required. This is kind of backwards, but
|
||||
there's no way to apply the transform to the image without
|
||||
creating a whole new bitmap. */
|
||||
NSRect dr = NSMakeRect (x, y, s->slice.width, s->slice.height);
|
||||
NSRect ir = NSMakeRect (s->slice.x,
|
||||
s->img->height - s->slice.y - s->slice.height,
|
||||
s->slice.width, s->slice.height);
|
||||
[img drawInRect: dr
|
||||
fromRect: ir
|
||||
operation: NSCompositingOperationSourceOver
|
||||
fraction: 1.0
|
||||
respectFlipped: YES
|
||||
hints: nil];
|
||||
#else
|
||||
[img compositeToPoint: NSMakePoint (x, y + s->slice.height)
|
||||
operation: NSCompositingOperationSourceOver];
|
||||
#endif
|
||||
NSRect ir = NSMakeRect (0, 0, [img size].width, [img size].height);
|
||||
|
||||
NSAffineTransform *setOrigin = [NSAffineTransform transform];
|
||||
|
||||
[[NSGraphicsContext currentContext] saveGraphicsState];
|
||||
|
||||
/* Because of the transforms it's far too difficult to work out
|
||||
what portion of the original, untransformed, image will be
|
||||
drawn, so the clipping area will ensure we draw only the
|
||||
correct bit. */
|
||||
NSRectClip (dr);
|
||||
|
||||
[setOrigin translateXBy:x - s->slice.x yBy:y - s->slice.y];
|
||||
[setOrigin concat];
|
||||
[img->transform concat];
|
||||
|
||||
[img drawInRect:ir fromRect:ir
|
||||
operation:NSCompositingOperationSourceOver
|
||||
fraction:1.0 respectFlipped:YES hints:nil];
|
||||
|
||||
[[NSGraphicsContext currentContext] restoreGraphicsState];
|
||||
}
|
||||
|
||||
if (s->hl == DRAW_CURSOR)
|
||||
|
|
|
@ -2883,10 +2883,7 @@ x_composite_image (struct glyph_string *s, Pixmap dest,
|
|||
destination = XRenderCreatePicture (display, dest,
|
||||
default_format, 0, &attr);
|
||||
|
||||
/* FIXME: It may make sense to use PictOpSrc instead of
|
||||
PictOpOver, as I don't know if we care about alpha values too
|
||||
much here. */
|
||||
XRenderComposite (display, PictOpOver,
|
||||
XRenderComposite (display, PictOpSrc,
|
||||
s->img->picture, s->img->mask_picture, destination,
|
||||
srcX, srcY,
|
||||
srcX, srcY,
|
||||
|
|
Loading…
Add table
Reference in a new issue