Support :transform-smoothing on images (MS-Windows) (bug#57166)

* src/dispextern.h (struct image): Add field 'smoothing' for
NTGUI.
* src/image.c (image_set_transform): Assign the 'smoothing'
field of the image struct.
* src/w32gdiplus.h: Add references to more GDI+ functions.
* src/w32image.c (gdiplus_init): Add references to more GDI+
functions.
* src/w32term.c (w32_draw_image_foreground): If the image is
marked for smoothing and GDI+ is available, draw it with GDI+
bilinear interpolation.
* etc/NEWS: New entry for this change.
This commit is contained in:
Cecilio Pardo 2024-11-04 18:58:40 +01:00 committed by Eli Zaretskii
parent 8a7910fb67
commit 4e8bf2977e
6 changed files with 112 additions and 14 deletions

View file

@ -838,6 +838,9 @@ current buffer, if the major mode supports it. (Support for
'yank-media' will be unavailable on MS-Windows if Emacs was configured
'--without-native-image-api'.)
---
** Images on MS-Windows now support the :transform-smoothing flag.
Bilinear interpolation with GDI+ is used to smooth images.
----------------------------------------------------------------------

View file

@ -3172,6 +3172,7 @@ struct image
#endif /* HAVE_ANDROID */
#ifdef HAVE_NTGUI
XFORM xform;
bool smoothing;
#endif
#ifdef HAVE_HAIKU
/* The affine transformation to apply to this image. */

View file

@ -3049,12 +3049,10 @@ image_set_transform (struct frame *f, struct image *img)
flip = !NILP (image_spec_value (img->spec, QCflip, NULL));
# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS || defined HAVE_HAIKU \
|| defined HAVE_ANDROID
|| defined HAVE_ANDROID || defined HAVE_NTGUI
/* We want scale up operations to use a nearest neighbor filter to
show real pixels instead of munging them, but scale down
operations to use a blended filter, to avoid aliasing and the like.
TODO: implement for Windows. */
operations to use a blended filter, to avoid aliasing and the like. */
bool smoothing;
Lisp_Object s = image_spec_value (img->spec, QCtransform_smoothing, NULL);
if (NILP (s))
@ -3067,6 +3065,10 @@ image_set_transform (struct frame *f, struct image *img)
img->use_bilinear_filtering = smoothing;
#endif
#ifdef HAVE_NTGUI
img->smoothing = smoothing;
#endif
/* Perform scale transformation. */
matrix3x3 matrix

View file

@ -2,6 +2,9 @@
typedef GpStatus (WINGDIPAPI *GdiplusStartup_Proc)
(ULONG_PTR *, GdiplusStartupInput *, GdiplusStartupOutput *);
typedef VOID (WINGDIPAPI *GdiplusShutdown_Proc) (ULONG_PTR);
typedef GpStatus (WINGDIPAPI *GdipCreateFromHDC_Proc)
(HDC hdc, GpGraphics **graphics);
typedef GpStatus (WINGDIPAPI *GdipDeleteGraphics_Proc) (GpGraphics *graphics);
typedef GpStatus (WINGDIPAPI *GdipGetPropertyItemSize_Proc)
(GpImage *, PROPID, UINT *);
typedef GpStatus (WINGDIPAPI *GdipGetPropertyItem_Proc)
@ -20,6 +23,15 @@ typedef GpStatus (WINGDIPAPI *GdipCreateBitmapFromStream_Proc)
(IStream *, GpBitmap **);
typedef GpStatus (WINGDIPAPI *GdipCreateBitmapFromScan0_Proc)
(INT, INT, INT, PixelFormat, BYTE*, GpBitmap**);
typedef GpStatus (WINGDIPAPI *GdipCreateBitmapFromHBITMAP_Proc)
(HBITMAP hbm, HPALETTE hpal, GpBitmap** bitmap);
typedef GpStatus (WINGDIPAPI *GdipSetInterpolationMode_Proc)
(GpGraphics *graphics, InterpolationMode interpolationMode);
typedef GpStatus (WINGDIPAPI *GdipDrawImageRectRectI_Proc)
(GpGraphics *graphics, GpImage *image, INT dstx, INT dsty, INT dstwidth,
INT dstheight, INT srcx, INT srcy, INT srcwidth, INT srcheight,
GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
DrawImageAbort callback, VOID * callbackData);
typedef IStream * (WINAPI *SHCreateMemStream_Proc) (const BYTE *, UINT);
typedef GpStatus (WINGDIPAPI *GdipCreateHBITMAPFromBitmap_Proc)
(GpBitmap *, HBITMAP *, ARGB);
@ -41,6 +53,8 @@ typedef GpStatus (WINGDIPAPI *GdipImageRotateFlip_Proc)
extern GdiplusStartup_Proc fn_GdiplusStartup;
extern GdiplusShutdown_Proc fn_GdiplusShutdown;
extern GdipCreateFromHDC_Proc fn_GdipCreateFromHDC;
extern GdipDeleteGraphics_Proc fn_GdipDeleteGraphics;
extern GdipGetPropertyItemSize_Proc fn_GdipGetPropertyItemSize;
extern GdipGetPropertyItem_Proc fn_GdipGetPropertyItem;
extern GdipImageGetFrameDimensionsCount_Proc fn_GdipImageGetFrameDimensionsCount;
@ -49,6 +63,9 @@ extern GdipImageGetFrameCount_Proc fn_GdipImageGetFrameCount;
extern GdipImageSelectActiveFrame_Proc fn_GdipImageSelectActiveFrame;
extern GdipCreateBitmapFromFile_Proc fn_GdipCreateBitmapFromFile;
extern GdipCreateBitmapFromStream_Proc fn_GdipCreateBitmapFromStream;
extern GdipCreateBitmapFromHBITMAP_Proc fn_GdipCreateBitmapFromHBITMAP;
extern GdipDrawImageRectRectI_Proc fn_GdipDrawImageRectRectI;
extern GdipSetInterpolationMode_Proc fn_GdipSetInterpolationMode;
extern GdipCreateBitmapFromScan0_Proc fn_GdipCreateBitmapFromScan0;
extern SHCreateMemStream_Proc fn_SHCreateMemStream;
extern GdipCreateHBITMAPFromBitmap_Proc fn_GdipCreateHBITMAPFromBitmap;
@ -73,6 +90,11 @@ extern GdipImageRotateFlip_Proc fn_GdipImageRotateFlip;
# undef GdipCreateBitmapFromFile
# undef GdipCreateBitmapFromStream
# undef GdipCreateBitmapFromScan0
# undef GdipCreateBitmapFromHBITMAP
# undef GdipCreateFromHDC
# undef GdipDrawImageRectRectI
# undef GdipSetInterpolationMode
# undef GdipDeleteGraphics
# undef SHCreateMemStream
# undef GdipCreateHBITMAPFromBitmap
# undef GdipDisposeImage
@ -96,6 +118,11 @@ extern GdipImageRotateFlip_Proc fn_GdipImageRotateFlip;
# define GdipCreateBitmapFromFile fn_GdipCreateBitmapFromFile
# define GdipCreateBitmapFromStream fn_GdipCreateBitmapFromStream
# define GdipCreateBitmapFromScan0 fn_GdipCreateBitmapFromScan0
# define GdipCreateBitmapFromHBITMAP fn_GdipCreateBitmapFromHBITMAP
# define GdipCreateFromHDC fn_GdipCreateFromHDC
# define GdipDrawImageRectRectI fn_GdipDrawImageRectRectI
# define GdipSetInterpolationMode fn_GdipSetInterpolationMode
# define GdipDeleteGraphics fn_GdipDeleteGraphics
# define SHCreateMemStream fn_SHCreateMemStream
# define GdipCreateHBITMAPFromBitmap fn_GdipCreateHBITMAPFromBitmap
# define GdipDisposeImage fn_GdipDisposeImage

View file

@ -42,6 +42,8 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#ifdef WINDOWSNT
GdiplusStartup_Proc fn_GdiplusStartup;
GdiplusShutdown_Proc fn_GdiplusShutdown;
GdipCreateFromHDC_Proc fn_GdipCreateFromHDC;
GdipDeleteGraphics_Proc fn_GdipDeleteGraphics;
GdipGetPropertyItemSize_Proc fn_GdipGetPropertyItemSize;
GdipGetPropertyItem_Proc fn_GdipGetPropertyItem;
GdipImageGetFrameDimensionsCount_Proc fn_GdipImageGetFrameDimensionsCount;
@ -53,6 +55,9 @@ GdipCreateBitmapFromStream_Proc fn_GdipCreateBitmapFromStream;
GdipCreateBitmapFromScan0_Proc fn_GdipCreateBitmapFromScan0;
SHCreateMemStream_Proc fn_SHCreateMemStream;
GdipCreateHBITMAPFromBitmap_Proc fn_GdipCreateHBITMAPFromBitmap;
GdipCreateBitmapFromHBITMAP_Proc fn_GdipCreateBitmapFromHBITMAP;
GdipDrawImageRectRectI_Proc fn_GdipDrawImageRectRectI;
GdipSetInterpolationMode_Proc fn_GdipSetInterpolationMode;
GdipDisposeImage_Proc fn_GdipDisposeImage;
GdipGetImageHeight_Proc fn_GdipGetImageHeight;
GdipGetImageWidth_Proc fn_GdipGetImageWidth;
@ -80,6 +85,14 @@ gdiplus_init (void)
get_proc_addr (gdiplus_lib, "GdiplusShutdown");
if (!fn_GdiplusShutdown)
return false;
fn_GdipCreateFromHDC = (GdipCreateFromHDC_Proc)
get_proc_addr (gdiplus_lib, "GdipCreateFromHDC");
if (!fn_GdipCreateFromHDC)
return false;
fn_GdipDeleteGraphics = (GdipDeleteGraphics_Proc)
get_proc_addr (gdiplus_lib, "GdipDeleteGraphics");
if (!fn_GdipDeleteGraphics)
return false;
fn_GdipGetPropertyItemSize = (GdipGetPropertyItemSize_Proc)
get_proc_addr (gdiplus_lib, "GdipGetPropertyItemSize");
if (!fn_GdipGetPropertyItemSize)
@ -120,6 +133,18 @@ gdiplus_init (void)
get_proc_addr (gdiplus_lib, "GdipCreateHBITMAPFromBitmap");
if (!fn_GdipCreateHBITMAPFromBitmap)
return false;
fn_GdipCreateBitmapFromHBITMAP = (GdipCreateBitmapFromHBITMAP_Proc)
get_proc_addr (gdiplus_lib, "GdipCreateBitmapFromHBITMAP");
if (!fn_GdipCreateBitmapFromHBITMAP)
return false;
fn_GdipDrawImageRectRectI = (GdipDrawImageRectRectI_Proc)
get_proc_addr (gdiplus_lib, "GdipDrawImageRectRectI");
if (!fn_GdipDrawImageRectRectI)
return false;
fn_GdipSetInterpolationMode = (GdipSetInterpolationMode_Proc)
get_proc_addr (gdiplus_lib, "GdipSetInterpolationMode");
if (!fn_GdipSetInterpolationMode)
return false;
fn_GdipDisposeImage = (GdipDisposeImage_Proc)
get_proc_addr (gdiplus_lib, "GdipDisposeImage");
if (!fn_GdipDisposeImage)

View file

@ -24,6 +24,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "blockinput.h"
#include "w32term.h"
#include "w32common.h" /* for OS version info */
#include <wtypes.h>
#include <gdiplus.h>
#include "w32gdiplus.h"
#include <ctype.h>
#include <errno.h>
@ -2106,16 +2109,53 @@ w32_draw_image_foreground (struct glyph_string *s)
compat_hdc, s->slice.x, s->slice.y, SRCCOPY);
else
{
int pmode = 0;
/* Windows 9X doesn't support HALFTONE. */
if (os_subtype == OS_SUBTYPE_NT
&& (pmode = SetStretchBltMode (s->hdc, HALFTONE)) != 0)
SetBrushOrgEx (s->hdc, 0, 0, NULL);
StretchBlt (s->hdc, x, y, s->slice.width, s->slice.height,
compat_hdc, orig_slice_x, orig_slice_y,
orig_slice_width, orig_slice_height, SRCCOPY);
if (pmode)
SetStretchBltMode (s->hdc, pmode);
#ifdef HAVE_NATIVE_IMAGE_API
if (s->img->smoothing && w32_gdiplus_startup ())
{
GpGraphics *graphics;
if (GdipCreateFromHDC (s->hdc, &graphics) == Ok)
{
GpBitmap *gp_bitmap;
/* Can't create a GpBitmap from a HBITMAP that was
ever selected into a DC, so we need to copy. */
HBITMAP copy
= CopyImage (GetCurrentObject (compat_hdc, OBJ_BITMAP),
IMAGE_BITMAP, 0, 0, 0);
if (GdipCreateBitmapFromHBITMAP (copy, NULL,
&gp_bitmap) == Ok)
{
GdipSetInterpolationMode (graphics,
InterpolationModeHighQualityBilinear);
GdipDrawImageRectRectI (graphics,
gp_bitmap, x, y,
s->slice.width,
s->slice.height,
orig_slice_x,
orig_slice_y,
orig_slice_width,
orig_slice_height,
UnitPixel,
NULL, NULL, NULL);
GdipDisposeImage (gp_bitmap);
}
DeleteObject (copy);
GdipDeleteGraphics (graphics);
}
}
else
#endif
{
int pmode = 0;
/* Windows 9X doesn't support HALFTONE. */
if (os_subtype == OS_SUBTYPE_NT
&& (pmode = SetStretchBltMode (s->hdc, HALFTONE)) != 0)
SetBrushOrgEx (s->hdc, 0, 0, NULL);
StretchBlt (s->hdc, x, y, s->slice.width, s->slice.height,
compat_hdc, orig_slice_x, orig_slice_y,
orig_slice_width, orig_slice_height, SRCCOPY);
if (pmode)
SetStretchBltMode (s->hdc, pmode);
}
}
/* When the image has a mask, we can expect that at