Initial version of native image API support for MS-Windows
* src/w32image.c: New file. * src/w32term.h: Add prototypes of 'w32_load_image', 'w32_gdiplus_startup', 'w32_gdiplus_shutdown', and 'w32_query_frame_background_color'. * src/w32term.c (w32_query_frame_background_color): No longer static. * src/w32.c (term_ntproc) [HAVE_GDIPLUS]: Call 'w32_gdiplus_shutdown'. * src/image.c (struct image_type) <valid_p>: Accept an additional argument, the image type. All implementations changed. (init_native_image_functions, native_image_p, native_image_load) [HAVE_NATIVE_IMAGE_API]: New methods for "native image type". (initialize_image_type) [HAVE_NATIVE_IMAGE_API]: Call 'init_native_image_functions'. (image_types) [HAVE_NATIVE_IMAGE_API]: Add settings for native image API. (lookup_image_type) [HAVE_NATIVE_IMAGE_API]: Initialize native functions if needed. * lisp/term/w32-win.el (dynamic-library-alist): Add gdiplus and shlwapi. * etc/NEWS: Announce the new feature. * configure.ac (native-image-api): New option, OFF by default. (HAVE_NATIVE_IMAGE_API): If native-image-api is selected, add w32image.o to W32_OBJ.
This commit is contained in:
parent
7a9fb5d55c
commit
df254a7445
8 changed files with 466 additions and 35 deletions
10
configure.ac
10
configure.ac
|
@ -433,6 +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([json], [don't compile with native JSON support])
|
||||
|
||||
OPTION_DEFAULT_ON([xft],[don't use XFT for anti aliased fonts])
|
||||
|
@ -2126,6 +2127,7 @@ LIB_WSOCK32=
|
|||
NTLIB=
|
||||
CM_OBJ="cm.o"
|
||||
XARGS_LIMIT=
|
||||
HAVE_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
|
||||
|
@ -2154,7 +2156,12 @@ 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"
|
||||
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"
|
||||
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)"
|
||||
|
@ -5703,6 +5710,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 support sound? ${HAVE_SOUND}
|
||||
Does Emacs use -lgpm? ${HAVE_GPM}
|
||||
Does Emacs use -ldbus? ${HAVE_DBUS}
|
||||
|
|
7
etc/NEWS
7
etc/NEWS
|
@ -386,6 +386,13 @@ and enable the MS-Windows native Input Method Editor (IME) at run
|
|||
time. A companion function 'w32-get-ime-open-status' returns the
|
||||
current IME activation status.
|
||||
|
||||
+++
|
||||
** On MS-Windows, Emacs can now use the native image API to display images.
|
||||
Emacs can now use the MS-Windows GDI+ library to load and display
|
||||
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 file is part of GNU Emacs.
|
||||
|
|
|
@ -231,6 +231,8 @@ See the documentation of `create-fontset-from-fontset-spec' for the format.")
|
|||
;;; Set default known names for external libraries
|
||||
(setq dynamic-library-alist
|
||||
(list
|
||||
'(gdiplus "gdiplus.dll")
|
||||
'(shlwapi "shlwapi.dll")
|
||||
'(xpm "libxpm.dll" "xpm4.dll" "libXpm-nox4.dll")
|
||||
;; Versions of libpng 1.4.x and later are incompatible with
|
||||
;; earlier versions. Set up the list of libraries according to
|
||||
|
|
163
src/image.c
163
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);
|
||||
bool (*valid_p) (Lisp_Object spec, Lisp_Object type);
|
||||
|
||||
/* 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);
|
||||
return type->valid_p (object, builtin_lisp_symbol (type->type));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -3144,12 +3144,12 @@ enum xbm_token
|
|||
displayed is used. */
|
||||
|
||||
static bool
|
||||
xbm_image_p (Lisp_Object object)
|
||||
xbm_image_p (Lisp_Object object, Lisp_Object type)
|
||||
{
|
||||
struct image_keyword kw[XBM_LAST];
|
||||
|
||||
memcpy (kw, xbm_format, sizeof kw);
|
||||
if (!parse_image_spec (object, kw, XBM_LAST, Qxbm))
|
||||
if (!parse_image_spec (object, kw, XBM_LAST, type))
|
||||
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));
|
||||
eassert (xbm_image_p (img->spec, Qxbm));
|
||||
|
||||
/* 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)
|
||||
xpm_image_p (Lisp_Object object, Lisp_Object type)
|
||||
{
|
||||
struct image_keyword fmt[XPM_LAST];
|
||||
memcpy (fmt, xpm_format, sizeof fmt);
|
||||
return (parse_image_spec (object, fmt, XPM_LAST, Qxpm)
|
||||
return (parse_image_spec (object, fmt, XPM_LAST, type)
|
||||
/* 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)
|
||||
pbm_image_p (Lisp_Object object, Lisp_Object type)
|
||||
{
|
||||
struct image_keyword fmt[PBM_LAST];
|
||||
|
||||
memcpy (fmt, pbm_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, PBM_LAST, Qpbm))
|
||||
if (!parse_image_spec (object, fmt, PBM_LAST, type))
|
||||
return 0;
|
||||
|
||||
/* Must specify either :data or :file. */
|
||||
|
@ -6231,6 +6231,83 @@ pbm_load (struct frame *f, struct image *img)
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
NATIVE IMAGE HANDLING
|
||||
***********************************************************************/
|
||||
#if defined(HAVE_NATIVE_IMAGE_API) && defined(HAVE_NTGUI)
|
||||
/*
|
||||
* 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,
|
||||
NATIVE_IMAGE_DATA,
|
||||
NATIVE_IMAGE_FILE,
|
||||
NATIVE_IMAGE_ASCENT,
|
||||
NATIVE_IMAGE_MARGIN,
|
||||
NATIVE_IMAGE_RELIEF,
|
||||
NATIVE_IMAGE_ALGORITHM,
|
||||
NATIVE_IMAGE_HEURISTIC_MASK,
|
||||
NATIVE_IMAGE_MASK,
|
||||
NATIVE_IMAGE_BACKGROUND,
|
||||
NATIVE_IMAGE_INDEX,
|
||||
NATIVE_IMAGE_LAST
|
||||
};
|
||||
|
||||
/* 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},
|
||||
{":data", IMAGE_STRING_VALUE, 0},
|
||||
{":file", IMAGE_STRING_VALUE, 0},
|
||||
{":ascent", IMAGE_ASCENT_VALUE, 0},
|
||||
{":margin", IMAGE_NON_NEGATIVE_INTEGER_VALUE_OR_PAIR, 0},
|
||||
{":relief", IMAGE_INTEGER_VALUE, 0},
|
||||
{":conversion", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
|
||||
{":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
|
||||
{":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
|
||||
{":background", IMAGE_STRING_OR_NIL_VALUE, 0},
|
||||
{":index", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}
|
||||
};
|
||||
|
||||
/* Return true if OBJECT is a valid native API image specification. */
|
||||
|
||||
static bool
|
||||
native_image_p (Lisp_Object object, Lisp_Object type)
|
||||
{
|
||||
struct image_keyword fmt[NATIVE_IMAGE_LAST];
|
||||
memcpy (fmt, native_image_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, 10, type))
|
||||
return 0;
|
||||
|
||||
/* Must specify either the :data or :file keyword. */
|
||||
return fmt[NATIVE_IMAGE_FILE].count + fmt[NATIVE_IMAGE_DATA].count == 1;
|
||||
}
|
||||
|
||||
static bool
|
||||
native_image_load (struct frame *f, struct image *img)
|
||||
{
|
||||
return w32_load_image (f, img,
|
||||
image_spec_value (img->spec, QCfile, NULL),
|
||||
image_spec_value (img->spec, QCdata, NULL));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
PNG
|
||||
|
@ -6275,12 +6352,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)
|
||||
png_image_p (Lisp_Object object, Lisp_Object type)
|
||||
{
|
||||
struct image_keyword fmt[PNG_LAST];
|
||||
memcpy (fmt, png_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, PNG_LAST, Qpng))
|
||||
if (!parse_image_spec (object, fmt, PNG_LAST, type))
|
||||
return 0;
|
||||
|
||||
/* Must specify either the :data or :file keyword. */
|
||||
|
@ -6890,7 +6967,6 @@ png_load (struct frame *f, struct image *img)
|
|||
image_spec_value (img->spec, QCdata, NULL));
|
||||
}
|
||||
|
||||
|
||||
#endif /* HAVE_NS */
|
||||
|
||||
|
||||
|
@ -6938,13 +7014,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)
|
||||
jpeg_image_p (Lisp_Object object, Lisp_Object type)
|
||||
{
|
||||
struct image_keyword fmt[JPEG_LAST];
|
||||
|
||||
memcpy (fmt, jpeg_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, JPEG_LAST, Qjpeg))
|
||||
if (!parse_image_spec (object, fmt, JPEG_LAST, type))
|
||||
return 0;
|
||||
|
||||
/* Must specify either the :data or :file keyword. */
|
||||
|
@ -7514,12 +7590,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)
|
||||
tiff_image_p (Lisp_Object object, Lisp_Object type)
|
||||
{
|
||||
struct image_keyword fmt[TIFF_LAST];
|
||||
memcpy (fmt, tiff_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, TIFF_LAST, Qtiff))
|
||||
if (!parse_image_spec (object, fmt, TIFF_LAST, type))
|
||||
return 0;
|
||||
|
||||
/* Must specify either the :data or :file keyword. */
|
||||
|
@ -7962,19 +8038,19 @@ 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)
|
||||
gif_image_p (Lisp_Object object, Lisp_Object type)
|
||||
{
|
||||
struct image_keyword fmt[GIF_LAST];
|
||||
memcpy (fmt, gif_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, GIF_LAST, Qgif))
|
||||
if (!parse_image_spec (object, fmt, GIF_LAST, type))
|
||||
return 0;
|
||||
|
||||
/* Must specify either the :data or :file keyword. */
|
||||
return fmt[GIF_FILE].count + fmt[GIF_DATA].count == 1;
|
||||
}
|
||||
|
||||
#endif /* HAVE_GIF */
|
||||
#endif /* HAVE_GIF || HAVE_NS */
|
||||
|
||||
#ifdef HAVE_GIF
|
||||
|
||||
|
@ -8574,12 +8650,12 @@ imagemagick_clear_image (struct frame *f,
|
|||
identify the IMAGEMAGICK format. */
|
||||
|
||||
static bool
|
||||
imagemagick_image_p (Lisp_Object object)
|
||||
imagemagick_image_p (Lisp_Object object, Lisp_Object type)
|
||||
{
|
||||
struct image_keyword fmt[IMAGEMAGICK_LAST];
|
||||
memcpy (fmt, imagemagick_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, IMAGEMAGICK_LAST, Qimagemagick))
|
||||
if (!parse_image_spec (object, fmt, IMAGEMAGICK_LAST, type))
|
||||
return 0;
|
||||
|
||||
/* Must specify either the :data or :file keyword. */
|
||||
|
@ -9369,12 +9445,12 @@ static const struct image_keyword svg_format[SVG_LAST] =
|
|||
identify the SVG format. */
|
||||
|
||||
static bool
|
||||
svg_image_p (Lisp_Object object)
|
||||
svg_image_p (Lisp_Object object, Lisp_Object type)
|
||||
{
|
||||
struct image_keyword fmt[SVG_LAST];
|
||||
memcpy (fmt, svg_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, SVG_LAST, Qsvg))
|
||||
if (!parse_image_spec (object, fmt, SVG_LAST, type))
|
||||
return 0;
|
||||
|
||||
/* Must specify either the :data or :file keyword. */
|
||||
|
@ -9837,7 +9913,7 @@ static const struct image_keyword gs_format[GS_LAST] =
|
|||
specification. */
|
||||
|
||||
static bool
|
||||
gs_image_p (Lisp_Object object)
|
||||
gs_image_p (Lisp_Object object, Lisp_Object type)
|
||||
{
|
||||
struct image_keyword fmt[GS_LAST];
|
||||
Lisp_Object tem;
|
||||
|
@ -9845,7 +9921,7 @@ gs_image_p (Lisp_Object object)
|
|||
|
||||
memcpy (fmt, gs_format, sizeof fmt);
|
||||
|
||||
if (!parse_image_spec (object, fmt, GS_LAST, Qpostscript))
|
||||
if (!parse_image_spec (object, fmt, GS_LAST, type))
|
||||
return 0;
|
||||
|
||||
/* Bounding box must be a list or vector containing 4 integers. */
|
||||
|
@ -10132,13 +10208,20 @@ static bool
|
|||
initialize_image_type (struct image_type const *type)
|
||||
{
|
||||
#ifdef WINDOWSNT
|
||||
Lisp_Object typesym = builtin_lisp_symbol (type->type);
|
||||
Lisp_Object tested = Fassq (typesym, Vlibrary_cache);
|
||||
Lisp_Object typesym, tested;
|
||||
bool (*init) (void) = type->init;
|
||||
|
||||
#ifdef HAVE_NATIVE_IMAGE_API
|
||||
if (init == init_native_image_functions)
|
||||
return init();
|
||||
#endif
|
||||
|
||||
typesym = builtin_lisp_symbol (type->type);
|
||||
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 ();
|
||||
|
@ -10165,6 +10248,16 @@ 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) },
|
||||
|
@ -10199,7 +10292,13 @@ lookup_image_type (Lisp_Object type)
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
@ -10316,22 +10415,22 @@ non-numeric, there is no explicit limit on the size of images. */);
|
|||
add_image_type (Qxpm);
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_JPEG) || defined (HAVE_NS)
|
||||
#if defined (HAVE_JPEG) || defined (HAVE_NS) || defined (HAVE_NATIVE_IMAGE_API)
|
||||
DEFSYM (Qjpeg, "jpeg");
|
||||
add_image_type (Qjpeg);
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_TIFF) || defined (HAVE_NS)
|
||||
#if defined (HAVE_TIFF) || defined (HAVE_NS) || defined (HAVE_NATIVE_IMAGE_API)
|
||||
DEFSYM (Qtiff, "tiff");
|
||||
add_image_type (Qtiff);
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_GIF) || defined (HAVE_NS)
|
||||
#if defined (HAVE_GIF) || defined (HAVE_NS) || defined (HAVE_NATIVE_IMAGE_API)
|
||||
DEFSYM (Qgif, "gif");
|
||||
add_image_type (Qgif);
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_PNG) || defined (HAVE_NS)
|
||||
#if defined (HAVE_PNG) || defined (HAVE_NS) || defined(HAVE_NATIVE_IMAGE_API)
|
||||
DEFSYM (Qpng, "png");
|
||||
add_image_type (Qpng);
|
||||
#endif
|
||||
|
|
|
@ -10225,6 +10225,10 @@ term_ntproc (int ignored)
|
|||
term_winsock ();
|
||||
|
||||
term_w32select ();
|
||||
|
||||
#ifdef HAVE_GDIPLUS
|
||||
w32_gdiplus_shutdown ();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
|
306
src/w32image.c
Normal file
306
src/w32image.c
Normal file
|
@ -0,0 +1,306 @@
|
|||
/* Implementation of MS-Windows native image API via the GDI+ library.
|
||||
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
GNU Emacs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
GNU Emacs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Juan Jose Garcia-Ripoll <juanjose.garciaripoll@gmail.com>. */
|
||||
|
||||
#include <config.h>
|
||||
#include "lisp.h"
|
||||
#include "dispextern.h"
|
||||
#define COBJMACROS
|
||||
#include <objidl.h>
|
||||
#include <wtypes.h>
|
||||
#include <gdiplus.h>
|
||||
#include <shlwapi.h>
|
||||
#include "w32common.h"
|
||||
#include "w32term.h"
|
||||
#include "frame.h"
|
||||
#include "coding.h"
|
||||
|
||||
/*#define LINK_GDIPLUS_STATICALLY 1*/
|
||||
|
||||
#ifndef LINK_GDIPLUS_STATICALLY
|
||||
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, 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, 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, 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
LOAD_DLL_FN (gdiplus_lib, GdiplusStartup);
|
||||
LOAD_DLL_FN (gdiplus_lib, GdiplusShutdown);
|
||||
LOAD_DLL_FN (gdiplus_lib, GdipGetPropertyItemSize);
|
||||
LOAD_DLL_FN (gdiplus_lib, GdipGetPropertyItem);
|
||||
LOAD_DLL_FN (gdiplus_lib, GdipImageGetFrameDimensionsCount);
|
||||
LOAD_DLL_FN (gdiplus_lib, GdipImageGetFrameDimensionsList);
|
||||
LOAD_DLL_FN (gdiplus_lib, GdipImageGetFrameCount);
|
||||
LOAD_DLL_FN (gdiplus_lib, GdipImageSelectActiveFrame);
|
||||
LOAD_DLL_FN (gdiplus_lib, GdipCreateBitmapFromFile);
|
||||
LOAD_DLL_FN (gdiplus_lib, GdipCreateBitmapFromStream);
|
||||
LOAD_DLL_FN (gdiplus_lib, GdipCreateHBITMAPFromBitmap);
|
||||
LOAD_DLL_FN (gdiplus_lib, GdipDisposeImage);
|
||||
LOAD_DLL_FN (gdiplus_lib, GdipGetImageHeight);
|
||||
LOAD_DLL_FN (gdiplus_lib, GdipGetImageWidth);
|
||||
LOAD_DLL_FN (shlwapi_lib, SHCreateMemStream);
|
||||
|
||||
# define GdiplusStartup fn_GdiplusStartup
|
||||
# define GdiplusShutdown fn_GdiplusShutdown
|
||||
# define GdipGetPropertyItemSize fn_GdipGetPropertyItemSize
|
||||
# define GdipGetPropertyItem fn_GdipGetPropertyItem
|
||||
# define GdipImageGetFrameDimensionsCount fn_GdipImageGetFrameDimensionsCount
|
||||
# define GdipImageGetFrameDimensionsList fn_GdipImageGetFrameDimensionsList
|
||||
# define GdipImageGetFrameCount fn_GdipImageGetFrameCount
|
||||
# define GdipImageSelectActiveFrame fn_GdipImageSelectActiveFrame
|
||||
# define GdipCreateBitmapFromFile fn_GdipCreateBitmapFromFile
|
||||
# define GdipCreateBitmapFromStream fn_GdipCreateBitmapFromStream
|
||||
# define SHCreateMemStream fn_SHCreateMemStream
|
||||
# define GdipCreateHBITMAPFromBitmap fn_GdipCreateHBITMAPFromBitmap
|
||||
# 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;
|
||||
|
||||
status = GdiplusStartup (&token, &input, &output);
|
||||
if (status == Ok)
|
||||
{
|
||||
gdip_initialized = 1;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
gdip_initialized = -1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
w32_gdiplus_shutdown (void)
|
||||
{
|
||||
GdiplusShutdown (token);
|
||||
}
|
||||
|
||||
|
||||
static double
|
||||
w32_frame_delay (GpBitmap *pBitmap, int frame)
|
||||
{
|
||||
UINT size;
|
||||
PropertyItem *propertyItem;
|
||||
double delay = 0.0;
|
||||
|
||||
/* Assume that the image has a property item of type PropertyItemEquipMake.
|
||||
Get the size of that property item. */
|
||||
GdipGetPropertyItemSize (pBitmap, PropertyTagFrameDelay, &size);
|
||||
|
||||
/* Allocate a buffer to receive the property item. */
|
||||
propertyItem = (PropertyItem*)malloc (size);
|
||||
if (propertyItem != NULL)
|
||||
{
|
||||
/* Get the property item. */
|
||||
GdipGetPropertyItem (pBitmap, PropertyTagFrameDelay, size, propertyItem);
|
||||
delay = ((double)propertyItem[frame].length) / 100;
|
||||
if (delay == 0)
|
||||
{
|
||||
/* In GIF files, unfortunately, delay is only specified for the first
|
||||
frame. */
|
||||
delay = ((double)propertyItem[0].length) / 100;
|
||||
}
|
||||
free (propertyItem);
|
||||
}
|
||||
return delay;
|
||||
}
|
||||
|
||||
static UINT
|
||||
w32_select_active_frame (GpBitmap *pBitmap, int frame, int *nframes, double *delay)
|
||||
{
|
||||
UINT count, frameCount;
|
||||
GUID pDimensionIDs[1];
|
||||
GpStatus status = Ok;
|
||||
|
||||
status = GdipImageGetFrameDimensionsCount (pBitmap, &count);
|
||||
frameCount = *nframes = 0;
|
||||
*delay = 0.0;
|
||||
if (count)
|
||||
{
|
||||
status = GdipImageGetFrameDimensionsList (pBitmap, pDimensionIDs, 1);
|
||||
status = GdipImageGetFrameCount (pBitmap, &pDimensionIDs[0], &frameCount);
|
||||
if ((status == Ok) && (frameCount > 1))
|
||||
{
|
||||
if (frame < 0 || frame >= frameCount)
|
||||
{
|
||||
status = GenericError;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = GdipImageSelectActiveFrame (pBitmap, &pDimensionIDs[0], frame);
|
||||
*delay = w32_frame_delay (pBitmap, frame);
|
||||
*nframes = frameCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
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);
|
||||
Emacs_Color color;
|
||||
|
||||
/* If the user specified a color, try to use it; if not, use the
|
||||
current frame background, ignoring any default background
|
||||
color set by the image. */
|
||||
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. */
|
||||
{
|
||||
DWORD red = (((DWORD) color.red) & 0xff00) << 8;
|
||||
DWORD green = ((DWORD) color.green) & 0xff00;
|
||||
DWORD blue = ((DWORD) color.blue) >> 8;
|
||||
return red | green | blue;
|
||||
}
|
||||
return ((DWORD) 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;
|
||||
|
||||
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. */
|
||||
if (STRINGP (spec_file))
|
||||
{
|
||||
if (w32_unicode_filenames)
|
||||
{
|
||||
filename_to_utf16 (SSDATA (spec_file) , filename);
|
||||
status = GdipCreateBitmapFromFile (filename, &pBitmap);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_to_log ("GDI+ requires w32-unicode-filenames to be T");
|
||||
status = GenericError;
|
||||
}
|
||||
}
|
||||
else if (STRINGP (spec_data))
|
||||
{
|
||||
IStream *pStream = SHCreateMemStream ((BYTE *) SSDATA (spec_data),
|
||||
SBYTES (spec_data));
|
||||
if (pStream != NULL)
|
||||
{
|
||||
status = GdipCreateBitmapFromStream (pStream, &pBitmap);
|
||||
IStream_Release (pStream);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
status = w32_select_active_frame (pBitmap, index, &nframes, &delay);
|
||||
if ((status == Ok))
|
||||
{
|
||||
if (nframes > 1)
|
||||
metadata = Fcons (Qcount, Fcons (make_fixnum (nframes), metadata));
|
||||
if (delay)
|
||||
metadata = Fcons (Qdelay, Fcons (make_float (delay), metadata));
|
||||
}
|
||||
}
|
||||
|
||||
if (status == Ok)
|
||||
{
|
||||
bg_color = w32_image_bg_color (f, img);
|
||||
status = GdipCreateHBITMAPFromBitmap (pBitmap, &pixmap, bg_color);
|
||||
if (status == Ok)
|
||||
{
|
||||
UINT width, height;
|
||||
GdipGetImageWidth (pBitmap, &width);
|
||||
GdipGetImageHeight (pBitmap, &height);
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
img->pixmap = pixmap;
|
||||
img->lisp_data = metadata;
|
||||
}
|
||||
|
||||
GdipDisposeImage (pBitmap);
|
||||
}
|
||||
|
||||
if (status != Ok)
|
||||
{
|
||||
add_to_log ("Unable to load image %s", img->spec);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
|
@ -1529,7 +1529,7 @@ w32_query_colors (struct frame *f, Emacs_Color *colors, int ncolors)
|
|||
|
||||
/* Store F's background color into *BGCOLOR. */
|
||||
|
||||
static void
|
||||
void
|
||||
w32_query_frame_background_color (struct frame *f, Emacs_Color *bgcolor)
|
||||
{
|
||||
bgcolor->pixel = FRAME_BACKGROUND_PIXEL (f);
|
||||
|
|
|
@ -75,7 +75,10 @@ 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. */
|
||||
|
@ -248,6 +251,8 @@ extern int w32_display_pixel_height (struct w32_display_info *);
|
|||
extern int w32_display_pixel_width (struct w32_display_info *);
|
||||
extern void initialize_frame_menubar (struct frame *);
|
||||
extern void w32_dialog_in_progress (Lisp_Object in_progress);
|
||||
extern void w32_query_frame_background_color (struct frame *f,
|
||||
Emacs_Color *bgcolor);
|
||||
|
||||
extern void w32_make_frame_visible (struct frame *f);
|
||||
extern void w32_make_frame_invisible (struct frame *f);
|
||||
|
|
Loading…
Add table
Reference in a new issue