Teach Emacs on MS-Windows how to export frame screenshots
* src/w32image.c (w32_gdip_export_frame): New function. (gdiplus_init): Load 'GdipCreateBitmapFromHBITMAP' from GDI+ DLL. * src/w32fns.c (Fw32_export_frame): New primitive. * etc/NEWS: Announce the new primitive.
This commit is contained in:
5
etc/NEWS
5
etc/NEWS
@@ -4439,6 +4439,11 @@ other free systems.
|
||||
Transformed images are smoothed using the bilinear interpolation by
|
||||
means of the GDI+ library.
|
||||
|
||||
---
|
||||
** Emacs on MS-Windows is now capable of exporting frame screenshots to files.
|
||||
The new primitive 'w32-export-frame' can be used to export a screenshot
|
||||
of a specified frame to an image file in one of the supported image
|
||||
formats, such as JPEG or PNG.
|
||||
---
|
||||
** Emacs on MS-Windows now supports the ':data' keyword for 'play-sound'.
|
||||
In addition to ':file FILE' for playing a sound from a file, ':data
|
||||
|
||||
44
src/w32fns.c
44
src/w32fns.c
@@ -91,6 +91,9 @@ typedef enum _WTS_VIRTUAL_CLASS {
|
||||
#include <dlgs.h>
|
||||
#include <imm.h>
|
||||
#include <windowsx.h>
|
||||
#include <gdiplus.h>
|
||||
|
||||
#include "w32gdiplus.h"
|
||||
|
||||
/*
|
||||
Internal/undocumented constants for Windows Dark mode.
|
||||
@@ -11453,6 +11456,46 @@ w32_register_for_sleep_notifications (void)
|
||||
RegisterSuspendResumeNotification_fn (¶ms, 2);
|
||||
}
|
||||
}
|
||||
/***********************************************************************
|
||||
Exporting frames
|
||||
***********************************************************************/
|
||||
DEFUN ("w32-export-frame", Fw32_export_frame, Sw32_export_frame, 2, 3, 0,
|
||||
doc: /* Export screenshot of FRAME to FILE as image of TYPE format.
|
||||
If FRAME is nil, it defaults to the selected frame.
|
||||
FRAME must be a visible GUI frame; if not, this function signals an error.
|
||||
Optional arg TYPE should be either `jpeg' (default), `bmp', `png',
|
||||
`gif', or `tiff'.
|
||||
|
||||
Value is non-nil if FRAME was successfully exported, nil otherwise. */)
|
||||
(Lisp_Object frame, Lisp_Object file, Lisp_Object type)
|
||||
{
|
||||
struct frame *f = decode_live_frame (frame);
|
||||
|
||||
if (NILP (type))
|
||||
type = Qjpeg;
|
||||
|
||||
if (!FRAME_VISIBLE_P (f))
|
||||
error ("Frame to be exported must be visible");
|
||||
else if (!FRAME_WINDOW_P (f))
|
||||
error ("Frame to be exported must be a GUI frame");
|
||||
|
||||
/* Make sure the current matrices are up-to-date. */
|
||||
redisplay_preserve_echo_area (32);
|
||||
|
||||
HWND frame_hwnd = FRAME_W32_WINDOW (f);
|
||||
int result = -1;
|
||||
CLSID image_type_clsid;
|
||||
block_input ();
|
||||
if (w32_gdiplus_startup ()
|
||||
&& frame_hwnd != NULL
|
||||
&& w32_gdip_get_encoder_clsid (SSDATA (SYMBOL_NAME (type)),
|
||||
&image_type_clsid) >= 0)
|
||||
result = w32_gdip_export_frame (frame_hwnd, file, &image_type_clsid);
|
||||
unblock_input ();
|
||||
|
||||
return result >= 0 ? Qt : Qnil;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
Initialization
|
||||
@@ -11938,6 +11981,7 @@ keys when IME input is received. */);
|
||||
defsubr (&Sw32_set_wallpaper);
|
||||
defsubr (&Sw32_system_idle_time);
|
||||
#endif
|
||||
defsubr (&Sw32_export_frame);
|
||||
|
||||
DEFSYM (Qnot_useful, "not-useful");
|
||||
DEFSYM (Qpseudo_color, "pseudo-color");
|
||||
|
||||
@@ -50,6 +50,8 @@ typedef GpStatus (WINGDIPAPI *GdipSaveImageToFile_Proc)
|
||||
GDIPCONST EncoderParameters *);
|
||||
typedef GpStatus (WINGDIPAPI *GdipImageRotateFlip_Proc)
|
||||
(GpImage *image, RotateFlipType rfType);
|
||||
typedef GpStatus (WINGDIPAPI *GdipCreateBitmapFromHBITMAP_Proc)
|
||||
(HBITMAP, HPALETTE, GpBitmap **);
|
||||
|
||||
extern GdiplusStartup_Proc fn_GdiplusStartup;
|
||||
extern GdiplusShutdown_Proc fn_GdiplusShutdown;
|
||||
@@ -78,6 +80,7 @@ extern GdipLoadImageFromFile_Proc fn_GdipLoadImageFromFile;
|
||||
extern GdipGetImageThumbnail_Proc fn_GdipGetImageThumbnail;
|
||||
extern GdipSaveImageToFile_Proc fn_GdipSaveImageToFile;
|
||||
extern GdipImageRotateFlip_Proc fn_GdipImageRotateFlip;
|
||||
extern GdipCreateBitmapFromHBITMAP_Proc fn_GdipCreateBitmapFromHBITMAP;
|
||||
|
||||
# undef GdiplusStartup
|
||||
# undef GdiplusShutdown
|
||||
@@ -106,6 +109,7 @@ extern GdipImageRotateFlip_Proc fn_GdipImageRotateFlip;
|
||||
# undef GdipGetImageThumbnail
|
||||
# undef GdipSaveImageToFile
|
||||
# undef GdipSaveImageRotateFlip
|
||||
# undef GdipCreateBitmapFromHBITMAP
|
||||
|
||||
# define GdiplusStartup fn_GdiplusStartup
|
||||
# define GdiplusShutdown fn_GdiplusShutdown
|
||||
@@ -134,6 +138,8 @@ extern GdipImageRotateFlip_Proc fn_GdipImageRotateFlip;
|
||||
# define GdipGetImageThumbnail fn_GdipGetImageThumbnail
|
||||
# define GdipSaveImageToFile fn_GdipSaveImageToFile
|
||||
# define GdipImageRotateFlip fn_GdipImageRotateFlip
|
||||
# define GdipCreateBitmapFromHBITMAP fn_GdipCreateBitmapFromHBITMAP
|
||||
#endif
|
||||
|
||||
int w32_gdip_get_encoder_clsid (const char *type, CLSID *clsid);
|
||||
extern int w32_gdip_get_encoder_clsid (const char *, CLSID *);
|
||||
extern int w32_gdip_export_frame (HWND, Lisp_Object, CLSID *);
|
||||
|
||||
@@ -67,6 +67,7 @@ GdipLoadImageFromFile_Proc fn_GdipLoadImageFromFile;
|
||||
GdipGetImageThumbnail_Proc fn_GdipGetImageThumbnail;
|
||||
GdipSaveImageToFile_Proc fn_GdipSaveImageToFile;
|
||||
GdipImageRotateFlip_Proc fn_GdipImageRotateFlip;
|
||||
GdipCreateBitmapFromHBITMAP_Proc fn_GdipCreateBitmapFromHBITMAP;
|
||||
|
||||
static bool
|
||||
gdiplus_init (void)
|
||||
@@ -195,6 +196,10 @@ gdiplus_init (void)
|
||||
get_proc_addr (gdiplus_lib, "GdipImageRotateFlip");
|
||||
if (!fn_GdipImageRotateFlip)
|
||||
return false;
|
||||
fn_GdipCreateBitmapFromHBITMAP = (GdipCreateBitmapFromHBITMAP_Proc)
|
||||
get_proc_addr (gdiplus_lib, "GdipCreateBitmapFromHBITMAP");
|
||||
if (!fn_GdipCreateBitmapFromHBITMAP)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -560,6 +565,77 @@ w32_gdip_get_encoder_clsid (const char *type, CLSID *clsid)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
w32_gdip_export_frame (HWND hwnd, Lisp_Object file, CLSID *clsid)
|
||||
{
|
||||
RECT frame_rect;
|
||||
int frame_width, frame_height;
|
||||
HDC hdc, capture_hdc;
|
||||
int bits_per_pixel;
|
||||
|
||||
/* FIXME (maybe): GetWindowRect returns the rectangle that includes
|
||||
the "resize borders", which are by default invisible on Windows 10
|
||||
and later. It is unclear whether we should strive to exclude those
|
||||
resize borders: it is not trivial, and OTOH these borders can be
|
||||
made visible, in which case users might want them in the image.
|
||||
The downside is that the image has some of the desktop around the
|
||||
frame included in it. So what? */
|
||||
GetWindowRect (hwnd, &frame_rect);
|
||||
frame_width = frame_rect.right - frame_rect.left;
|
||||
frame_height = frame_rect.bottom - frame_rect.top;
|
||||
hdc = GetWindowDC (hwnd);
|
||||
bits_per_pixel = GetDeviceCaps (hdc, BITSPIXEL);
|
||||
capture_hdc = CreateCompatibleDC (hdc);
|
||||
|
||||
/* Capture the image. */
|
||||
BITMAPINFO capture_bmi;
|
||||
memset (&capture_bmi, 0, sizeof (capture_bmi));
|
||||
BITMAPINFOHEADER *header = &capture_bmi.bmiHeader;
|
||||
header->biSize = sizeof (BITMAPINFOHEADER);
|
||||
header->biWidth = frame_width;
|
||||
header->biHeight = -frame_height;
|
||||
header->biPlanes = 1;
|
||||
header->biBitCount = bits_per_pixel;
|
||||
header->biCompression = BI_RGB;
|
||||
DWORD *dib_bits;
|
||||
HBITMAP capture_bitmap = CreateDIBSection (hdc, &capture_bmi,
|
||||
DIB_PAL_COLORS,
|
||||
(void **)&dib_bits, NULL, 0);
|
||||
if (capture_bitmap)
|
||||
{
|
||||
SaveDC (capture_hdc);
|
||||
SelectObject (capture_hdc, capture_bitmap);
|
||||
BitBlt (capture_hdc, 0, 0, frame_width, frame_height, hdc, 0, 0, SRCCOPY);
|
||||
RestoreDC (capture_hdc, -1);
|
||||
}
|
||||
DeleteDC (capture_hdc);
|
||||
DeleteDC (hdc);
|
||||
|
||||
/* Save the captured image to file in requested format. */
|
||||
GpBitmap *bitmap;
|
||||
if (capture_bitmap)
|
||||
{
|
||||
GpStatus status = GdipCreateBitmapFromHBITMAP (capture_bitmap, NULL,
|
||||
&bitmap);
|
||||
if (status == Ok)
|
||||
{
|
||||
wchar_t file_w[MAX_PATH];
|
||||
file = ENCODE_FILE (Fexpand_file_name (Fcopy_sequence (file),
|
||||
Qnil));
|
||||
unixtodos_filename (SSDATA (file));
|
||||
filename_to_utf16 (SSDATA (file), file_w);
|
||||
status = GdipSaveImageToFile (bitmap, file_w, clsid, NULL);
|
||||
}
|
||||
GdipDisposeImage (bitmap);
|
||||
DeleteObject (capture_bitmap);
|
||||
|
||||
if (status == Ok)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEFUN ("w32image-create-thumbnail", Fw32image_create_thumbnail,
|
||||
Sw32image_create_thumbnail, 5, 5, 0,
|
||||
doc: /* Create a HEIGHT by WIDTH thumbnail file THUMB-FILE for image INPUT-FILE.
|
||||
|
||||
Reference in New Issue
Block a user