From 0fae31b5732f666eb12f2bf792208483d2319ff8 Mon Sep 17 00:00:00 2001 From: Cecilio Pardo Date: Mon, 4 Nov 2024 18:58:40 +0100 Subject: [PATCH] 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+ interpolation. --- src/dispextern.h | 1 + src/image.c | 10 +++++---- src/w32gdiplus.h | 27 ++++++++++++++++++++++ src/w32image.c | 25 +++++++++++++++++++++ src/w32term.c | 58 +++++++++++++++++++++++++++++++++++++++--------- 5 files changed, 107 insertions(+), 14 deletions(-) diff --git a/src/dispextern.h b/src/dispextern.h index cc248a4472e..004eb82d87a 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -3172,6 +3172,7 @@ reset_mouse_highlight (Mouse_HLInfo *hlinfo) #endif /* HAVE_ANDROID */ #ifdef HAVE_NTGUI XFORM xform; + bool smoothing; #endif #ifdef HAVE_HAIKU /* The affine transformation to apply to this image. */ diff --git a/src/image.c b/src/image.c index 34936977a40..db7f6acd171 100644 --- a/src/image.c +++ b/src/image.c @@ -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 diff --git a/src/w32gdiplus.h b/src/w32gdiplus.h index 9d05ae6c190..b438b1a64f8 100644 --- a/src/w32gdiplus.h +++ b/src/w32gdiplus.h @@ -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 @@ (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 @@ 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 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 @@ # 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 @@ # 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 diff --git a/src/w32image.c b/src/w32image.c index 44eed087528..da4d6843ba9 100644 --- a/src/w32image.c +++ b/src/w32image.c @@ -42,6 +42,8 @@ #define COBJMACROS #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 @@ #define COBJMACROS 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) diff --git a/src/w32term.c b/src/w32term.c index 88622700386..d3c3b9f590e 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -24,6 +24,9 @@ Copyright (C) 1989, 1993-2024 Free Software Foundation, Inc. #include "blockinput.h" #include "w32term.h" #include "w32common.h" /* for OS version info */ +#include +#include +#include "w32gdiplus.h" #include #include @@ -2106,16 +2109,51 @@ 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); + 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, + InterpolationModeHighQualityBicubic); + 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 + { + 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 -- 2.35.1.windows.2