From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.ciao.gmane.io!not-for-mail From: Juan =?utf-8?Q?Jos=C3=A9_Garc=C3=ADa-Ripoll?= Newsgroups: gmane.emacs.devel Subject: Re: GDI+ take 3 Date: Mon, 13 Apr 2020 12:04:39 +0200 Message-ID: <865ze3wvk8.fsf@csic.es> References: <86h7xzc5ai.fsf@csic.es> <83d08mt7gy.fsf@gnu.org> <83eesrkivc.fsf@gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="ciao.gmane.io:159.69.161.202"; logging-data="34251"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.3 (windows-nt) To: emacs-devel@gnu.org Cancel-Lock: sha1:XLFxYpfav0S9P0c0RSUn7j61jIY= Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Mon Apr 13 12:05:29 2020 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1jNvy4-0008gT-J0 for ged-emacs-devel@m.gmane-mx.org; Mon, 13 Apr 2020 12:05:28 +0200 Original-Received: from localhost ([::1]:42400 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jNvy3-0006kM-MM for ged-emacs-devel@m.gmane-mx.org; Mon, 13 Apr 2020 06:05:27 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:52932) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jNvxT-00065W-QY for emacs-devel@gnu.org; Mon, 13 Apr 2020 06:04:55 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1jNvxQ-0001pf-Ba for emacs-devel@gnu.org; Mon, 13 Apr 2020 06:04:51 -0400 Original-Received: from ciao.gmane.io ([159.69.161.202]:34004) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1jNvxP-0001ov-VC for emacs-devel@gnu.org; Mon, 13 Apr 2020 06:04:48 -0400 Original-Received: from list by ciao.gmane.io with local (Exim 4.92) (envelope-from ) id 1jNvxN-0007js-Si for emacs-devel@gnu.org; Mon, 13 Apr 2020 12:04:45 +0200 X-Injected-Via-Gmane: http://gmane.org/ X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 159.69.161.202 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.io gmane.emacs.devel:246907 Archived-At: --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Eli Zaretskii writes: > I'd like to install these changes, but some parts seem to be missing, > see above. Could you please post the missing parts (or the entire > changeset, if that is more convenient)? Apologies for the corrupted patch. Here it goes, I hope, in a complete form. -- Juan José García Ripoll http://juanjose.garciaripoll.com http://quinfog.hbar.es --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=patch-gdi+-v3.diff diff --git a/configure.ac b/configure.ac index 9907160482..41a1860493 100644 --- a/configure.ac +++ b/configure.ac @@ -433,6 +433,7 @@ AC_DEFUN 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 @@ AC_DEFUN 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 @@ AC_DEFUN # 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 @@ AC_DEFUN 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 7a7f11f507..847ecffec2 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -372,6 +372,12 @@ coding-system-for-{read,write} or call 'set-process-coding-system'. 'module-file-suffix' now has the value ".dylib" on macOS, but the ".so" suffix is supported as well. ++++ +** Emacs can now use Microsoft Windows GDI+ library to load bitmap images in +JPEG, PNG, GIF and TIFF formats. This support is enabled with +--with-native-image-api, which automatically disables the use of 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 3e932c7593..5901e0295e 100644 --- a/lisp/term/w32-win.el +++ b/lisp/term/w32-win.el @@ -231,6 +231,8 @@ libgnutls-version ;;; 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 c98ca291ca..ff2d12fa1a 100644 --- a/src/image.c +++ b/src/image.c @@ -751,7 +751,7 @@ x_create_bitmap_mask (struct frame *f, ptrdiff_t id) /* 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 @@ slurp_file (int fd, ptrdiff_t *size) 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 @@ image_build_heuristic_mask (struct frame *f, struct image *img, /* 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 @@ pbm_load (struct frame *f, struct image *img) /* 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 @@ png_load (struct frame *f, struct image *img) /* 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 @@ jpeg_load (struct frame *f, struct image *img) /* 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 @@ DEFUN ("imagemagick-types", Fimagemagick_types, Simagemagick_types, 0, 0, 0, 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 @@ #define HAVE_GHOSTSCRIPT 1 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 @@ DEFUN ("init-image-library", Finit_image_library, Sinit_image_library, 1, 1, 0, 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 @@ initialize_image_type (struct image_type const *type) { 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 @@ syms_of_image (void) 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 698e10e234..1d2a52b6df 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 0000000000..28aa8637b6 --- /dev/null +++ b/src/w32image.c @@ -0,0 +1,304 @@ +/* Implementation of GUI terminal on the Microsoft Windows API. + +Copyright (C) 1989, 1993-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 . */ + +#include +#include "lisp.h" +#include "dispextern.h" +#define COBJMACROS +#include +#include +#include +#include +#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 5fa77d58e1..f19754df02 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 f8a8a727e8..d44c6f9b83 100644 --- a/src/w32term.h +++ b/src/w32term.h @@ -75,7 +75,10 @@ #define CP_DEFAULT 1004 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 @@ #define CP_DEFAULT 1004 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); --=-=-=--