diff options
| author | Alan Third | 2019-11-09 17:04:25 +0000 |
|---|---|---|
| committer | Alan Third | 2019-11-29 21:22:26 +0000 |
| commit | 3e9c82d99902230812c10e8d464f4908a8a6ea42 (patch) | |
| tree | c5698e1d0c7158a63af8572148d0592731dfe209 | |
| parent | b1a69505843c593a3a757a614dea16e2a7185579 (diff) | |
| download | emacs-3e9c82d99902230812c10e8d464f4908a8a6ea42.tar.gz emacs-3e9c82d99902230812c10e8d464f4908a8a6ea42.zip | |
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.
| -rw-r--r-- | src/image.c | 147 | ||||
| -rw-r--r-- | src/xterm.c | 11 |
2 files changed, 106 insertions, 52 deletions
diff --git a/src/image.c b/src/image.c index 870f008b14f..70d932f9edb 100644 --- a/src/image.c +++ b/src/image.c | |||
| @@ -2244,6 +2244,14 @@ image_set_transform (struct frame *f, struct image *img) | |||
| 2244 | XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, FilterBest, | 2244 | XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, FilterBest, |
| 2245 | 0, 0); | 2245 | 0, 0); |
| 2246 | XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat); | 2246 | XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat); |
| 2247 | |||
| 2248 | if (img->mask_picture) | ||
| 2249 | { | ||
| 2250 | XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->mask_picture, | ||
| 2251 | FilterBest, 0, 0); | ||
| 2252 | XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->mask_picture, | ||
| 2253 | &tmat); | ||
| 2254 | } | ||
| 2247 | } | 2255 | } |
| 2248 | # elif defined HAVE_NTGUI | 2256 | # elif defined HAVE_NTGUI |
| 2249 | /* Store the transform matrix for application at draw time. */ | 2257 | /* Store the transform matrix for application at draw time. */ |
| @@ -2313,10 +2321,6 @@ lookup_image (struct frame *f, Lisp_Object spec) | |||
| 2313 | Lisp_Object ascent, margin, relief, bg; | 2321 | Lisp_Object ascent, margin, relief, bg; |
| 2314 | int relief_bound; | 2322 | int relief_bound; |
| 2315 | 2323 | ||
| 2316 | #ifdef HAVE_NATIVE_TRANSFORMS | ||
| 2317 | image_set_transform (f, img); | ||
| 2318 | #endif | ||
| 2319 | |||
| 2320 | ascent = image_spec_value (spec, QCascent, NULL); | 2324 | ascent = image_spec_value (spec, QCascent, NULL); |
| 2321 | if (FIXNUMP (ascent)) | 2325 | if (FIXNUMP (ascent)) |
| 2322 | img->ascent = XFIXNUM (ascent); | 2326 | img->ascent = XFIXNUM (ascent); |
| @@ -2357,6 +2361,13 @@ lookup_image (struct frame *f, Lisp_Object spec) | |||
| 2357 | don't have the image yet. */ | 2361 | don't have the image yet. */ |
| 2358 | if (!EQ (builtin_lisp_symbol (img->type->type), Qpostscript)) | 2362 | if (!EQ (builtin_lisp_symbol (img->type->type), Qpostscript)) |
| 2359 | postprocess_image (f, img); | 2363 | postprocess_image (f, img); |
| 2364 | |||
| 2365 | /* postprocess_image above may modify the image or the mask, | ||
| 2366 | relying on the image's real width and height, so | ||
| 2367 | image_set_transform must be called after it. */ | ||
| 2368 | #ifdef HAVE_NATIVE_TRANSFORMS | ||
| 2369 | image_set_transform (f, img); | ||
| 2370 | #endif | ||
| 2360 | } | 2371 | } |
| 2361 | 2372 | ||
| 2362 | unblock_input (); | 2373 | unblock_input (); |
| @@ -2527,6 +2538,61 @@ x_destroy_x_image (XImage *ximg) | |||
| 2527 | } | 2538 | } |
| 2528 | } | 2539 | } |
| 2529 | 2540 | ||
| 2541 | # if !defined USE_CAIRO && defined HAVE_XRENDER | ||
| 2542 | /* Create and return an XRender Picture for XRender transforms. */ | ||
| 2543 | static Picture | ||
| 2544 | x_create_xrender_picture (struct frame *f, Emacs_Pixmap pixmap, int depth) | ||
| 2545 | { | ||
| 2546 | Picture p; | ||
| 2547 | Display *display = FRAME_X_DISPLAY (f); | ||
| 2548 | int event_basep, error_basep; | ||
| 2549 | |||
| 2550 | if (XRenderQueryExtension (display, &event_basep, &error_basep)) | ||
| 2551 | { | ||
| 2552 | if (depth <= 0) | ||
| 2553 | depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f)); | ||
| 2554 | if (depth == 32 || depth == 24 || depth == 8 || depth == 4 || depth == 1) | ||
| 2555 | { | ||
| 2556 | /* FIXME: Do we need to handle all possible bit depths? | ||
| 2557 | XRenderFindStandardFormat supports PictStandardARGB32, | ||
| 2558 | PictStandardRGB24, PictStandardA8, PictStandardA4, | ||
| 2559 | PictStandardA1, and PictStandardNUM (what is this?!). | ||
| 2560 | |||
| 2561 | XRenderFindFormat may support more, but I don't | ||
| 2562 | understand the documentation. */ | ||
| 2563 | XRenderPictFormat *format; | ||
| 2564 | format = XRenderFindStandardFormat (display, | ||
| 2565 | depth == 32 ? PictStandardARGB32 | ||
| 2566 | : depth == 24 ? PictStandardRGB24 | ||
| 2567 | : depth == 8 ? PictStandardA8 | ||
| 2568 | : depth == 4 ? PictStandardA4 | ||
| 2569 | : PictStandardA1); | ||
| 2570 | |||
| 2571 | /* Set the Picture repeat to "pad". This means when | ||
| 2572 | operations look at pixels outside the image area they | ||
| 2573 | will use the value of the nearest real pixel instead of | ||
| 2574 | using a transparent black pixel. */ | ||
| 2575 | XRenderPictureAttributes attr; | ||
| 2576 | unsigned long attr_mask = CPRepeat; | ||
| 2577 | attr.repeat = RepeatPad; | ||
| 2578 | |||
| 2579 | p = XRenderCreatePicture (display, pixmap, format, attr_mask, &attr); | ||
| 2580 | } | ||
| 2581 | else | ||
| 2582 | { | ||
| 2583 | image_error ("Specified image bit depth is not supported by XRender"); | ||
| 2584 | return 0; | ||
| 2585 | } | ||
| 2586 | } | ||
| 2587 | else | ||
| 2588 | { | ||
| 2589 | /* XRender not supported on this display. */ | ||
| 2590 | return 0; | ||
| 2591 | } | ||
| 2592 | |||
| 2593 | return p; | ||
| 2594 | } | ||
| 2595 | # endif /* !defined USE_CAIRO && defined HAVE_XRENDER */ | ||
| 2530 | #endif /* HAVE_X_WINDOWS */ | 2596 | #endif /* HAVE_X_WINDOWS */ |
| 2531 | 2597 | ||
| 2532 | /* Return true if XIMG's size WIDTH x HEIGHT doesn't break the | 2598 | /* 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 | |||
| 2579 | if (!x_create_x_image_and_pixmap (f, width, height, depth, pimg, pixmap)) | 2645 | if (!x_create_x_image_and_pixmap (f, width, height, depth, pimg, pixmap)) |
| 2580 | return 0; | 2646 | return 0; |
| 2581 | # ifdef HAVE_XRENDER | 2647 | # ifdef HAVE_XRENDER |
| 2582 | Display *display = FRAME_X_DISPLAY (f); | 2648 | if (picture) |
| 2583 | int event_basep, error_basep; | 2649 | *picture = x_create_xrender_picture (f, *pixmap, depth); |
| 2584 | if (picture && XRenderQueryExtension (display, &event_basep, &error_basep)) | ||
| 2585 | { | ||
| 2586 | if (depth <= 0) | ||
| 2587 | depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f)); | ||
| 2588 | if (depth == 32 || depth == 24 || depth == 8) | ||
| 2589 | { | ||
| 2590 | XRenderPictFormat *format; | ||
| 2591 | XRenderPictureAttributes attr; | ||
| 2592 | |||
| 2593 | /* FIXME: Do we need to handle all possible bit depths? | ||
| 2594 | XRenderFindStandardFormat supports PictStandardARGB32, | ||
| 2595 | PictStandardRGB24, PictStandardA8, PictStandardA4, | ||
| 2596 | PictStandardA1, and PictStandardNUM (what is this?!). | ||
| 2597 | |||
| 2598 | XRenderFindFormat may support more, but I don't | ||
| 2599 | understand the documentation. */ | ||
| 2600 | format = XRenderFindStandardFormat (display, | ||
| 2601 | depth == 32 ? PictStandardARGB32 | ||
| 2602 | : depth == 24 ? PictStandardRGB24 | ||
| 2603 | : PictStandardA8); | ||
| 2604 | *picture = XRenderCreatePicture (display, *pixmap, format, 0, &attr); | ||
| 2605 | } | ||
| 2606 | else | ||
| 2607 | { | ||
| 2608 | image_error ("Specified image bit depth is not supported by XRender"); | ||
| 2609 | *picture = 0; | ||
| 2610 | } | ||
| 2611 | } | ||
| 2612 | # endif | 2650 | # endif |
| 2613 | 2651 | ||
| 2614 | return 1; | 2652 | return 1; |
| @@ -3387,6 +3425,11 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct image *img, char *data, | |||
| 3387 | img->width, img->height, | 3425 | img->width, img->height, |
| 3388 | fg, bg, | 3426 | fg, bg, |
| 3389 | DefaultDepthOfScreen (FRAME_X_SCREEN (f))); | 3427 | DefaultDepthOfScreen (FRAME_X_SCREEN (f))); |
| 3428 | # if !defined USE_CAIRO && defined HAVE_XRENDER | ||
| 3429 | if (img->pixmap) | ||
| 3430 | img->picture = x_create_xrender_picture (f, img->pixmap, 0); | ||
| 3431 | # endif | ||
| 3432 | |||
| 3390 | #elif defined HAVE_NTGUI | 3433 | #elif defined HAVE_NTGUI |
| 3391 | img->pixmap | 3434 | img->pixmap |
| 3392 | = w32_create_pixmap_from_bitmap_data (img->width, img->height, data); | 3435 | = w32_create_pixmap_from_bitmap_data (img->width, img->height, data); |
| @@ -4359,18 +4402,30 @@ xpm_load (struct frame *f, struct image *img) | |||
| 4359 | image_clear_image (f, img); | 4402 | image_clear_image (f, img); |
| 4360 | rc = XpmNoMemory; | 4403 | rc = XpmNoMemory; |
| 4361 | } | 4404 | } |
| 4362 | else if (img->mask_img) | 4405 | else |
| 4363 | { | 4406 | { |
| 4364 | img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), | 4407 | # if !defined USE_CAIRO && defined HAVE_XRENDER |
| 4365 | img->mask_img->width, | 4408 | img->picture = x_create_xrender_picture (f, img->pixmap, |
| 4366 | img->mask_img->height, | 4409 | img->ximg->depth); |
| 4367 | img->mask_img->depth); | 4410 | # endif |
| 4368 | if (img->mask == NO_PIXMAP) | 4411 | if (img->mask_img) |
| 4369 | { | 4412 | { |
| 4370 | image_clear_image (f, img); | 4413 | img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), |
| 4371 | rc = XpmNoMemory; | 4414 | img->mask_img->width, |
| 4372 | } | 4415 | img->mask_img->height, |
| 4373 | } | 4416 | img->mask_img->depth); |
| 4417 | if (img->mask == NO_PIXMAP) | ||
| 4418 | { | ||
| 4419 | image_clear_image (f, img); | ||
| 4420 | rc = XpmNoMemory; | ||
| 4421 | } | ||
| 4422 | # if !defined USE_CAIRO && defined HAVE_XRENDER | ||
| 4423 | else | ||
| 4424 | img->mask_picture = x_create_xrender_picture | ||
| 4425 | (f, img->mask, img->mask_img->depth); | ||
| 4426 | # endif | ||
| 4427 | } | ||
| 4428 | } | ||
| 4374 | } | 4429 | } |
| 4375 | #endif | 4430 | #endif |
| 4376 | 4431 | ||
diff --git a/src/xterm.c b/src/xterm.c index d55bc3890d6..9a6eda4488d 100644 --- a/src/xterm.c +++ b/src/xterm.c | |||
| @@ -3049,14 +3049,12 @@ x_composite_image (struct glyph_string *s, Pixmap dest, | |||
| 3049 | XRenderPictFormat *default_format; | 3049 | XRenderPictFormat *default_format; |
| 3050 | XRenderPictureAttributes attr; | 3050 | XRenderPictureAttributes attr; |
| 3051 | 3051 | ||
| 3052 | /* FIXME: Should we do this each time or would it make sense to | ||
| 3053 | store destination in the frame struct? */ | ||
| 3054 | default_format = XRenderFindVisualFormat (display, | 3052 | default_format = XRenderFindVisualFormat (display, |
| 3055 | DefaultVisual (display, 0)); | 3053 | DefaultVisual (display, 0)); |
| 3056 | destination = XRenderCreatePicture (display, dest, | 3054 | destination = XRenderCreatePicture (display, dest, |
| 3057 | default_format, 0, &attr); | 3055 | default_format, 0, &attr); |
| 3058 | 3056 | ||
| 3059 | XRenderComposite (display, PictOpSrc, | 3057 | XRenderComposite (display, s->img->mask_picture ? PictOpOver : PictOpSrc, |
| 3060 | s->img->picture, s->img->mask_picture, destination, | 3058 | s->img->picture, s->img->mask_picture, destination, |
| 3061 | srcX, srcY, | 3059 | srcX, srcY, |
| 3062 | srcX, srcY, | 3060 | srcX, srcY, |
| @@ -3315,6 +3313,7 @@ x_draw_image_foreground_1 (struct glyph_string *s, Pixmap pixmap) | |||
| 3315 | trust on the shape extension to be available | 3313 | trust on the shape extension to be available |
| 3316 | (XShapeCombineRegion). So, compute the rectangle to draw | 3314 | (XShapeCombineRegion). So, compute the rectangle to draw |
| 3317 | manually. */ | 3315 | manually. */ |
| 3316 | /* FIXME: Do we need to do this when using XRender compositing? */ | ||
| 3318 | unsigned long mask = (GCClipMask | GCClipXOrigin | GCClipYOrigin | 3317 | unsigned long mask = (GCClipMask | GCClipXOrigin | GCClipYOrigin |
| 3319 | | GCFunction); | 3318 | | GCFunction); |
| 3320 | XGCValues xgcv; | 3319 | XGCValues xgcv; |
| @@ -3325,9 +3324,9 @@ x_draw_image_foreground_1 (struct glyph_string *s, Pixmap pixmap) | |||
| 3325 | xgcv.function = GXcopy; | 3324 | xgcv.function = GXcopy; |
| 3326 | XChangeGC (display, s->gc, mask, &xgcv); | 3325 | XChangeGC (display, s->gc, mask, &xgcv); |
| 3327 | 3326 | ||
| 3328 | XCopyArea (display, s->img->pixmap, pixmap, s->gc, | 3327 | x_composite_image (s, pixmap, |
| 3329 | s->slice.x, s->slice.y, | 3328 | s->slice.x, s->slice.y, |
| 3330 | s->slice.width, s->slice.height, x, y); | 3329 | x, y, s->slice.width, s->slice.height); |
| 3331 | XSetClipMask (display, s->gc, None); | 3330 | XSetClipMask (display, s->gc, None); |
| 3332 | } | 3331 | } |
| 3333 | else | 3332 | else |