Fix image scaling with masks (bug#38109)

* src/image.c (lookup_image): Move call to image_set_transform after
postprocess_image.
(image_create_x_image_and_pixmap_1): Use new function.
(image_set_transform): Apply the transform to the mask too.
(x_create_xrender_picture): New function.
(Create_Pixmap_From_Bitmap_Data):
(xpm_load): Use new function.
* src/xterm.c (x_composite_image): Use PictOpOver when there is a mask
so the transparency is honoured.
(x_draw_image_foreground_1): Use x_composite_image.
This commit is contained in:
Alan Third 2019-11-09 17:04:25 +00:00
parent b1a6950584
commit 3e9c82d999
2 changed files with 106 additions and 52 deletions

View file

@ -2244,6 +2244,14 @@ image_set_transform (struct frame *f, struct image *img)
XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, FilterBest,
0, 0);
XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat);
if (img->mask_picture)
{
XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->mask_picture,
FilterBest, 0, 0);
XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->mask_picture,
&tmat);
}
}
# elif defined HAVE_NTGUI
/* Store the transform matrix for application at draw time. */
@ -2313,10 +2321,6 @@ lookup_image (struct frame *f, Lisp_Object spec)
Lisp_Object ascent, margin, relief, bg;
int relief_bound;
#ifdef HAVE_NATIVE_TRANSFORMS
image_set_transform (f, img);
#endif
ascent = image_spec_value (spec, QCascent, NULL);
if (FIXNUMP (ascent))
img->ascent = XFIXNUM (ascent);
@ -2357,6 +2361,13 @@ lookup_image (struct frame *f, Lisp_Object spec)
don't have the image yet. */
if (!EQ (builtin_lisp_symbol (img->type->type), Qpostscript))
postprocess_image (f, img);
/* postprocess_image above may modify the image or the mask,
relying on the image's real width and height, so
image_set_transform must be called after it. */
#ifdef HAVE_NATIVE_TRANSFORMS
image_set_transform (f, img);
#endif
}
unblock_input ();
@ -2527,6 +2538,61 @@ x_destroy_x_image (XImage *ximg)
}
}
# if !defined USE_CAIRO && defined HAVE_XRENDER
/* Create and return an XRender Picture for XRender transforms. */
static Picture
x_create_xrender_picture (struct frame *f, Emacs_Pixmap pixmap, int depth)
{
Picture p;
Display *display = FRAME_X_DISPLAY (f);
int event_basep, error_basep;
if (XRenderQueryExtension (display, &event_basep, &error_basep))
{
if (depth <= 0)
depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f));
if (depth == 32 || depth == 24 || depth == 8 || depth == 4 || depth == 1)
{
/* FIXME: Do we need to handle all possible bit depths?
XRenderFindStandardFormat supports PictStandardARGB32,
PictStandardRGB24, PictStandardA8, PictStandardA4,
PictStandardA1, and PictStandardNUM (what is this?!).
XRenderFindFormat may support more, but I don't
understand the documentation. */
XRenderPictFormat *format;
format = XRenderFindStandardFormat (display,
depth == 32 ? PictStandardARGB32
: depth == 24 ? PictStandardRGB24
: depth == 8 ? PictStandardA8
: depth == 4 ? PictStandardA4
: PictStandardA1);
/* Set the Picture repeat to "pad". This means when
operations look at pixels outside the image area they
will use the value of the nearest real pixel instead of
using a transparent black pixel. */
XRenderPictureAttributes attr;
unsigned long attr_mask = CPRepeat;
attr.repeat = RepeatPad;
p = XRenderCreatePicture (display, pixmap, format, attr_mask, &attr);
}
else
{
image_error ("Specified image bit depth is not supported by XRender");
return 0;
}
}
else
{
/* XRender not supported on this display. */
return 0;
}
return p;
}
# endif /* !defined USE_CAIRO && defined HAVE_XRENDER */
#endif /* HAVE_X_WINDOWS */
/* Return true if XIMG's size WIDTH x HEIGHT doesn't break the
@ -2579,36 +2645,8 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d
if (!x_create_x_image_and_pixmap (f, width, height, depth, pimg, pixmap))
return 0;
# ifdef HAVE_XRENDER
Display *display = FRAME_X_DISPLAY (f);
int event_basep, error_basep;
if (picture && XRenderQueryExtension (display, &event_basep, &error_basep))
{
if (depth <= 0)
depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f));
if (depth == 32 || depth == 24 || depth == 8)
{
XRenderPictFormat *format;
XRenderPictureAttributes attr;
/* FIXME: Do we need to handle all possible bit depths?
XRenderFindStandardFormat supports PictStandardARGB32,
PictStandardRGB24, PictStandardA8, PictStandardA4,
PictStandardA1, and PictStandardNUM (what is this?!).
XRenderFindFormat may support more, but I don't
understand the documentation. */
format = XRenderFindStandardFormat (display,
depth == 32 ? PictStandardARGB32
: depth == 24 ? PictStandardRGB24
: PictStandardA8);
*picture = XRenderCreatePicture (display, *pixmap, format, 0, &attr);
}
else
{
image_error ("Specified image bit depth is not supported by XRender");
*picture = 0;
}
}
if (picture)
*picture = x_create_xrender_picture (f, *pixmap, depth);
# endif
return 1;
@ -3387,6 +3425,11 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct image *img, char *data,
img->width, img->height,
fg, bg,
DefaultDepthOfScreen (FRAME_X_SCREEN (f)));
# if !defined USE_CAIRO && defined HAVE_XRENDER
if (img->pixmap)
img->picture = x_create_xrender_picture (f, img->pixmap, 0);
# endif
#elif defined HAVE_NTGUI
img->pixmap
= w32_create_pixmap_from_bitmap_data (img->width, img->height, data);
@ -4359,18 +4402,30 @@ xpm_load (struct frame *f, struct image *img)
image_clear_image (f, img);
rc = XpmNoMemory;
}
else if (img->mask_img)
{
img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
img->mask_img->width,
img->mask_img->height,
img->mask_img->depth);
if (img->mask == NO_PIXMAP)
{
image_clear_image (f, img);
rc = XpmNoMemory;
}
}
else
{
# if !defined USE_CAIRO && defined HAVE_XRENDER
img->picture = x_create_xrender_picture (f, img->pixmap,
img->ximg->depth);
# endif
if (img->mask_img)
{
img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
img->mask_img->width,
img->mask_img->height,
img->mask_img->depth);
if (img->mask == NO_PIXMAP)
{
image_clear_image (f, img);
rc = XpmNoMemory;
}
# if !defined USE_CAIRO && defined HAVE_XRENDER
else
img->mask_picture = x_create_xrender_picture
(f, img->mask, img->mask_img->depth);
# endif
}
}
}
#endif

