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.
This commit is contained in:
parent
df254a7445
commit
e94206aaf6
8 changed files with 259 additions and 153 deletions
16
configure.ac
16
configure.ac
|
@ -433,7 +433,7 @@ OPTION_DEFAULT_ON([libsystemd],[don't compile with libsystemd support])
|
|||
OPTION_DEFAULT_ON([cairo],[don't compile with Cairo drawing])
|
||||
OPTION_DEFAULT_ON([xml2],[don't compile with XML parsing support])
|
||||
OPTION_DEFAULT_OFF([imagemagick],[compile with ImageMagick image support])
|
||||
OPTION_DEFAULT_OFF([native-image-api], [use native API's (GDI+ on Windows) for JPEG/TIFF/GIFF/PNG])
|
||||
OPTION_DEFAULT_ON([native-image-api], [use native image APIs (GDI+ on Windows)])
|
||||
OPTION_DEFAULT_ON([json], [don't compile with native JSON support])
|
||||
|
||||
OPTION_DEFAULT_ON([xft],[don't use XFT for anti aliased fonts])
|
||||
|
@ -2127,7 +2127,7 @@ LIB_WSOCK32=
|
|||
NTLIB=
|
||||
CM_OBJ="cm.o"
|
||||
XARGS_LIMIT=
|
||||
HAVE_NATIVE_IMAGE_API=no
|
||||
NATIVE_IMAGE_API=no
|
||||
if test "${HAVE_W32}" = "yes"; then
|
||||
AC_DEFINE(HAVE_NTGUI, 1, [Define to use native MS Windows GUI.])
|
||||
if test "$with_toolkit_scroll_bars" = "no"; then
|
||||
|
@ -2156,12 +2156,14 @@ if test "${HAVE_W32}" = "yes"; then
|
|||
# the rc file), not a linker script.
|
||||
W32_RES_LINK="-Wl,emacs.res"
|
||||
else
|
||||
W32_OBJ="$W32_OBJ w32.o w32console.o w32heap.o w32inevt.o w32proc.o"
|
||||
dnl FIXME: This should probably be supported for Cygwin/w32 as
|
||||
dnl well, but the Cygwin build needs to link against -lgdiplus
|
||||
if test "${with_native_image_api}" = yes; then
|
||||
AC_DEFINE(HAVE_NATIVE_IMAGE_API, 1, [Define to use MS Windows GDI+ for images.])
|
||||
HAVE_NATIVE_IMAGE_API=yes
|
||||
W32_NATIVE_IMAGE_API="w32image.o"
|
||||
AC_DEFINE(HAVE_NATIVE_IMAGE_API, 1, [Define to use native OS APIs for images.])
|
||||
NATIVE_IMAGE_API="yes (w32)"
|
||||
W32_OBJ="$W32_OBJ w32image.o"
|
||||
fi
|
||||
W32_OBJ="$W32_OBJ w32.o w32console.o w32heap.o w32inevt.o w32proc.o $W32_NATIVE_IMAGE_API"
|
||||
W32_LIBS="$W32_LIBS -lwinmm -lusp10 -lgdi32 -lcomdlg32"
|
||||
W32_LIBS="$W32_LIBS -lmpr -lwinspool -lole32 -lcomctl32"
|
||||
W32_RES_LINK="\$(EMACSRES)"
|
||||
|
@ -5710,7 +5712,7 @@ AS_ECHO([" Does Emacs use -lXaw3d? ${HAVE_XAW3D
|
|||
Does Emacs use cairo? ${HAVE_CAIRO}
|
||||
Does Emacs use -llcms2? ${HAVE_LCMS2}
|
||||
Does Emacs use imagemagick? ${HAVE_IMAGEMAGICK}
|
||||
Does Emacs use native API for images? ${HAVE_NATIVE_IMAGE_API}
|
||||
Does Emacs use native APIs for images? ${NATIVE_IMAGE_API}
|
||||
Does Emacs support sound? ${HAVE_SOUND}
|
||||
Does Emacs use -lgpm? ${HAVE_GPM}
|
||||
Does Emacs use -ldbus? ${HAVE_DBUS}
|
||||
|
|
5
etc/NEWS
5
etc/NEWS
|
@ -393,6 +393,11 @@ images in JPEG, PNG, GIF and TIFF formats. This support is enabled
|
|||
with --with-native-image-api, which automatically disables the use of
|
||||
optional third party libraries for those formats.
|
||||
|
||||
This feature is experimental, and needs to be turned on to be used.
|
||||
To turn this on, set the variable 'w32-use-native-image-API' to a
|
||||
non-nil value. Please report any bugs you find while using the native
|
||||
image API via "M-x report-emacs-bug".
|
||||
|
||||
|
||||
----------------------------------------------------------------------
|
||||
This file is part of GNU Emacs.
|
||||
|
|
134
src/image.c
134
src/image.c
|
@ -751,7 +751,7 @@ struct image_type
|
|||
|
||||
/* Check that SPEC is a valid image specification for the given
|
||||
image type. Value is true if SPEC is valid. */
|
||||
bool (*valid_p) (Lisp_Object spec, Lisp_Object type);
|
||||
bool (*valid_p) (Lisp_Object spec);
|
||||
|
||||
/* Load IMG which is used on frame F from information contained in
|
||||
IMG->spec. Value is true if successful. */
|
||||
|
@ -807,7 +807,7 @@ valid_image_p (Lisp_Object object)
|
|||
{
|
||||
struct image_type const *type = lookup_image_type (XCAR (tail));
|
||||
if (type)
|
||||
return type->valid_p (object, builtin_lisp_symbol (type->type));
|
||||
return type->valid_p (object);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -816,7 +816,6 @@ valid_image_p (Lisp_Object object)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* Log error message with format string FORMAT and trailing arguments.
|
||||
Signaling an error, e.g. when an image cannot be loaded, is not a
|
||||
good idea because this would interrupt redisplay, and the error
|
||||
|
@ -1004,7 +1003,8 @@ parse_image_spec (Lisp_Object spec, struct image_keyword *keywords,
|
|||
break;
|
||||
}
|
||||
|
||||
if (EQ (key, QCtype) && !EQ (type, value))
|
||||
if (EQ (key, QCtype)
|
||||
&& !(EQ (type, value) || EQ (type, Qnative_image)))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -3144,12 +3144,12 @@ enum xbm_token
|
|||
displayed is used. */
|
||||
|
||||
static bool
|
||||
xbm_image_p (Lisp_Object object, Lisp_Object type)
|
||||
xbm_image_p (Lisp_Object object)
|
||||
{
|
||||
struct image_keyword kw[XBM_LAST];
|
||||
|
||||
memcpy (kw, xbm_format, sizeof kw);
|
||||
if (!parse_image_spec (object, kw, XBM_LAST, type))
|
||||
if (!parse_image_spec (object, kw, XBM_LAST, Qxbm))
|
||||
return 0;
|
||||
|
||||
eassert (EQ (kw[XBM_TYPE].value, Qxbm));
|
||||
|
@ -3697,7 +3697,7 @@ xbm_load (struct frame *f, struct image *img)
|
|||
bool success_p = 0;
|
||||
Lisp_Object file_name;
|
||||
|
||||
eassert (xbm_image_p (img->spec, Qxbm));
|
||||
eassert (xbm_image_p (img->spec));
|
||||
|
||||
/* If IMG->spec specifies a file name, create a non-file spec from it. */
|
||||
file_name = image_spec_value (img->spec, QCfile, NULL);
|
||||
|
@ -4155,11 +4155,11 @@ xpm_valid_color_symbols_p (Lisp_Object color_symbols)
|
|||
/* Value is true if OBJECT is a valid XPM image specification. */
|
||||
|
||||
static bool
|
||||
xpm_image_p (Lisp_Object object, Lisp_Object type)
|
||||
xpm_image_p (Lisp_Object object)
|
||||
{
|
||||
struct image_keyword fmt[XPM_LAST];
|
||||
memcpy (fmt, xpm_format, sizeof fmt);
|
||||
return (parse_image_spec (object, fmt, XPM_LAST, type)
|
||||
return (parse_image_spec (object, fmt, XPM_LAST, Qxpm)
|
||||
/* Either `:file' or `:data' must be present. */
|
||||
&& fmt[XPM_FILE].count + fmt[XPM_DATA].count == 1
|
||||
/* Either no `:color-symbols' or it's a list of conses
|
||||
|
@ -5883,13 +5883,13 @@ static const struct image_keyword pbm_format[PBM_LAST] =
|
|||
/* Return true if OBJECT is a valid PBM image specification. */
|
||||
|
||||
static bool
|
||||
pbm_image_p (Lisp_Object object, Lisp_Object type)
|
||||
pbm_image_p (Lisp_Object object)
|
||||
{
|
||||
struct image_keyword fmt[PBM_LAST];
|
||||
|
||||
memcpy (fmt, pbm_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, PBM_LAST, type))
|
||||
if (!parse_image_spec (object, fmt, PBM_LAST, Qpbm))
|
||||
return 0;
|
||||
|
||||
/* Must specify either :data or :file. */
|
||||
|
@ -6235,21 +6235,30 @@ pbm_load (struct frame *f, struct image *img)
|
|||
/***********************************************************************
|
||||
NATIVE IMAGE HANDLING
|
||||
***********************************************************************/
|
||||
#if defined(HAVE_NATIVE_IMAGE_API) && defined(HAVE_NTGUI)
|
||||
|
||||
static bool
|
||||
image_can_use_native_api (Lisp_Object type)
|
||||
{
|
||||
#if HAVE_NATIVE_IMAGE_API
|
||||
# ifdef HAVE_NTGUI
|
||||
return w32_can_use_native_image_api (type);
|
||||
# else
|
||||
return false;
|
||||
# endif
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAVE_NATIVE_IMAGE_API
|
||||
|
||||
/*
|
||||
* These functions are actually defined in the OS-native implementation
|
||||
* file. Currently, for Windows GDI+ interface, w32image.c, but other
|
||||
* operating systems can follow suit.
|
||||
*/
|
||||
|
||||
static bool
|
||||
init_native_image_functions (void)
|
||||
{
|
||||
return w32_gdiplus_startup ();
|
||||
}
|
||||
|
||||
/* Indices of image specification fields in native format, below. */
|
||||
|
||||
enum native_image_keyword_index
|
||||
{
|
||||
NATIVE_IMAGE_TYPE,
|
||||
|
@ -6268,7 +6277,6 @@ enum native_image_keyword_index
|
|||
|
||||
/* Vector of image_keyword structures describing the format
|
||||
of valid user-defined image specifications. */
|
||||
|
||||
static const struct image_keyword native_image_format[] =
|
||||
{
|
||||
{":type", IMAGE_SYMBOL_VALUE, 1},
|
||||
|
@ -6287,12 +6295,12 @@ static const struct image_keyword native_image_format[] =
|
|||
/* Return true if OBJECT is a valid native API image specification. */
|
||||
|
||||
static bool
|
||||
native_image_p (Lisp_Object object, Lisp_Object type)
|
||||
native_image_p (Lisp_Object object)
|
||||
{
|
||||
struct image_keyword fmt[NATIVE_IMAGE_LAST];
|
||||
memcpy (fmt, native_image_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, 10, type))
|
||||
if (!parse_image_spec (object, fmt, 10, Qnative_image))
|
||||
return 0;
|
||||
|
||||
/* Must specify either the :data or :file keyword. */
|
||||
|
@ -6302,11 +6310,17 @@ native_image_p (Lisp_Object object, Lisp_Object type)
|
|||
static bool
|
||||
native_image_load (struct frame *f, struct image *img)
|
||||
{
|
||||
|
||||
# ifdef HAVE_NTGUI
|
||||
return w32_load_image (f, img,
|
||||
image_spec_value (img->spec, QCfile, NULL),
|
||||
image_spec_value (img->spec, QCdata, NULL));
|
||||
# else
|
||||
return 0;
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAVE_NATIVE_IMAGE_API */
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
|
@ -6352,12 +6366,12 @@ static const struct image_keyword png_format[PNG_LAST] =
|
|||
/* Return true if OBJECT is a valid PNG image specification. */
|
||||
|
||||
static bool
|
||||
png_image_p (Lisp_Object object, Lisp_Object type)
|
||||
png_image_p (Lisp_Object object)
|
||||
{
|
||||
struct image_keyword fmt[PNG_LAST];
|
||||
memcpy (fmt, png_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, PNG_LAST, type))
|
||||
if (!parse_image_spec (object, fmt, PNG_LAST, Qpng))
|
||||
return 0;
|
||||
|
||||
/* Must specify either the :data or :file keyword. */
|
||||
|
@ -7014,13 +7028,13 @@ static const struct image_keyword jpeg_format[JPEG_LAST] =
|
|||
/* Return true if OBJECT is a valid JPEG image specification. */
|
||||
|
||||
static bool
|
||||
jpeg_image_p (Lisp_Object object, Lisp_Object type)
|
||||
jpeg_image_p (Lisp_Object object)
|
||||
{
|
||||
struct image_keyword fmt[JPEG_LAST];
|
||||
|
||||
memcpy (fmt, jpeg_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, JPEG_LAST, type))
|
||||
if (!parse_image_spec (object, fmt, JPEG_LAST, Qjpeg))
|
||||
return 0;
|
||||
|
||||
/* Must specify either the :data or :file keyword. */
|
||||
|
@ -7590,12 +7604,12 @@ static const struct image_keyword tiff_format[TIFF_LAST] =
|
|||
/* Return true if OBJECT is a valid TIFF image specification. */
|
||||
|
||||
static bool
|
||||
tiff_image_p (Lisp_Object object, Lisp_Object type)
|
||||
tiff_image_p (Lisp_Object object)
|
||||
{
|
||||
struct image_keyword fmt[TIFF_LAST];
|
||||
memcpy (fmt, tiff_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, TIFF_LAST, type))
|
||||
if (!parse_image_spec (object, fmt, TIFF_LAST, Qtiff))
|
||||
return 0;
|
||||
|
||||
/* Must specify either the :data or :file keyword. */
|
||||
|
@ -8038,12 +8052,12 @@ gif_clear_image (struct frame *f, struct image *img)
|
|||
/* Return true if OBJECT is a valid GIF image specification. */
|
||||
|
||||
static bool
|
||||
gif_image_p (Lisp_Object object, Lisp_Object type)
|
||||
gif_image_p (Lisp_Object object)
|
||||
{
|
||||
struct image_keyword fmt[GIF_LAST];
|
||||
memcpy (fmt, gif_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, GIF_LAST, type))
|
||||
if (!parse_image_spec (object, fmt, GIF_LAST, Qgif))
|
||||
return 0;
|
||||
|
||||
/* Must specify either the :data or :file keyword. */
|
||||
|
@ -8650,12 +8664,12 @@ imagemagick_clear_image (struct frame *f,
|
|||
identify the IMAGEMAGICK format. */
|
||||
|
||||
static bool
|
||||
imagemagick_image_p (Lisp_Object object, Lisp_Object type)
|
||||
imagemagick_image_p (Lisp_Object object)
|
||||
{
|
||||
struct image_keyword fmt[IMAGEMAGICK_LAST];
|
||||
memcpy (fmt, imagemagick_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, IMAGEMAGICK_LAST, type))
|
||||
if (!parse_image_spec (object, fmt, IMAGEMAGICK_LAST, Qimagemagick))
|
||||
return 0;
|
||||
|
||||
/* Must specify either the :data or :file keyword. */
|
||||
|
@ -9445,12 +9459,12 @@ static const struct image_keyword svg_format[SVG_LAST] =
|
|||
identify the SVG format. */
|
||||
|
||||
static bool
|
||||
svg_image_p (Lisp_Object object, Lisp_Object type)
|
||||
svg_image_p (Lisp_Object object)
|
||||
{
|
||||
struct image_keyword fmt[SVG_LAST];
|
||||
memcpy (fmt, svg_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, SVG_LAST, type))
|
||||
if (!parse_image_spec (object, fmt, SVG_LAST, Qsvg))
|
||||
return 0;
|
||||
|
||||
/* Must specify either the :data or :file keyword. */
|
||||
|
@ -9913,7 +9927,7 @@ static const struct image_keyword gs_format[GS_LAST] =
|
|||
specification. */
|
||||
|
||||
static bool
|
||||
gs_image_p (Lisp_Object object, Lisp_Object type)
|
||||
gs_image_p (Lisp_Object object)
|
||||
{
|
||||
struct image_keyword fmt[GS_LAST];
|
||||
Lisp_Object tem;
|
||||
|
@ -9921,7 +9935,7 @@ gs_image_p (Lisp_Object object, Lisp_Object type)
|
|||
|
||||
memcpy (fmt, gs_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, GS_LAST, type))
|
||||
if (!parse_image_spec (object, fmt, GS_LAST, Qpostscript))
|
||||
return 0;
|
||||
|
||||
/* Bounding box must be a list or vector containing 4 integers. */
|
||||
|
@ -10208,20 +10222,19 @@ static bool
|
|||
initialize_image_type (struct image_type const *type)
|
||||
{
|
||||
#ifdef WINDOWSNT
|
||||
Lisp_Object typesym, tested;
|
||||
bool (*init) (void) = type->init;
|
||||
Lisp_Object typesym = builtin_lisp_symbol (type->type);
|
||||
|
||||
#ifdef HAVE_NATIVE_IMAGE_API
|
||||
if (init == init_native_image_functions)
|
||||
return init();
|
||||
if (image_can_use_native_api (typesym))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
typesym = builtin_lisp_symbol (type->type);
|
||||
tested = Fassq (typesym, Vlibrary_cache);
|
||||
Lisp_Object tested = Fassq (typesym, Vlibrary_cache);
|
||||
/* If we failed to load the library before, don't try again. */
|
||||
if (CONSP (tested))
|
||||
return !NILP (XCDR (tested)) ? true : false;
|
||||
|
||||
bool (*init) (void) = type->init;
|
||||
if (init)
|
||||
{
|
||||
bool type_valid = init ();
|
||||
|
@ -10248,16 +10261,6 @@ static struct image_type const image_types[] =
|
|||
{ SYMBOL_INDEX (Qsvg), svg_image_p, svg_load, image_clear_image,
|
||||
IMAGE_TYPE_INIT (init_svg_functions) },
|
||||
#endif
|
||||
#if defined HAVE_NATIVE_IMAGE_API
|
||||
{ SYMBOL_INDEX (Qjpeg), native_image_p, native_image_load, image_clear_image,
|
||||
IMAGE_TYPE_INIT (init_native_image_functions) },
|
||||
{ SYMBOL_INDEX (Qpng), native_image_p, native_image_load, image_clear_image,
|
||||
IMAGE_TYPE_INIT (init_native_image_functions) },
|
||||
{ SYMBOL_INDEX (Qgif), native_image_p, native_image_load, image_clear_image,
|
||||
IMAGE_TYPE_INIT (init_native_image_functions) },
|
||||
{ SYMBOL_INDEX (Qtiff), native_image_p, native_image_load, image_clear_image,
|
||||
IMAGE_TYPE_INIT (init_native_image_functions) },
|
||||
#endif
|
||||
#if defined HAVE_PNG || defined HAVE_NS
|
||||
{ SYMBOL_INDEX (Qpng), png_image_p, png_load, image_clear_image,
|
||||
IMAGE_TYPE_INIT (init_png_functions) },
|
||||
|
@ -10282,23 +10285,28 @@ static struct image_type const image_types[] =
|
|||
{ SYMBOL_INDEX (Qpbm), pbm_image_p, pbm_load, image_clear_image },
|
||||
};
|
||||
|
||||
#ifdef HAVE_NATIVE_IMAGE_API
|
||||
struct image_type native_image_type =
|
||||
{ SYMBOL_INDEX (Qnative_image), native_image_p, native_image_load,
|
||||
image_clear_image };
|
||||
#endif
|
||||
|
||||
/* Look up image type TYPE, and return a pointer to its image_type
|
||||
structure. Return 0 if TYPE is not a known image type. */
|
||||
|
||||
static struct image_type const *
|
||||
lookup_image_type (Lisp_Object type)
|
||||
{
|
||||
#ifdef HAVE_NATIVE_IMAGE_API
|
||||
if (image_can_use_native_api (type))
|
||||
return &native_image_type;
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < ARRAYELTS (image_types); i++)
|
||||
{
|
||||
struct image_type const *r = &image_types[i];
|
||||
if (EQ (type, builtin_lisp_symbol (r->type)))
|
||||
#ifdef HAVE_NATIVE_IMAGE_API
|
||||
/* We can have more than one backend for one image type. */
|
||||
if (initialize_image_type (r))
|
||||
return r;
|
||||
#else
|
||||
return initialize_image_type (r) ? r : NULL;
|
||||
#endif
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -10454,6 +10462,14 @@ non-numeric, there is no explicit limit on the size of images. */);
|
|||
#endif /* HAVE_NTGUI */
|
||||
#endif /* HAVE_RSVG */
|
||||
|
||||
#if HAVE_NATIVE_IMAGE_API
|
||||
DEFSYM (Qnative_image, "native-image");
|
||||
# ifdef HAVE_NTGUI
|
||||
DEFSYM (Qgdiplus, "gdiplus");
|
||||
DEFSYM (Qshlwapi, "shlwapi");
|
||||
# endif
|
||||
#endif
|
||||
|
||||
defsubr (&Sinit_image_library);
|
||||
#ifdef HAVE_IMAGEMAGICK
|
||||
defsubr (&Simagemagick_types);
|
||||
|
|
|
@ -10226,7 +10226,7 @@ term_ntproc (int ignored)
|
|||
|
||||
term_w32select ();
|
||||
|
||||
#ifdef HAVE_GDIPLUS
|
||||
#if HAVE_NATIVE_IMAGE_API
|
||||
w32_gdiplus_shutdown ();
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -41,6 +41,12 @@ typedef struct _XImage
|
|||
/* Optional RGBQUAD array for palette follows (see BITMAPINFO docs). */
|
||||
} XImage;
|
||||
|
||||
struct image;
|
||||
extern int w32_load_image (struct frame *f, struct image *img,
|
||||
Lisp_Object spec_file, Lisp_Object spec_data);
|
||||
extern bool w32_can_use_native_image_api (Lisp_Object);
|
||||
extern void w32_gdiplus_shutdown (void);
|
||||
|
||||
#define FACE_DEFAULT (~0)
|
||||
|
||||
extern HINSTANCE hinst;
|
||||
|
|
226
src/w32image.c
226
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 /* WINDOWSNT */
|
||||
|
||||
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
|
||||
|
||||
input.GdiplusVersion = 1;
|
||||
input.DebugEventCallback = NULL;
|
||||
input.SuppressBackgroundThread = FALSE;
|
||||
input.SuppressExternalCodecs = FALSE;
|
||||
|
||||
status = GdiplusStartup (&token, &input, &output);
|
||||
if (status == Ok)
|
||||
if (gdip_initialized > 0)
|
||||
{
|
||||
gdip_initialized = 1;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -7658,6 +7658,25 @@ Windows 8. It is set to nil on Windows 9X. */);
|
|||
else
|
||||
w32_unicode_filenames = 1;
|
||||
|
||||
DEFVAR_BOOL ("w32-use-native-image-API",
|
||||
w32_use_native_image_api,
|
||||
doc: /* Non-nil means use the native MS-Windows image API to display images.
|
||||
|
||||
A value of nil means displaying images other than PBM and XBM requires
|
||||
optional supporting libraries to be installed.
|
||||
The native image API library used is GDI+ via GDIPLUS.DLL. This
|
||||
library is available only since W2K, therefore this variable is
|
||||
unconditionally set to nil on older systems. */);
|
||||
|
||||
/* For now, disabled by default, since this is an experimental feature. */
|
||||
#if 0 && HAVE_NATIVE_IMAGE_API
|
||||
if (os_subtype == OS_9X)
|
||||
w32_use_native_image_api = 0;
|
||||
else
|
||||
w32_use_native_image_api = 1;
|
||||
#else
|
||||
w32_use_native_image_api = 0;
|
||||
#endif
|
||||
|
||||
/* FIXME: The following variable will be (hopefully) removed
|
||||
before Emacs 25.1 gets released. */
|
||||
|
|
|
@ -75,10 +75,6 @@ struct w32_palette_entry {
|
|||
extern void w32_regenerate_palette (struct frame *f);
|
||||
extern void w32_fullscreen_rect (HWND hwnd, int fsmode, RECT normal,
|
||||
RECT *rect);
|
||||
extern int w32_load_image (struct frame *f, struct image *img,
|
||||
Lisp_Object spec_file, Lisp_Object spec_data);
|
||||
extern bool w32_gdiplus_startup (void);
|
||||
extern void w32_gdiplus_shutdown (void);
|
||||
|
||||
/* For each display (currently only one on w32), we have a structure that
|
||||
records information about it. */
|
||||
|
|
Loading…
Add table
Reference in a new issue