[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
feature/native-comp 208a11d 3/3: Merge remote-tracking branch 'savannah/
From: |
Andrea Corallo |
Subject: |
feature/native-comp 208a11d 3/3: Merge remote-tracking branch 'savannah/master' into HEAD |
Date: |
Tue, 14 Apr 2020 13:52:04 -0400 (EDT) |
branch: feature/native-comp
commit 208a11d3f0ede17b29da45c2491b703b6942a764
Merge: 8db8c85 6bf79d6
Author: Andrea Corallo <address@hidden>
Commit: Andrea Corallo <address@hidden>
Merge remote-tracking branch 'savannah/master' into HEAD
---
configure.ac | 10 ++
etc/NEWS | 12 ++
lisp/hi-lock.el | 4 +-
lisp/term/w32-win.el | 2 +
lisp/vc/vc.el | 34 +++--
src/image.c | 129 +++++++++++++++-
src/w32.c | 4 +
src/w32gui.h | 6 +
src/w32image.c | 368 +++++++++++++++++++++++++++++++++++++++++++++
src/w32term.c | 21 ++-
src/w32term.h | 3 +-
test/lisp/hi-lock-tests.el | 62 +++++++-
12 files changed, 628 insertions(+), 27 deletions(-)
diff --git a/configure.ac b/configure.ac
index 49b972a..7b97203 100644
--- a/configure.ac
+++ b/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_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,6 +2128,7 @@ LIB_WSOCK32=
NTLIB=
CM_OBJ="cm.o"
XARGS_LIMIT=
+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,6 +2158,13 @@ if test "${HAVE_W32}" = "yes"; then
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 native OS APIs for
images.])
+ NATIVE_IMAGE_API="yes (w32)"
+ W32_OBJ="$W32_OBJ w32image.o"
+ fi
W32_LIBS="$W32_LIBS -lwinmm -lusp10 -lgdi32 -lcomdlg32"
W32_LIBS="$W32_LIBS -lmpr -lwinspool -lole32 -lcomctl32"
W32_RES_LINK="\$(EMACSRES)"
@@ -5731,6 +5740,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 APIs for images? ${NATIVE_IMAGE_API}
Does Emacs support sound? ${HAVE_SOUND}
Does Emacs use -lgpm? ${HAVE_GPM}
Does Emacs use -ldbus? ${HAVE_DBUS}
diff --git a/etc/NEWS b/etc/NEWS
index 46f59ab..1bfaf65 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -386,6 +386,18 @@ 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 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.
diff --git a/lisp/hi-lock.el b/lisp/hi-lock.el
index 1d8dc06..bf79e48 100644
--- a/lisp/hi-lock.el
+++ b/lisp/hi-lock.el
@@ -681,8 +681,8 @@ then remove all hi-lock highlighting."
(delq keyword hi-lock-interactive-patterns))
(remove-overlays
nil nil 'hi-lock-overlay-regexp
- (hi-lock--hashcons (or (car (rassq keyword
hi-lock-interactive-lighters))
- (car keyword))))
+ (or (car (rassq keyword hi-lock-interactive-lighters))
+ (hi-lock--hashcons (car keyword))))
(setq hi-lock-interactive-lighters
(rassq-delete-all keyword hi-lock-interactive-lighters))
(font-lock-flush))))
diff --git a/lisp/term/w32-win.el b/lisp/term/w32-win.el
index 3e932c7..5901e02 100644
--- a/lisp/term/w32-win.el
+++ b/lisp/term/w32-win.el
@@ -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
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index d4323d5..c640ba0 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -1012,23 +1012,33 @@ Within directories, only files already under version
control are noticed."
allow-unregistered
state-model-only-files)
"Deduce a set of files and a backend to which to apply an operation.
-Return (BACKEND FILESET FILESET-ONLY-FILES STATE CHECKOUT-MODEL).
+Return a list of the form:
-NOT-STATE-CHANGING if non-nil, means that the operation
-requesting the fileset doesn't intend to change VC state,
-such as printing the log or showing the diff.
+ (BACKEND FILESET FILESET-ONLY-FILES STATE CHECKOUT-MODEL)
-If we're in VC-dir mode, FILESET is the list of marked files,
-or the directory if no files are marked.
-Otherwise, if in a buffer visiting a version-controlled file,
-FILESET is a single-file fileset containing that file.
+where the last 3 members are optional, and must be present only if
+STATE-MODEL-ONLY-FILES is non-nil.
+
+NOT-STATE-CHANGING, if non-nil, means that the operation
+requesting the fileset doesn't intend to change the VC state,
+such as when printing the log or showing the diffs.
+
+If the current buffer is in `vc-dir' or Dired mode, FILESET is the
+list of marked files, or the current directory if no files are
+marked.
+Otherwise, if the current buffer is visiting a version-controlled
+file, FILESET is a single-file list containing that file's name.
Otherwise, if ALLOW-UNREGISTERED is non-nil and the visited file
-is unregistered, FILESET is a single-file fileset containing it.
+is unregistered, FILESET is a single-file list containing the
+name of the visited file.
Otherwise, throw an error.
-STATE-MODEL-ONLY-FILES if non-nil, means that the caller needs
-the FILESET-ONLY-FILES STATE and MODEL info. Otherwise, that
-part may be skipped.
+STATE-MODEL-ONLY-FILES, if non-nil, means that the caller needs
+the FILESET-ONLY-FILES, STATE, and CHECKOUT-MODEL info, where
+FILESET-ONLY-FILES means only files in similar VC states,
+possible values of STATE are explained in `vc-state', and MODEL in
+`vc-checkout-model'. Otherwise, these 3 members may be omitted from
+the returned list.
BEWARE: this function may change the current buffer."
(let (backend)
diff --git a/src/image.c b/src/image.c
index c98ca29..aacaf0b 100644
--- a/src/image.c
+++ b/src/image.c
@@ -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;
}
@@ -6233,6 +6233,97 @@ pbm_load (struct frame *f, struct image *img)
/***********************************************************************
+ NATIVE IMAGE HANDLING
+ ***********************************************************************/
+
+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.
+ */
+
+/* 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)
+{
+ struct image_keyword fmt[NATIVE_IMAGE_LAST];
+ memcpy (fmt, native_image_format, sizeof fmt);
+
+ if (!parse_image_spec (object, fmt, 10, Qnative_image))
+ 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)
+{
+
+# 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 /* HAVE_NATIVE_IMAGE_API */
+
+
+/***********************************************************************
PNG
***********************************************************************/
@@ -6890,7 +6981,6 @@ png_load (struct frame *f, struct image *img)
image_spec_value (img->spec, QCdata, NULL));
}
-
#endif /* HAVE_NS */
@@ -7974,7 +8064,7 @@ gif_image_p (Lisp_Object object)
return fmt[GIF_FILE].count + fmt[GIF_DATA].count == 1;
}
-#endif /* HAVE_GIF */
+#endif /* HAVE_GIF || HAVE_NS */
#ifdef HAVE_GIF
@@ -10133,6 +10223,10 @@ initialize_image_type (struct image_type const *type)
{
#ifdef WINDOWSNT
Lisp_Object typesym = builtin_lisp_symbol (type->type);
+
+ if (image_can_use_native_api (typesym))
+ return true;
+
Lisp_Object tested = Fassq (typesym, Vlibrary_cache);
/* If we failed to load the library before, don't try again. */
if (CONSP (tested))
@@ -10189,12 +10283,23 @@ static struct image_type const image_types[] =
{ SYMBOL_INDEX (Qpbm), pbm_image_p, pbm_load, image_clear_image },
};
+#if 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)
{
+#if 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];
@@ -10316,22 +10421,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
@@ -10355,6 +10460,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);
diff --git a/src/w32.c b/src/w32.c
index 698e10e..8017802 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -10225,6 +10225,10 @@ term_ntproc (int ignored)
term_winsock ();
term_w32select ();
+
+#if HAVE_NATIVE_IMAGE_API
+ w32_gdiplus_shutdown ();
+#endif
}
void
diff --git a/src/w32gui.h b/src/w32gui.h
index 5cc6428..dfec1f0 100644
--- a/src/w32gui.h
+++ b/src/w32gui.h
@@ -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;
diff --git a/src/w32image.c b/src/w32image.c
new file mode 100644
index 0000000..6a3e37c
--- /dev/null
+++ b/src/w32image.c
@@ -0,0 +1,368 @@
+/* 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 <address@hidden>. */
+
+#include <config.h>
+#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>
+#include "w32common.h"
+#include "w32term.h"
+#include "frame.h"
+#include "coding.h"
+
+#ifdef WINDOWSNT
+
+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 *));
+
+static bool
+gdiplus_init (void)
+{
+ HANDLE gdiplus_lib, shlwapi_lib;
+
+ 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);
+ 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); */
+
+ /* 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
+# 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 /* 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
+ if (gdip_initialized > 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)
+{
+ 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)
+{
+ 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 = malloc (size);
+ if (propertyItem != NULL)
+ {
+ /* Get the property item. */
+ GdipGetPropertyItem (pBitmap, PropertyTagFrameDelay, size, propertyItem);
+ delay = propertyItem[frame].length / 100.0;
+ if (delay == 0)
+ {
+ /* In GIF files, unfortunately, delay is only specified for the first
+ frame. */
+ delay = propertyItem[0].length / 100.0;
+ }
+ free (propertyItem);
+ }
+ return delay;
+}
+
+static GpStatus
+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)
+{
+ 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 (ARGB) (red | green | blue);
+ }
+ return (ARGB) 0xff000000;
+}
+
+int
+w32_load_image (struct frame *f, struct image *img,
+ Lisp_Object spec_file, Lisp_Object spec_data)
+{
+ GpStatus status = GenericError;
+ GpBitmap *pBitmap;
+ Lisp_Object metadata;
+
+ eassert (valid_image_p (img->spec));
+
+ /* 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)
+ {
+ wchar_t filename[MAX_PATH];
+ 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 *) SDATA (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 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 (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)
+ {
+ ARGB bg_color = w32_image_bg_color (f, img);
+ Emacs_Pixmap pixmap;
+
+ 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;
+}
diff --git a/src/w32term.c b/src/w32term.c
index 5fa77d5..108cb79 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -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);
@@ -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. */
diff --git a/src/w32term.h b/src/w32term.h
index 4e9234f..8ba2480 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -75,7 +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);
-
/* For each display (currently only one on w32), we have a structure that
records information about it. */
@@ -248,6 +247,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);
diff --git a/test/lisp/hi-lock-tests.el b/test/lisp/hi-lock-tests.el
index 252caaa..59f3e73 100644
--- a/test/lisp/hi-lock-tests.el
+++ b/test/lisp/hi-lock-tests.el
@@ -33,9 +33,7 @@
(car defaults))))
(dotimes (_ 2)
(let ((face (hi-lock-read-face-name)))
- ;; This test should use regexp "b" different from "a"
- ;; used in another test because hi-lock--hashcons is global.
- (hi-lock-set-pattern "b" face))))
+ (hi-lock-set-pattern "a" face))))
(should (equal hi-lock--unused-faces (cdr faces))))))
(ert-deftest hi-lock-test-set-pattern ()
@@ -148,5 +146,63 @@
(call-interactively 'unhighlight-regexp))
(should (null (get-text-property 1 'face))))))
+(ert-deftest hi-lock-unhighlight ()
+ "Test for unhighlighting and `hi-lock--regexps-at-point'."
+ (let ((hi-lock-auto-select-face t))
+ (with-temp-buffer
+ (insert "aAbB\n")
+
+ (cl-letf (((symbol-function 'completing-read)
+ (lambda (_prompt _coll _x _y _z _hist defaults)
+ (car defaults))))
+
+ (highlight-regexp "a")
+ (highlight-regexp "b")
+ (should (= (length (overlays-in (point-min) (point-max))) 4))
+ ;; `hi-lock--regexps-at-point' should take regexp "a" at point 1,
+ ;; not the last regexp "b"
+ (goto-char 1)
+ (call-interactively 'unhighlight-regexp)
+ (should (= (length (overlays-in 1 3)) 0))
+ (should (= (length (overlays-in 3 5)) 2))
+ ;; Next call should unhighlight remaining regepxs
+ (call-interactively 'unhighlight-regexp)
+ (should (= (length (overlays-in 3 5)) 0))
+
+ ;; Test unhighlight all
+ (highlight-regexp "a")
+ (highlight-regexp "b")
+ (should (= (length (overlays-in (point-min) (point-max))) 4))
+ (unhighlight-regexp t)
+ (should (= (length (overlays-in (point-min) (point-max))) 0))
+
+ (emacs-lisp-mode)
+ (setq font-lock-mode t)
+
+ (highlight-regexp "a")
+ (highlight-regexp "b")
+ (font-lock-ensure)
+ (should (memq 'hi-yellow (get-text-property 1 'face)))
+ (should (memq 'hi-yellow (get-text-property 3 'face)))
+ ;; `hi-lock--regexps-at-point' should take regexp "a" at point 1,
+ ;; not the last regexp "b"
+ (goto-char 1)
+ (let ((font-lock-fontified t)) (call-interactively
'unhighlight-regexp))
+ (should (null (get-text-property 1 'face)))
+ (should (memq 'hi-yellow (get-text-property 3 'face)))
+ ;; Next call should unhighlight remaining regepxs
+ (let ((font-lock-fontified t)) (call-interactively
'unhighlight-regexp))
+ (should (null (get-text-property 3 'face)))
+
+ ;; Test unhighlight all
+ (highlight-regexp "a")
+ (highlight-regexp "b")
+ (font-lock-ensure)
+ (should (memq 'hi-yellow (get-text-property 1 'face)))
+ (should (memq 'hi-yellow (get-text-property 3 'face)))
+ (let ((font-lock-fontified t)) (unhighlight-regexp t))
+ (should (null (get-text-property 1 'face)))
+ (should (null (get-text-property 3 'face)))))))
+
(provide 'hi-lock-tests)
;;; hi-lock-tests.el ends here