]> git.eshelyaron.com Git - emacs.git/commitdiff
Support :transform-smoothing on images (MS-Windows) (bug#57166)
authorCecilio Pardo <cpardo@imayhem.com>
Mon, 4 Nov 2024 17:58:40 +0000 (18:58 +0100)
committerEshel Yaron <me@eshelyaron.com>
Fri, 8 Nov 2024 13:31:38 +0000 (14:31 +0100)
* 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.

(cherry picked from commit 4e8bf2977e6d1abf6d3cf82e9c1ae3dee5bfcda0)

etc/NEWS
src/dispextern.h
src/image.c
src/w32gdiplus.h
src/w32image.c
src/w32term.c

index 453f2545718dae2255f8d69d977e6c3ba356d245..8babeed4ea78378c377ca910dd19739da89d3c38 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -755,6 +755,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.
 
 \f
 ----------------------------------------------------------------------
index cc248a4472ecb324e233e6e977dda101398be5ee..004eb82d87a70b7539ae1adb70366bcbd2f32b3c 100644 (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.  */
index 34936977a40628ad48317e9021cde928fd2cf193..db7f6acd17146a8416603cbf2408d3beee18c8e8 100644 (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
index 9d05ae6c1908b036a9352cf2d4fd33ec9d0aeea5..b438b1a64f8c6308d6cd118b62fab990d93f9868 100644 (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
index 44eed08752884447056f3dce5e9866bcf7595fa6..da4d6843ba9e1fa605fa96eae7ba6ebc0aa940fb 100644 (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)
index 886227003866d5be48ab1721241aebfe511d7ee8..e18f39dd2a8bd53ffeb335925c85727279c05a20 100644 (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