diff options
author | Eli Zaretskii <eliz@gnu.org> | 2020-04-14 18:10:41 +0300 |
---|---|---|
committer | Eli Zaretskii <eliz@gnu.org> | 2020-04-14 18:10:41 +0300 |
commit | e94206aaf608a899c81bb07fe91d26439f51b3f8 (patch) | |
tree | a4a24407f1ba3d70ae192a1aad954a2bed3e91e1 /src/w32image.c | |
parent | df254a7445a86dc25d133f2d79be8096190a8b96 (diff) | |
download | emacs-e94206aaf608a899c81bb07fe91d26439f51b3f8.tar.gz |
Make use of MS-Windows native image API be selectable at run time
* configure.ac: Minor cleanup in how w32image.o is added to the
build when native image APIs are requested.
* src/w32gui.h (w32_load_image, w32_can_use_native_image_api)
(w32_gdiplus_shutdown): Move prototypes from w32term.h here, since
w32.c doesn't include w32term.h.
* src/image.c (struct image_type): No need to pass TYPE to the
'valid_p' method. All callers changed.
(initialize_image_type) [HAVE_NATIVE_IMAGE_API]: Call
'image_can_use_native_api' before trying image-specific methods.
(image_can_use_native_api): New function.
(image_types): Remove the native_image_type parts.
(syms_of_image): New symbol 'native-image'.
(parse_image_spec): Accept native-image "type" for any image type.
* src/w32term.c (syms_of_w32term): New variable
'w32-use-native-image-API'.
* src/w32image.c: (w32_can_use_native_image_api): New function.
(gdiplus_init): Rename from w32_gdiplus_startup. Simplify code.
Move the call to GdiplusStartup to a separate function. Use
ordinal number for SHCreateMemStream if cannot load it by name.
(w32_load_image): Ignore Win32Error status from
w32_select_active_frame.
Move DEFSYMs from here...
* src/image.c (syms_of_image) [HAVE_NATIVE_IMAGE_API]: ...to here.
* etc/NEWS: Update the entry about native image API use.
Diffstat (limited to 'src/w32image.c')
-rw-r--r-- | src/w32image.c | 224 |
1 files changed, 143 insertions, 81 deletions
diff --git a/src/w32image.c b/src/w32image.c index fe32660f712..6a3e37ce0ee 100644 --- a/src/w32image.c +++ b/src/w32image.c @@ -23,7 +23,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include "lisp.h" #include "dispextern.h" #define COBJMACROS +#ifdef MINGW_W64 +/* FIXME: Do we need to include objidl.h? */ #include <objidl.h> +#endif #include <wtypes.h> #include <gdiplus.h> #include <shlwapi.h> @@ -32,53 +35,39 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include "frame.h" #include "coding.h" -/*#define LINK_GDIPLUS_STATICALLY 1*/ +#ifdef WINDOWSNT -#ifndef LINK_GDIPLUS_STATICALLY -DEF_DLL_FN (GpStatus, GdiplusStartup, (ULONG_PTR *, GdiplusStartupInput *, GdiplusStartupOutput *)); +DEF_DLL_FN (GpStatus, GdiplusStartup, + (ULONG_PTR *, GdiplusStartupInput *, GdiplusStartupOutput *)); DEF_DLL_FN (VOID, GdiplusShutdown, (ULONG_PTR)); -DEF_DLL_FN (GpStatus, GdipGetPropertyItemSize, (GpImage *, PROPID, UINT *)); -DEF_DLL_FN (GpStatus, GdipGetPropertyItem, (GpImage *, PROPID, UINT, PropertyItem *)); +DEF_DLL_FN (GpStatus, GdipGetPropertyItemSize, + (GpImage *, PROPID, UINT *)); +DEF_DLL_FN (GpStatus, GdipGetPropertyItem, + (GpImage *, PROPID, UINT, PropertyItem *)); DEF_DLL_FN (GpStatus, GdipImageGetFrameDimensionsCount, (GpImage *, UINT *)); -DEF_DLL_FN (GpStatus, GdipImageGetFrameDimensionsList, (GpImage *, GUID *, UINT)); -DEF_DLL_FN (GpStatus, GdipImageGetFrameCount, (GpImage *, GDIPCONST GUID *, UINT *)); -DEF_DLL_FN (GpStatus, GdipImageSelectActiveFrame, (GpImage*, GDIPCONST GUID *, UINT)); +DEF_DLL_FN (GpStatus, GdipImageGetFrameDimensionsList, + (GpImage *, GUID *, UINT)); +DEF_DLL_FN (GpStatus, GdipImageGetFrameCount, + (GpImage *, GDIPCONST GUID *, UINT *)); +DEF_DLL_FN (GpStatus, GdipImageSelectActiveFrame, + (GpImage*, GDIPCONST GUID *, UINT)); DEF_DLL_FN (GpStatus, GdipCreateBitmapFromFile, (WCHAR *, GpBitmap **)); DEF_DLL_FN (GpStatus, GdipCreateBitmapFromStream, (IStream *, GpBitmap **)); DEF_DLL_FN (IStream *, SHCreateMemStream, (const BYTE *pInit, UINT cbInit)); -DEF_DLL_FN (GpStatus, GdipCreateHBITMAPFromBitmap, (GpBitmap *, HBITMAP *, ARGB)); +DEF_DLL_FN (GpStatus, GdipCreateHBITMAPFromBitmap, + (GpBitmap *, HBITMAP *, ARGB)); DEF_DLL_FN (GpStatus, GdipDisposeImage, (GpImage *)); DEF_DLL_FN (GpStatus, GdipGetImageHeight, (GpImage *, UINT *)); DEF_DLL_FN (GpStatus, GdipGetImageWidth, (GpImage *, UINT *)); -#endif - -static int gdip_initialized = 0; -static ULONG_PTR token; -static GdiplusStartupInput input; -static GdiplusStartupOutput output; -bool -w32_gdiplus_startup (void) +static bool +gdiplus_init (void) { HANDLE gdiplus_lib, shlwapi_lib; - GpStatus status; - if (gdip_initialized < 0) - return 0; - else if (gdip_initialized) - return 1; - -#ifndef LINK_GDIPLUS_STATICALLY - DEFSYM (Qgdiplus, "gdiplus"); - DEFSYM (Qshlwapi, "shlwapi"); - if (!(gdiplus_lib = w32_delayed_load (Qgdiplus))) { - gdip_initialized = -1; - return 0; - } - if (!(shlwapi_lib = w32_delayed_load (Qshlwapi))) { - gdip_initialized = -1; - return 0; - } + if (!((gdiplus_lib = w32_delayed_load (Qgdiplus)) + && (shlwapi_lib = w32_delayed_load (Qshlwapi)))) + return false; LOAD_DLL_FN (gdiplus_lib, GdiplusStartup); LOAD_DLL_FN (gdiplus_lib, GdiplusShutdown); @@ -94,7 +83,41 @@ w32_gdiplus_startup (void) LOAD_DLL_FN (gdiplus_lib, GdipDisposeImage); LOAD_DLL_FN (gdiplus_lib, GdipGetImageHeight); LOAD_DLL_FN (gdiplus_lib, GdipGetImageWidth); - LOAD_DLL_FN (shlwapi_lib, SHCreateMemStream); + /* LOAD_DLL_FN (shlwapi_lib, SHCreateMemStream); */ + + /* The following terrible kludge is required to use native image API + on Windows before Vista, because SHCreateMemStream was not + exported by name in those versions, only by ordinal number. */ + fn_SHCreateMemStream = + (W32_PFN_SHCreateMemStream) get_proc_addr (shlwapi_lib, + "SHCreateMemStream"); + if (!fn_SHCreateMemStream) + { + fn_SHCreateMemStream = + (W32_PFN_SHCreateMemStream) get_proc_addr (shlwapi_lib, + MAKEINTRESOURCEA (12)); + if (!fn_SHCreateMemStream) + return false; + } + + return true; +} + +# undef GdiplusStartup +# undef GdiplusShutdown +# undef GdipGetPropertyItemSize +# undef GdipGetPropertyItem +# undef GdipImageGetFrameDimensionsCount +# undef GdipImageGetFrameDimensionsList +# undef GdipImageGetFrameCount +# undef GdipImageSelectActiveFrame +# undef GdipCreateBitmapFromFile +# undef GdipCreateBitmapFromStream +# undef SHCreateMemStream +# undef GdipCreateHBITMAPFromBitmap +# undef GdipDisposeImage +# undef GdipGetImageHeight +# undef GdipGetImageWidth # define GdiplusStartup fn_GdiplusStartup # define GdiplusShutdown fn_GdiplusShutdown @@ -111,32 +134,71 @@ w32_gdiplus_startup (void) # define GdipDisposeImage fn_GdipDisposeImage # define GdipGetImageHeight fn_GdipGetImageHeight # define GdipGetImageWidth fn_GdipGetImageWidth -#endif - input.GdiplusVersion = 1; - input.DebugEventCallback = NULL; - input.SuppressBackgroundThread = FALSE; - input.SuppressExternalCodecs = FALSE; +#endif /* WINDOWSNT */ - status = GdiplusStartup (&token, &input, &output); - if (status == Ok) - { - gdip_initialized = 1; - return 1; - } - else +static int gdip_initialized; +static bool gdiplus_started; +static ULONG_PTR token; +static GdiplusStartupInput input; +static GdiplusStartupOutput output; + + +/* Initialize GDI+, return true if successful. */ +static bool +gdiplus_startup (void) +{ + GpStatus status; + + if (gdiplus_started) + return true; +#ifdef WINDOWSNT + if (!gdip_initialized) + gdip_initialized = gdiplus_init () ? 1 : -1; +#else + gdip_initialized = 1; +#endif + if (gdip_initialized > 0) { - gdip_initialized = -1; - return 0; + input.GdiplusVersion = 1; + input.DebugEventCallback = NULL; + input.SuppressBackgroundThread = FALSE; + input.SuppressExternalCodecs = FALSE; + + status = GdiplusStartup (&token, &input, &output); + if (status == Ok) + gdiplus_started = true; + return (status == Ok); } + return false; } +/* This is called from term_ntproc. */ void w32_gdiplus_shutdown (void) { - GdiplusShutdown (token); + if (gdiplus_started) + GdiplusShutdown (token); + gdiplus_started = false; } +bool +w32_can_use_native_image_api (Lisp_Object type) +{ + if (!w32_use_native_image_api) + return false; + if (!(EQ (type, Qjpeg) + || EQ (type, Qpng) + || EQ (type, Qgif) + || EQ (type, Qtiff) + || EQ (type, Qnative_image))) + { + /* GDI+ can also display BMP, Exif, ICON, WMF, and EMF images. + But we don't yet support these in image.c. */ + return false; + } + return gdiplus_startup (); +} static double w32_frame_delay (GpBitmap *pBitmap, int frame) @@ -150,25 +212,26 @@ w32_frame_delay (GpBitmap *pBitmap, int frame) GdipGetPropertyItemSize (pBitmap, PropertyTagFrameDelay, &size); /* Allocate a buffer to receive the property item. */ - propertyItem = (PropertyItem*)malloc (size); + propertyItem = malloc (size); if (propertyItem != NULL) { /* Get the property item. */ GdipGetPropertyItem (pBitmap, PropertyTagFrameDelay, size, propertyItem); - delay = ((double)propertyItem[frame].length) / 100; + delay = propertyItem[frame].length / 100.0; if (delay == 0) { /* In GIF files, unfortunately, delay is only specified for the first frame. */ - delay = ((double)propertyItem[0].length) / 100; + delay = propertyItem[0].length / 100.0; } free (propertyItem); } return delay; } -static UINT -w32_select_active_frame (GpBitmap *pBitmap, int frame, int *nframes, double *delay) +static GpStatus +w32_select_active_frame (GpBitmap *pBitmap, int frame, int *nframes, + double *delay) { UINT count, frameCount; GUID pDimensionIDs[1]; @@ -181,15 +244,14 @@ w32_select_active_frame (GpBitmap *pBitmap, int frame, int *nframes, double *del { status = GdipImageGetFrameDimensionsList (pBitmap, pDimensionIDs, 1); status = GdipImageGetFrameCount (pBitmap, &pDimensionIDs[0], &frameCount); - if ((status == Ok) && (frameCount > 1)) + if (status == Ok && frameCount > 1) { if (frame < 0 || frame >= frameCount) - { - status = GenericError; - } + status = GenericError; else { - status = GdipImageSelectActiveFrame (pBitmap, &pDimensionIDs[0], frame); + status = GdipImageSelectActiveFrame (pBitmap, &pDimensionIDs[0], + frame); *delay = w32_frame_delay (pBitmap, frame); *nframes = frameCount; } @@ -201,9 +263,7 @@ w32_select_active_frame (GpBitmap *pBitmap, int frame, int *nframes, double *del static ARGB w32_image_bg_color (struct frame *f, struct image *img) { - /* png_color_16 *image_bg; */ - Lisp_Object specified_bg - = Fplist_get (XCDR (img->spec), QCbackground); + Lisp_Object specified_bg = Fplist_get (XCDR (img->spec), QCbackground); Emacs_Color color; /* If the user specified a color, try to use it; if not, use the @@ -212,38 +272,34 @@ w32_image_bg_color (struct frame *f, struct image *img) if (STRINGP (specified_bg) ? w32_defined_color (f, SSDATA (specified_bg), &color, false, false) : (w32_query_frame_background_color (f, &color), true)) - /* The user specified `:background', use that. */ + /* The user specified ':background', use that. */ { DWORD red = (((DWORD) color.red) & 0xff00) << 8; DWORD green = ((DWORD) color.green) & 0xff00; DWORD blue = ((DWORD) color.blue) >> 8; - return red | green | blue; + return (ARGB) (red | green | blue); } - return ((DWORD) 0xff000000); + return (ARGB) 0xff000000; } int w32_load_image (struct frame *f, struct image *img, Lisp_Object spec_file, Lisp_Object spec_data) { - Emacs_Pixmap pixmap; GpStatus status = GenericError; GpBitmap *pBitmap; - wchar_t filename[MAX_PATH]; - ARGB bg_color; - Lisp_Object lisp_index, metadata; - unsigned int index, nframes; - double delay; + Lisp_Object metadata; eassert (valid_image_p (img->spec)); - /* This function only gets called if init_w32_gdiplus () was invoked. We have - a valid token and GDI+ is active. */ + /* This function only gets called if w32_gdiplus_startup was invoked + and succeeded. We have a valid token and GDI+ is active. */ if (STRINGP (spec_file)) { if (w32_unicode_filenames) { - filename_to_utf16 (SSDATA (spec_file) , filename); + wchar_t filename[MAX_PATH]; + filename_to_utf16 (SSDATA (spec_file), filename); status = GdipCreateBitmapFromFile (filename, &pBitmap); } else @@ -254,7 +310,7 @@ w32_load_image (struct frame *f, struct image *img, } else if (STRINGP (spec_data)) { - IStream *pStream = SHCreateMemStream ((BYTE *) SSDATA (spec_data), + IStream *pStream = SHCreateMemStream ((BYTE *) SDATA (spec_data), SBYTES (spec_data)); if (pStream != NULL) { @@ -266,22 +322,28 @@ w32_load_image (struct frame *f, struct image *img, metadata = Qnil; if (status == Ok) { - /* In multiframe pictures, select the first one */ - lisp_index = Fplist_get (XCDR (img->spec), QCindex); - index = FIXNUMP (lisp_index) ? XFIXNAT (lisp_index) : 0; + /* In multiframe pictures, select the first frame. */ + Lisp_Object lisp_index = Fplist_get (XCDR (img->spec), QCindex); + int index = FIXNATP (lisp_index) ? XFIXNAT (lisp_index) : 0; + int nframes; + double delay; status = w32_select_active_frame (pBitmap, index, &nframes, &delay); - if ((status == Ok)) + if (status == Ok) { if (nframes > 1) metadata = Fcons (Qcount, Fcons (make_fixnum (nframes), metadata)); if (delay) metadata = Fcons (Qdelay, Fcons (make_float (delay), metadata)); } + else if (status == Win32Error) /* FIXME! */ + status = Ok; } if (status == Ok) { - bg_color = w32_image_bg_color (f, img); + ARGB bg_color = w32_image_bg_color (f, img); + Emacs_Pixmap pixmap; + status = GdipCreateHBITMAPFromBitmap (pBitmap, &pixmap, bg_color); if (status == Ok) { |