diff --git a/configure.ac b/configure.ac index a4daf14..4384bc9 100644 --- a/configure.ac +++ b/configure.ac @@ -2132,6 +2132,7 @@ AC_DEFUN NTLIB= CM_OBJ="cm.o" XARGS_LIMIT= +HAVE_GDIPLUS=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 @@ -2160,9 +2161,11 @@ 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" - W32_LIBS="$W32_LIBS -lwinmm -lusp10 -lgdi32 -lcomdlg32" - W32_LIBS="$W32_LIBS -lmpr -lwinspool -lole32 -lcomctl32" + AC_DEFINE(HAVE_GDIPLUS, 1, [Define to use MS Windows GDI+ for images.]) + HAVE_GDIPLUS=yes + W32_OBJ="$W32_OBJ w32.o w32console.o w32heap.o w32inevt.o w32proc.o w32image.o" + W32_LIBS="$W32_LIBS -lwinmm -lusp10 -lgdiplus -lgdi32 -lcomdlg32" + W32_LIBS="$W32_LIBS -lmpr -lwinspool -lshlwapi -lole32 -lcomctl32" W32_RES_LINK="\$(EMACSRES)" CLIENTRES="emacsclient.res" CLIENTW="emacsclientw\$(EXEEXT)" @@ -3572,8 +3575,8 @@ AC_DEFUN ### Use -ljpeg if available, unless '--with-jpeg=no'. HAVE_JPEG=no LIBJPEG= -if test "${NS_IMPL_COCOA}" = yes; then - : # Cocoa provides its own jpeg support, so do nothing. +if test "${NS_IMPL_COCOA}" = yes || test "${HAVE_GDIPLUS}" = "yes"; then + : # Cocoa and Windows' GDI+ provide their own jpeg support, so do nothing. elif test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes"; then if test "${with_jpeg}" != "no"; then AC_CACHE_CHECK([for jpeglib 6b or later], @@ -3723,8 +3726,8 @@ AC_DEFUN HAVE_PNG=no LIBPNG= PNG_CFLAGS= -if test "${NS_IMPL_COCOA}" = yes; then - : # Cocoa provides its own png support, so do nothing. +if test "${NS_IMPL_COCOA}" = yes || test "${HAVE_GDIPLUS}" = "yes"; then + : # Cocoa and Windows' GDI+ provide their own png support, so do nothing. elif test "${with_png}" != no; then # mingw32 loads the library dynamically. if test "$opsys" = mingw32; then @@ -3796,7 +3799,9 @@ AC_DEFUN ### mingw32 doesn't use -ltiff, since it loads the library dynamically. HAVE_TIFF=no LIBTIFF= -if test "${opsys}" = "mingw32"; then +if test "${HAVE_GDIPLUS}" = "yes"; then + : # Windows' GDI+ supports TIFF +elif test "${opsys}" = "mingw32"; then if test "${with_tiff}" != "no"; then AC_CHECK_HEADER(tiffio.h, HAVE_TIFF=yes, HAVE_TIFF=no) fi @@ -3824,7 +3829,9 @@ AC_DEFUN ### mingw32 doesn't use -lgif/-lungif, since it loads the library dynamically. HAVE_GIF=no LIBGIF= -if test "${opsys}" = "mingw32"; then +if test "${HAVE_GDIPLUS}" = "yes"; then + : # Windows' GDI+ supports TIFF +elif test "${opsys}" = "mingw32"; then if test "${with_gif}" != "no"; then AC_CHECK_HEADER(gif_lib.h, HAVE_GIF=yes, HAVE_GIF=no) fi diff --git a/src/Makefile.in b/src/Makefile.in index 552dd2e..0dc6133 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -280,10 +280,12 @@ GNU_OBJC_CFLAGS= ## w32fns.o w32menu.c w32reg.o fringe.o fontset.o w32font.o w32term.o ## w32xfns.o w32select.o image.o w32uniscribe.o w32cygwinx.o if HAVE_W32, ## w32cygwinx.o if CYGWIN but not HAVE_W32, else empty. +## w32image.o if we use GDI+ W32_OBJ=@W32_OBJ@ ## -lkernel32 -luser32 -lusp10 -lgdi32 -lole32 -lcomdlg32 -lcomctl32 ## -lwinspool if HAVE_W32, ## -lkernel32 if CYGWIN but not HAVE_W32, else empty. +## -lshlwapi if we use GDI+ W32_LIBS=@W32_LIBS@ ## emacs.res if HAVE_W32 @@ -435,7 +437,7 @@ SOME_MACHINE_OBJECTS = fontset.o dbusbind.o cygw32.o \ nsterm.o nsfns.o nsmenu.o nsselect.o nsimage.o nsfont.o macfont.o \ w32.o w32console.o w32cygwinx.o w32fns.o w32heap.o w32inevt.o w32notify.o \ - w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \ + w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32image.o w32xfns.o \ w16select.o widget.o xfont.o ftfont.o xftfont.o gtkutil.o \ xsettings.o xgselect.o termcap.o hbfont.o diff --git a/src/image.c b/src/image.c index 65d5925..7cdd85c 100644 --- a/src/image.c +++ b/src/image.c @@ -18,6 +18,12 @@ Copyright (C) 1989, 1992-2020 Free Software Foundation, Inc. along with GNU Emacs. If not, see . */ #include +#ifdef HAVE_GDIPLUS +#undef HAVE_JPEG +#undef HAVE_PNG +#undef HAVE_GIF +#undef HAVE_TIFF +#endif #include #include @@ -6235,7 +6241,7 @@ pbm_load (struct frame *f, struct image *img) PNG ***********************************************************************/ -#if defined (HAVE_PNG) || defined (HAVE_NS) +#if defined (HAVE_PNG) || defined (HAVE_NS) || defined (HAVE_NTGUI) /* Indices of image specification fields in png_format, below. */ @@ -6289,7 +6295,7 @@ png_image_p (Lisp_Object object) #endif /* HAVE_PNG || HAVE_NS */ -#if defined HAVE_PNG && !defined HAVE_NS +#if defined HAVE_PNG && !defined HAVE_NS && !defined HAVE_NTGUI # ifdef WINDOWSNT /* PNG library details. */ @@ -6889,8 +6895,19 @@ png_load (struct frame *f, struct image *img) image_spec_value (img->spec, QCdata, NULL)); } +#elif defined HAVE_GDIPLUS -#endif /* HAVE_NS */ +static bool +png_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)); +} + +#define init_png_functions init_w32_image_load_functions + +#endif /* HAVE_GDIPLUS */ @@ -6898,7 +6915,7 @@ png_load (struct frame *f, struct image *img) JPEG ***********************************************************************/ -#if defined (HAVE_JPEG) || defined (HAVE_NS) +#if defined (HAVE_JPEG) || defined (HAVE_NS) || defined (HAVE_GDIPLUS) /* Indices of image specification fields in gs_format, below. */ @@ -6950,7 +6967,7 @@ jpeg_image_p (Lisp_Object object) return fmt[JPEG_FILE].count + fmt[JPEG_DATA].count == 1; } -#endif /* HAVE_JPEG || HAVE_NS */ +#endif /* HAVE_JPEG || HAVE_NS || HAVE_GDIPLUS */ #ifdef HAVE_JPEG @@ -7464,6 +7481,26 @@ jpeg_load (struct frame *f, struct image *img) } #endif /* HAVE_NS */ +#ifdef HAVE_GDIPLUS +static bool +jpeg_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)); +} + +static bool +init_w32_image_load_functions (void) +{ + fprintf(stderr, "Functions initialized\n"); + return 1; +} + +#define init_jpeg_functions init_w32_image_load_functions +#endif /* HAVE_GDIPLUS */ + + #endif /* !HAVE_JPEG */ @@ -7472,7 +7509,7 @@ jpeg_load (struct frame *f, struct image *img) TIFF ***********************************************************************/ -#if defined (HAVE_TIFF) || defined (HAVE_NS) +#if defined (HAVE_TIFF) || defined (HAVE_NS) || defined (HAVE_GDIPLUS) /* Indices of image specification fields in tiff_format, below. */ @@ -7525,7 +7562,7 @@ tiff_image_p (Lisp_Object object) return fmt[TIFF_FILE].count + fmt[TIFF_DATA].count == 1; } -#endif /* HAVE_TIFF || HAVE_NS */ +#endif /* HAVE_TIFF || HAVE_NS || HAVE_GDIPLUS */ #ifdef HAVE_TIFF @@ -7903,6 +7940,18 @@ tiff_load (struct frame *f, struct image *img) image_spec_value (img->spec, QCdata, NULL)); } +#elif defined HAVE_GDIPLUS + +static bool +tiff_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)); +} + +#define init_tiff_functions init_w32_image_load_functions + #endif @@ -7911,7 +7960,7 @@ tiff_load (struct frame *f, struct image *img) GIF ***********************************************************************/ -#if defined (HAVE_GIF) || defined (HAVE_NS) +#if defined (HAVE_GIF) || defined (HAVE_NS) || defined (HAVE_NTGUI) /* Indices of image specification fields in gif_format, below. */ @@ -7973,7 +8022,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 || HAVE_NTGUI */ #ifdef HAVE_GIF @@ -8502,6 +8551,17 @@ gif_load (struct frame *f, struct image *img) } #endif /* HAVE_NS */ +#ifdef HAVE_NTGUI +static bool +gif_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)); +} +#define init_gif_functions init_w32_image_load_functions +#endif /* HAVE_NTGUI */ + #endif /* HAVE_GIF */ @@ -10164,19 +10224,19 @@ 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_PNG || defined HAVE_NS +#if defined HAVE_PNG || defined HAVE_NS || defined HAVE_GDIPLUS { SYMBOL_INDEX (Qpng), png_image_p, png_load, image_clear_image, IMAGE_TYPE_INIT (init_png_functions) }, #endif -#if defined HAVE_GIF || defined HAVE_NS +#if defined HAVE_GIF || defined HAVE_NS || defined HAVE_GDIPLUS { SYMBOL_INDEX (Qgif), gif_image_p, gif_load, gif_clear_image, IMAGE_TYPE_INIT (init_gif_functions) }, #endif -#if defined HAVE_TIFF || defined HAVE_NS +#if defined HAVE_TIFF || defined HAVE_NS || defined HAVE_GDIPLUS { SYMBOL_INDEX (Qtiff), tiff_image_p, tiff_load, image_clear_image, IMAGE_TYPE_INIT (init_tiff_functions) }, #endif -#if defined HAVE_JPEG || defined HAVE_NS +#if defined HAVE_JPEG || defined HAVE_NS || defined HAVE_GDIPLUS { SYMBOL_INDEX (Qjpeg), jpeg_image_p, jpeg_load, image_clear_image, IMAGE_TYPE_INIT (init_jpeg_functions) }, #endif @@ -10315,22 +10375,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_GDIPLUS) DEFSYM (Qjpeg, "jpeg"); add_image_type (Qjpeg); #endif -#if defined (HAVE_TIFF) || defined (HAVE_NS) +#if defined (HAVE_TIFF) || defined (HAVE_NS) || defined (HAVE_GDIPLUS) DEFSYM (Qtiff, "tiff"); add_image_type (Qtiff); #endif -#if defined (HAVE_GIF) || defined (HAVE_NS) +#if defined (HAVE_GIF) || defined (HAVE_NS) || defined (HAVE_GDIPLUS) DEFSYM (Qgif, "gif"); add_image_type (Qgif); #endif -#if defined (HAVE_PNG) || defined (HAVE_NS) +#if defined (HAVE_PNG) || defined (HAVE_NS) || defined(HAVE_GDIPLUS) DEFSYM (Qpng, "png"); add_image_type (Qpng); #endif diff --git a/src/w32image.c b/src/w32image.c new file mode 100644 index 0000000..9292879 --- /dev/null +++ b/src/w32image.c @@ -0,0 +1,240 @@ +/* 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 "w32term.h" +#include "frame.h" +#include "coding.h" + +static int +gdiplus_initialized_p() +{ + static int gdip_initialized = 0; + static ULONG_PTR token; + static GdiplusStartupInput input; + static GdiplusStartupOutput output; + GpStatus status; + + if (gdip_initialized < 0) + { + return 0; + } + else if (gdip_initialized) + { + return 1; + } + else + { + 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; + } + } + return 1; +} + +static float +w32_frame_delay(GpBitmap *pBitmap, int frame) +{ + + UINT size; + PropertyItem *propertyItem; + float 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 = ((float)propertyItem[frame].length) / 100; + if (delay == 0) + { + /* In GIF files, unfortunately, delay is only specified for + the first frame */ + delay = ((float)propertyItem[0].length) / 100; + } + // Free space + free(propertyItem); + } + return delay; +} + +static UINT +w32_select_active_frame(GpBitmap *pBitmap, int frame, int *nframes, float *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); + fprintf(stderr, "FrameCount: %d\n", (int)frameCount); + fprintf(stderr, " index: %d\n", frame); + 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) + ? FRAME_TERMINAL (f)->defined_color_hook (f, + SSDATA (specified_bg), + &color, + false, + false) + : (FRAME_TERMINAL (f)->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; + float delay; + + eassert (valid_image_p (img->spec)); + + if (!gdiplus_initialized_p ()) + { + return 0; + } + + if (STRINGP (spec_file)) + { + filename_to_utf16 (SSDATA (spec_file) , filename); + status = GdipCreateBitmapFromFile (filename, &pBitmap); + } + 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.h b/src/w32term.h index f8a8a72..ed67957 100644 --- a/src/w32term.h +++ b/src/w32term.h @@ -75,7 +75,8 @@ #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); /* For each display (currently only one on w32), we have a structure that records information about it. */