From: Cecilio Pardo <cpardo@imayhem.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: 57166@debbugs.gnu.org
Subject: bug#57166: 28.1; image :transform-smoothing dose not work
Date: Tue, 5 Nov 2024 00:11:52 +0100 [thread overview]
Message-ID: <a178e138-2c1c-4a8b-8332-c05b5356db4c@imayhem.com> (raw)
In-Reply-To: <86ttcmu6go.fsf@gnu.org>
[-- Attachment #1: Type: text/plain, Size: 56 bytes --]
New patch with NEWS entry and bilinear interpolation.
[-- Attachment #2: 0001-Support-transform-smoothing-on-images-MS-Windows-bug.patch --]
[-- Type: text/plain, Size: 11764 bytes --]
From bbe02f4218b6ba0ffd5a8356978a35bf920d1727 Mon Sep 17 00:00:00 2001
From: Cecilio Pardo <cpardo@imayhem.com>
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+
bilinear interpolation.
* etc/NEWS: New entry for this change.
---
etc/NEWS | 3 +++
src/dispextern.h | 1 +
src/image.c | 10 ++++----
src/w32gdiplus.h | 27 ++++++++++++++++++++++
src/w32image.c | 25 ++++++++++++++++++++
src/w32term.c | 60 ++++++++++++++++++++++++++++++++++++++++--------
6 files changed, 112 insertions(+), 14 deletions(-)
diff --git a/etc/NEWS b/etc/NEWS
index 41a76d1cd95..33da804d63e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -830,6 +830,9 @@ color Emoji font installed on your system for the 'emoji' script.
This command inserts clipboard data of different formats into the
current buffer, if the major mode supports it.
+---
+** Images on MS-Windows now support the :transform-smoothing flag.
+Bilinear interpolation with GDI+ is used to smooth images.
\f
----------------------------------------------------------------------
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..e18f39dd2a8 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 <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
--
2.35.1.windows.2
next prev parent reply other threads:[~2024-11-04 23:11 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-08-13 6:06 bug#57166: 28.1; image :transform-smoothing dose not work awrhygty
2022-08-13 6:24 ` Eli Zaretskii
2024-11-04 19:19 ` Cecilio Pardo
2024-11-04 19:47 ` Eli Zaretskii
2024-11-04 20:07 ` Eli Zaretskii
2024-11-04 23:04 ` Cecilio Pardo
2024-11-04 23:03 ` Cecilio Pardo
2024-11-04 23:11 ` Cecilio Pardo [this message]
2024-11-05 12:41 ` Eli Zaretskii
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=a178e138-2c1c-4a8b-8332-c05b5356db4c@imayhem.com \
--to=cpardo@imayhem.com \
--cc=57166@debbugs.gnu.org \
--cc=eliz@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).