emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master df254a7 1/3: Initial version of native image API support for MS-W


From: Eli Zaretskii
Subject: master df254a7 1/3: Initial version of native image API support for MS-Windows
Date: Tue, 14 Apr 2020 11:22:22 -0400 (EDT)

branch: master
commit df254a7445a86dc25d133f2d79be8096190a8b96
Author: Juan José García-Ripoll <address@hidden>
Commit: Eli Zaretskii <address@hidden>

    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.
---
 configure.ac         |  10 +-
 etc/NEWS             |   7 ++
 lisp/term/w32-win.el |   2 +
 src/image.c          | 163 +++++++++++++++++++++------
 src/w32.c            |   4 +
 src/w32image.c       | 306 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/w32term.c        |   2 +-
 src/w32term.h        |   7 +-
 8 files changed, 466 insertions(+), 35 deletions(-)

diff --git a/configure.ac b/configure.ac
index 9907160..41a1860 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_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}
diff --git a/etc/NEWS b/etc/NEWS
index 46f59ab..7ed24e3 100644
--- a/etc/NEWS
+++ b/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.
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/src/image.c b/src/image.c
index c98ca29..ff2d12f 100644
--- a/src/image.c
+++ b/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.  */
@@ -6233,6 +6233,83 @@ pbm_load (struct frame *f, struct image *img)
 
 
 /***********************************************************************
+                           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
diff --git a/src/w32.c b/src/w32.c
index 698e10e..1d2a52b 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -10225,6 +10225,10 @@ term_ntproc (int ignored)
   term_winsock ();
 
   term_w32select ();
+
+#ifdef HAVE_GDIPLUS
+  w32_gdiplus_shutdown ();
+#endif
 }
 
 void
diff --git a/src/w32image.c b/src/w32image.c
new file mode 100644
index 0000000..fe32660
--- /dev/null
+++ b/src/w32image.c
@@ -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 <address@hidden>.  */
+
+#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;
+}
diff --git a/src/w32term.c b/src/w32term.c
index 5fa77d5..f19754d 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);
diff --git a/src/w32term.h b/src/w32term.h
index 4e9234f..7ca00d0 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -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);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]