View file

@ -3049,14 +3049,12 @@ x_composite_image (struct glyph_string *s, Pixmap dest,
XRenderPictFormat *default_format;
XRenderPictureAttributes attr;
/* FIXME: Should we do this each time or would it make sense to
store destination in the frame struct? */
default_format = XRenderFindVisualFormat (display,
DefaultVisual (display, 0));
destination = XRenderCreatePicture (display, dest,
default_format, 0, &attr);
XRenderComposite (display, PictOpSrc,
XRenderComposite (display, s->img->mask_picture ? PictOpOver : PictOpSrc,
s->img->picture, s->img->mask_picture, destination,
srcX, srcY,
srcX, srcY,
@ -3315,6 +3313,7 @@ x_draw_image_foreground_1 (struct glyph_string *s, Pixmap pixmap)
trust on the shape extension to be available
(XShapeCombineRegion). So, compute the rectangle to draw
manually. */
/* FIXME: Do we need to do this when using XRender compositing? */
unsigned long mask = (GCClipMask | GCClipXOrigin | GCClipYOrigin
| GCFunction);
XGCValues xgcv;
@ -3325,9 +3324,9 @@ x_draw_image_foreground_1 (struct glyph_string *s, Pixmap pixmap)
xgcv.function = GXcopy;
XChangeGC (display, s->gc, mask, &xgcv);
XCopyArea (display, s->img->pixmap, pixmap, s->gc,
s->slice.x, s->slice.y,
s->slice.width, s->slice.height, x, y);
x_composite_image (s, pixmap,
s->slice.x, s->slice.y,
x, y, s->slice.width, s->slice.height);
XSetClipMask (display, s->gc, None);
}
else