aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEli Zaretskii2020-04-14 18:10:41 +0300
committerEli Zaretskii2020-04-14 18:10:41 +0300
commite94206aaf608a899c81bb07fe91d26439f51b3f8 (patch)
treea4a24407f1ba3d70ae192a1aad954a2bed3e91e1 /src
parentdf254a7445a86dc25d133f2d79be8096190a8b96 (diff)
downloademacs-e94206aaf608a899c81bb07fe91d26439f51b3f8.tar.gz
emacs-e94206aaf608a899c81bb07fe91d26439f51b3f8.zip
Make use of MS-Windows native image API be selectable at run time
* configure.ac: Minor cleanup in how w32image.o is added to the build when native image APIs are requested. * src/w32gui.h (w32_load_image, w32_can_use_native_image_api) (w32_gdiplus_shutdown): Move prototypes from w32term.h here, since w32.c doesn't include w32term.h. * src/image.c (struct image_type): No need to pass TYPE to the 'valid_p' method. All callers changed. (initialize_image_type) [HAVE_NATIVE_IMAGE_API]: Call 'image_can_use_native_api' before trying image-specific methods. (image_can_use_native_api): New function. (image_types): Remove the native_image_type parts. (syms_of_image): New symbol 'native-image'. (parse_image_spec): Accept native-image "type" for any image type. * src/w32term.c (syms_of_w32term): New variable 'w32-use-native-image-API'. * src/w32image.c: (w32_can_use_native_image_api): New function. (gdiplus_init): Rename from w32_gdiplus_startup. Simplify code. Move the call to GdiplusStartup to a separate function. Use ordinal number for SHCreateMemStream if cannot load it by name. (w32_load_image): Ignore Win32Error status from w32_select_active_frame. Move DEFSYMs from here... * src/image.c (syms_of_image) [HAVE_NATIVE_IMAGE_API]: ...to here. * etc/NEWS: Update the entry about native image API use.
Diffstat (limited to 'src')
-rw-r--r--src/image.c134
-rw-r--r--src/w32.c2
-rw-r--r--src/w32gui.h6
-rw-r--r--src/w32image.c224
-rw-r--r--src/w32term.c19
-rw-r--r--src/w32term.h4
6 files changed, 244 insertions, 145 deletions
diff --git a/src/image.c b/src/image.c
index ff2d12fa1a1..4ef3e9d3e4c 100644
--- a/src/image.c
+++ b/src/image.c
@@ -751,7 +751,7 @@ struct image_type
751 751
752 /* Check that SPEC is a valid image specification for the given 752 /* Check that SPEC is a valid image specification for the given
753 image type. Value is true if SPEC is valid. */ 753 image type. Value is true if SPEC is valid. */
754 bool (*valid_p) (Lisp_Object spec, Lisp_Object type); 754 bool (*valid_p) (Lisp_Object spec);
755 755
756 /* Load IMG which is used on frame F from information contained in 756 /* Load IMG which is used on frame F from information contained in
757 IMG->spec. Value is true if successful. */ 757 IMG->spec. Value is true if successful. */
@@ -807,7 +807,7 @@ valid_image_p (Lisp_Object object)
807 { 807 {
808 struct image_type const *type = lookup_image_type (XCAR (tail)); 808 struct image_type const *type = lookup_image_type (XCAR (tail));
809 if (type) 809 if (type)
810 return type->valid_p (object, builtin_lisp_symbol (type->type)); 810 return type->valid_p (object);
811 } 811 }
812 break; 812 break;
813 } 813 }
@@ -816,7 +816,6 @@ valid_image_p (Lisp_Object object)
816 return false; 816 return false;
817} 817}
818 818
819
820/* Log error message with format string FORMAT and trailing arguments. 819/* Log error message with format string FORMAT and trailing arguments.
821 Signaling an error, e.g. when an image cannot be loaded, is not a 820 Signaling an error, e.g. when an image cannot be loaded, is not a
822 good idea because this would interrupt redisplay, and the error 821 good idea because this would interrupt redisplay, and the error
@@ -1004,7 +1003,8 @@ parse_image_spec (Lisp_Object spec, struct image_keyword *keywords,
1004 break; 1003 break;
1005 } 1004 }
1006 1005
1007 if (EQ (key, QCtype) && !EQ (type, value)) 1006 if (EQ (key, QCtype)
1007 && !(EQ (type, value) || EQ (type, Qnative_image)))
1008 return false; 1008 return false;
1009 } 1009 }
1010 1010
@@ -3144,12 +3144,12 @@ enum xbm_token
3144 displayed is used. */ 3144 displayed is used. */
3145 3145
3146static bool 3146static bool
3147xbm_image_p (Lisp_Object object, Lisp_Object type) 3147xbm_image_p (Lisp_Object object)
3148{ 3148{
3149 struct image_keyword kw[XBM_LAST]; 3149 struct image_keyword kw[XBM_LAST];
3150 3150
3151 memcpy (kw, xbm_format, sizeof kw); 3151 memcpy (kw, xbm_format, sizeof kw);
3152 if (!parse_image_spec (object, kw, XBM_LAST, type)) 3152 if (!parse_image_spec (object, kw, XBM_LAST, Qxbm))
3153 return 0; 3153 return 0;
3154 3154
3155 eassert (EQ (kw[XBM_TYPE].value, Qxbm)); 3155 eassert (EQ (kw[XBM_TYPE].value, Qxbm));
@@ -3697,7 +3697,7 @@ xbm_load (struct frame *f, struct image *img)
3697 bool success_p = 0; 3697 bool success_p = 0;
3698 Lisp_Object file_name; 3698 Lisp_Object file_name;
3699 3699
3700 eassert (xbm_image_p (img->spec, Qxbm)); 3700 eassert (xbm_image_p (img->spec));
3701 3701
3702 /* If IMG->spec specifies a file name, create a non-file spec from it. */ 3702 /* If IMG->spec specifies a file name, create a non-file spec from it. */
3703 file_name = image_spec_value (img->spec, QCfile, NULL); 3703 file_name = image_spec_value (img->spec, QCfile, NULL);
@@ -4155,11 +4155,11 @@ xpm_valid_color_symbols_p (Lisp_Object color_symbols)
4155/* Value is true if OBJECT is a valid XPM image specification. */ 4155/* Value is true if OBJECT is a valid XPM image specification. */
4156 4156
4157static bool 4157static bool
4158xpm_image_p (Lisp_Object object, Lisp_Object type) 4158xpm_image_p (Lisp_Object object)
4159{ 4159{
4160 struct image_keyword fmt[XPM_LAST]; 4160 struct image_keyword fmt[XPM_LAST];
4161 memcpy (fmt, xpm_format, sizeof fmt); 4161 memcpy (fmt, xpm_format, sizeof fmt);
4162 return (parse_image_spec (object, fmt, XPM_LAST, type) 4162 return (parse_image_spec (object, fmt, XPM_LAST, Qxpm)
4163 /* Either `:file' or `:data' must be present. */ 4163 /* Either `:file' or `:data' must be present. */
4164 && fmt[XPM_FILE].count + fmt[XPM_DATA].count == 1 4164 && fmt[XPM_FILE].count + fmt[XPM_DATA].count == 1
4165 /* Either no `:color-symbols' or it's a list of conses 4165 /* Either no `:color-symbols' or it's a list of conses
@@ -5883,13 +5883,13 @@ static const struct image_keyword pbm_format[PBM_LAST] =
5883/* Return true if OBJECT is a valid PBM image specification. */ 5883/* Return true if OBJECT is a valid PBM image specification. */
5884 5884
5885static bool 5885static bool
5886pbm_image_p (Lisp_Object object, Lisp_Object type) 5886pbm_image_p (Lisp_Object object)
5887{ 5887{
5888 struct image_keyword fmt[PBM_LAST]; 5888 struct image_keyword fmt[PBM_LAST];
5889 5889
5890 memcpy (fmt, pbm_format, sizeof fmt); 5890 memcpy (fmt, pbm_format, sizeof fmt);
5891 5891
5892 if (!parse_image_spec (object, fmt, PBM_LAST, type)) 5892 if (!parse_image_spec (object, fmt, PBM_LAST, Qpbm))
5893 return 0; 5893 return 0;
5894 5894
5895 /* Must specify either :data or :file. */ 5895 /* Must specify either :data or :file. */
@@ -6235,21 +6235,30 @@ pbm_load (struct frame *f, struct image *img)
6235/*********************************************************************** 6235/***********************************************************************
6236 NATIVE IMAGE HANDLING 6236 NATIVE IMAGE HANDLING
6237 ***********************************************************************/ 6237 ***********************************************************************/
6238#if defined(HAVE_NATIVE_IMAGE_API) && defined(HAVE_NTGUI) 6238
6239static bool
6240image_can_use_native_api (Lisp_Object type)
6241{
6242#if HAVE_NATIVE_IMAGE_API
6243# ifdef HAVE_NTGUI
6244 return w32_can_use_native_image_api (type);
6245# else
6246 return false;
6247# endif
6248#else
6249 return false;
6250#endif
6251}
6252
6253#if HAVE_NATIVE_IMAGE_API
6254
6239/* 6255/*
6240 * These functions are actually defined in the OS-native implementation 6256 * These functions are actually defined in the OS-native implementation
6241 * file. Currently, for Windows GDI+ interface, w32image.c, but other 6257 * file. Currently, for Windows GDI+ interface, w32image.c, but other
6242 * operating systems can follow suit. 6258 * operating systems can follow suit.
6243 */ 6259 */
6244 6260
6245static bool
6246init_native_image_functions (void)
6247{
6248 return w32_gdiplus_startup ();
6249}
6250
6251/* Indices of image specification fields in native format, below. */ 6261/* Indices of image specification fields in native format, below. */
6252
6253enum native_image_keyword_index 6262enum native_image_keyword_index
6254{ 6263{
6255 NATIVE_IMAGE_TYPE, 6264 NATIVE_IMAGE_TYPE,
@@ -6268,7 +6277,6 @@ enum native_image_keyword_index
6268 6277
6269/* Vector of image_keyword structures describing the format 6278/* Vector of image_keyword structures describing the format
6270 of valid user-defined image specifications. */ 6279 of valid user-defined image specifications. */
6271
6272static const struct image_keyword native_image_format[] = 6280static const struct image_keyword native_image_format[] =
6273{ 6281{
6274 {":type", IMAGE_SYMBOL_VALUE, 1}, 6282 {":type", IMAGE_SYMBOL_VALUE, 1},
@@ -6287,12 +6295,12 @@ static const struct image_keyword native_image_format[] =
6287/* Return true if OBJECT is a valid native API image specification. */ 6295/* Return true if OBJECT is a valid native API image specification. */
6288 6296
6289static bool 6297static bool
6290native_image_p (Lisp_Object object, Lisp_Object type) 6298native_image_p (Lisp_Object object)
6291{ 6299{
6292 struct image_keyword fmt[NATIVE_IMAGE_LAST]; 6300 struct image_keyword fmt[NATIVE_IMAGE_LAST];
6293 memcpy (fmt, native_image_format, sizeof fmt); 6301 memcpy (fmt, native_image_format, sizeof fmt);
6294 6302
6295 if (!parse_image_spec (object, fmt, 10, type)) 6303 if (!parse_image_spec (object, fmt, 10, Qnative_image))
6296 return 0; 6304 return 0;
6297 6305
6298 /* Must specify either the :data or :file keyword. */ 6306 /* Must specify either the :data or :file keyword. */
@@ -6302,11 +6310,17 @@ native_image_p (Lisp_Object object, Lisp_Object type)
6302static bool 6310static bool
6303native_image_load (struct frame *f, struct image *img) 6311native_image_load (struct frame *f, struct image *img)
6304{ 6312{
6313
6314# ifdef HAVE_NTGUI
6305 return w32_load_image (f, img, 6315 return w32_load_image (f, img,
6306 image_spec_value (img->spec, QCfile, NULL), 6316 image_spec_value (img->spec, QCfile, NULL),
6307 image_spec_value (img->spec, QCdata, NULL)); 6317 image_spec_value (img->spec, QCdata, NULL));
6318# else
6319 return 0;
6320# endif
6308} 6321}
6309#endif 6322
6323#endif /* HAVE_NATIVE_IMAGE_API */
6310 6324
6311 6325
6312/*********************************************************************** 6326/***********************************************************************
@@ -6352,12 +6366,12 @@ static const struct image_keyword png_format[PNG_LAST] =
6352/* Return true if OBJECT is a valid PNG image specification. */ 6366/* Return true if OBJECT is a valid PNG image specification. */
6353 6367
6354static bool 6368static bool
6355png_image_p (Lisp_Object object, Lisp_Object type) 6369png_image_p (Lisp_Object object)
6356{ 6370{
6357 struct image_keyword fmt[PNG_LAST]; 6371 struct image_keyword fmt[PNG_LAST];
6358 memcpy (fmt, png_format, sizeof fmt); 6372 memcpy (fmt, png_format, sizeof fmt);
6359 6373
6360 if (!parse_image_spec (object, fmt, PNG_LAST, type)) 6374 if (!parse_image_spec (object, fmt, PNG_LAST, Qpng))
6361 return 0; 6375 return 0;
6362 6376
6363 /* Must specify either the :data or :file keyword. */ 6377 /* Must specify either the :data or :file keyword. */
@@ -7014,13 +7028,13 @@ static const struct image_keyword jpeg_format[JPEG_LAST] =
7014/* Return true if OBJECT is a valid JPEG image specification. */ 7028/* Return true if OBJECT is a valid JPEG image specification. */
7015 7029
7016static bool 7030static bool
7017jpeg_image_p (Lisp_Object object, Lisp_Object type) 7031jpeg_image_p (Lisp_Object object)
7018{ 7032{
7019 struct image_keyword fmt[JPEG_LAST]; 7033 struct image_keyword fmt[JPEG_LAST];
7020 7034
7021 memcpy (fmt, jpeg_format, sizeof fmt); 7035 memcpy (fmt, jpeg_format, sizeof fmt);
7022 7036
7023 if (!parse_image_spec (object, fmt, JPEG_LAST, type)) 7037 if (!parse_image_spec (object, fmt, JPEG_LAST, Qjpeg))
7024 return 0; 7038 return 0;
7025 7039
7026 /* Must specify either the :data or :file keyword. */ 7040 /* Must specify either the :data or :file keyword. */
@@ -7590,12 +7604,12 @@ static const struct image_keyword tiff_format[TIFF_LAST] =
7590/* Return true if OBJECT is a valid TIFF image specification. */ 7604/* Return true if OBJECT is a valid TIFF image specification. */
7591 7605
7592static bool 7606static bool
7593tiff_image_p (Lisp_Object object, Lisp_Object type) 7607tiff_image_p (Lisp_Object object)
7594{ 7608{
7595 struct image_keyword fmt[TIFF_LAST]; 7609 struct image_keyword fmt[TIFF_LAST];
7596 memcpy (fmt, tiff_format, sizeof fmt); 7610 memcpy (fmt, tiff_format, sizeof fmt);
7597 7611
7598 if (!parse_image_spec (object, fmt, TIFF_LAST, type)) 7612 if (!parse_image_spec (object, fmt, TIFF_LAST, Qtiff))
7599 return 0; 7613 return 0;
7600 7614
7601 /* Must specify either the :data or :file keyword. */ 7615 /* Must specify either the :data or :file keyword. */
@@ -8038,12 +8052,12 @@ gif_clear_image (struct frame *f, struct image *img)
8038/* Return true if OBJECT is a valid GIF image specification. */ 8052/* Return true if OBJECT is a valid GIF image specification. */
8039 8053
8040static bool 8054static bool
8041gif_image_p (Lisp_Object object, Lisp_Object type) 8055gif_image_p (Lisp_Object object)
8042{ 8056{
8043 struct image_keyword fmt[GIF_LAST]; 8057 struct image_keyword fmt[GIF_LAST];
8044 memcpy (fmt, gif_format, sizeof fmt); 8058 memcpy (fmt, gif_format, sizeof fmt);
8045 8059
8046 if (!parse_image_spec (object, fmt, GIF_LAST, type)) 8060 if (!parse_image_spec (object, fmt, GIF_LAST, Qgif))
8047 return 0; 8061 return 0;
8048 8062
8049 /* Must specify either the :data or :file keyword. */ 8063 /* Must specify either the :data or :file keyword. */
@@ -8650,12 +8664,12 @@ imagemagick_clear_image (struct frame *f,
8650 identify the IMAGEMAGICK format. */ 8664 identify the IMAGEMAGICK format. */
8651 8665
8652static bool 8666static bool
8653imagemagick_image_p (Lisp_Object object, Lisp_Object type) 8667imagemagick_image_p (Lisp_Object object)
8654{ 8668{
8655 struct image_keyword fmt[IMAGEMAGICK_LAST]; 8669 struct image_keyword fmt[IMAGEMAGICK_LAST];
8656 memcpy (fmt, imagemagick_format, sizeof fmt); 8670 memcpy (fmt, imagemagick_format, sizeof fmt);
8657 8671
8658 if (!parse_image_spec (object, fmt, IMAGEMAGICK_LAST, type)) 8672 if (!parse_image_spec (object, fmt, IMAGEMAGICK_LAST, Qimagemagick))
8659 return 0; 8673 return 0;
8660 8674
8661 /* Must specify either the :data or :file keyword. */ 8675 /* Must specify either the :data or :file keyword. */
@@ -9445,12 +9459,12 @@ static const struct image_keyword svg_format[SVG_LAST] =
9445 identify the SVG format. */ 9459 identify the SVG format. */
9446 9460
9447static bool 9461static bool
9448svg_image_p (Lisp_Object object, Lisp_Object type) 9462svg_image_p (Lisp_Object object)
9449{ 9463{
9450 struct image_keyword fmt[SVG_LAST]; 9464 struct image_keyword fmt[SVG_LAST];
9451 memcpy (fmt, svg_format, sizeof fmt); 9465 memcpy (fmt, svg_format, sizeof fmt);
9452 9466
9453 if (!parse_image_spec (object, fmt, SVG_LAST, type)) 9467 if (!parse_image_spec (object, fmt, SVG_LAST, Qsvg))
9454 return 0; 9468 return 0;
9455 9469
9456 /* Must specify either the :data or :file keyword. */ 9470 /* Must specify either the :data or :file keyword. */
@@ -9913,7 +9927,7 @@ static const struct image_keyword gs_format[GS_LAST] =
9913 specification. */ 9927 specification. */
9914 9928
9915static bool 9929static bool
9916gs_image_p (Lisp_Object object, Lisp_Object type) 9930gs_image_p (Lisp_Object object)
9917{ 9931{
9918 struct image_keyword fmt[GS_LAST]; 9932 struct image_keyword fmt[GS_LAST];
9919 Lisp_Object tem; 9933 Lisp_Object tem;
@@ -9921,7 +9935,7 @@ gs_image_p (Lisp_Object object, Lisp_Object type)
9921 9935
9922 memcpy (fmt, gs_format, sizeof fmt); 9936 memcpy (fmt, gs_format, sizeof fmt);
9923 9937
9924 if (!parse_image_spec (object, fmt, GS_LAST, type)) 9938 if (!parse_image_spec (object, fmt, GS_LAST, Qpostscript))
9925 return 0; 9939 return 0;
9926 9940
9927 /* Bounding box must be a list or vector containing 4 integers. */ 9941 /* Bounding box must be a list or vector containing 4 integers. */
@@ -10208,20 +10222,19 @@ static bool
10208initialize_image_type (struct image_type const *type) 10222initialize_image_type (struct image_type const *type)
10209{ 10223{
10210#ifdef WINDOWSNT 10224#ifdef WINDOWSNT
10211 Lisp_Object typesym, tested; 10225 Lisp_Object typesym = builtin_lisp_symbol (type->type);
10212 bool (*init) (void) = type->init;
10213 10226
10214#ifdef HAVE_NATIVE_IMAGE_API 10227#ifdef HAVE_NATIVE_IMAGE_API
10215 if (init == init_native_image_functions) 10228 if (image_can_use_native_api (typesym))
10216 return init(); 10229 return true;
10217#endif 10230#endif
10218 10231
10219 typesym = builtin_lisp_symbol (type->type); 10232 Lisp_Object tested = Fassq (typesym, Vlibrary_cache);
10220 tested = Fassq (typesym, Vlibrary_cache);
10221 /* If we failed to load the library before, don't try again. */ 10233 /* If we failed to load the library before, don't try again. */
10222 if (CONSP (tested)) 10234 if (CONSP (tested))
10223 return !NILP (XCDR (tested)) ? true : false; 10235 return !NILP (XCDR (tested)) ? true : false;
10224 10236
10237 bool (*init) (void) = type->init;
10225 if (init) 10238 if (init)
10226 { 10239 {
10227 bool type_valid = init (); 10240 bool type_valid = init ();
@@ -10248,16 +10261,6 @@ static struct image_type const image_types[] =
10248 { SYMBOL_INDEX (Qsvg), svg_image_p, svg_load, image_clear_image, 10261 { SYMBOL_INDEX (Qsvg), svg_image_p, svg_load, image_clear_image,
10249 IMAGE_TYPE_INIT (init_svg_functions) }, 10262 IMAGE_TYPE_INIT (init_svg_functions) },
10250#endif 10263#endif
10251#if defined HAVE_NATIVE_IMAGE_API
10252 { SYMBOL_INDEX (Qjpeg), native_image_p, native_image_load, image_clear_image,
10253 IMAGE_TYPE_INIT (init_native_image_functions) },
10254 { SYMBOL_INDEX (Qpng), native_image_p, native_image_load, image_clear_image,
10255 IMAGE_TYPE_INIT (init_native_image_functions) },
10256 { SYMBOL_INDEX (Qgif), native_image_p, native_image_load, image_clear_image,
10257 IMAGE_TYPE_INIT (init_native_image_functions) },
10258 { SYMBOL_INDEX (Qtiff), native_image_p, native_image_load, image_clear_image,
10259 IMAGE_TYPE_INIT (init_native_image_functions) },
10260#endif
10261#if defined HAVE_PNG || defined HAVE_NS 10264#if defined HAVE_PNG || defined HAVE_NS
10262 { SYMBOL_INDEX (Qpng), png_image_p, png_load, image_clear_image, 10265 { SYMBOL_INDEX (Qpng), png_image_p, png_load, image_clear_image,
10263 IMAGE_TYPE_INIT (init_png_functions) }, 10266 IMAGE_TYPE_INIT (init_png_functions) },
@@ -10282,23 +10285,28 @@ static struct image_type const image_types[] =
10282 { SYMBOL_INDEX (Qpbm), pbm_image_p, pbm_load, image_clear_image }, 10285 { SYMBOL_INDEX (Qpbm), pbm_image_p, pbm_load, image_clear_image },
10283}; 10286};
10284 10287
10288#ifdef HAVE_NATIVE_IMAGE_API
10289struct image_type native_image_type =
10290 { SYMBOL_INDEX (Qnative_image), native_image_p, native_image_load,
10291 image_clear_image };
10292#endif
10293
10285/* Look up image type TYPE, and return a pointer to its image_type 10294/* Look up image type TYPE, and return a pointer to its image_type
10286 structure. Return 0 if TYPE is not a known image type. */ 10295 structure. Return 0 if TYPE is not a known image type. */
10287 10296
10288static struct image_type const * 10297static struct image_type const *
10289lookup_image_type (Lisp_Object type) 10298lookup_image_type (Lisp_Object type)
10290{ 10299{
10300#ifdef HAVE_NATIVE_IMAGE_API
10301 if (image_can_use_native_api (type))
10302 return &native_image_type;
10303#endif
10304
10291 for (int i = 0; i < ARRAYELTS (image_types); i++) 10305 for (int i = 0; i < ARRAYELTS (image_types); i++)
10292 { 10306 {
10293 struct image_type const *r = &image_types[i]; 10307 struct image_type const *r = &image_types[i];
10294 if (EQ (type, builtin_lisp_symbol (r->type))) 10308 if (EQ (type, builtin_lisp_symbol (r->type)))
10295#ifdef HAVE_NATIVE_IMAGE_API
10296 /* We can have more than one backend for one image type. */
10297 if (initialize_image_type (r))
10298 return r;
10299#else
10300 return initialize_image_type (r) ? r : NULL; 10309 return initialize_image_type (r) ? r : NULL;
10301#endif
10302 } 10310 }
10303 return NULL; 10311 return NULL;
10304} 10312}
@@ -10454,6 +10462,14 @@ non-numeric, there is no explicit limit on the size of images. */);
10454#endif /* HAVE_NTGUI */ 10462#endif /* HAVE_NTGUI */
10455#endif /* HAVE_RSVG */ 10463#endif /* HAVE_RSVG */
10456 10464
10465#if HAVE_NATIVE_IMAGE_API
10466 DEFSYM (Qnative_image, "native-image");
10467# ifdef HAVE_NTGUI
10468 DEFSYM (Qgdiplus, "gdiplus");
10469 DEFSYM (Qshlwapi, "shlwapi");
10470# endif
10471#endif
10472
10457 defsubr (&Sinit_image_library); 10473 defsubr (&Sinit_image_library);
10458#ifdef HAVE_IMAGEMAGICK 10474#ifdef HAVE_IMAGEMAGICK
10459 defsubr (&Simagemagick_types); 10475 defsubr (&Simagemagick_types);
diff --git a/src/w32.c b/src/w32.c
index 1d2a52b6df4..80178029bd0 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -10226,7 +10226,7 @@ term_ntproc (int ignored)
10226 10226
10227 term_w32select (); 10227 term_w32select ();
10228 10228
10229#ifdef HAVE_GDIPLUS 10229#if HAVE_NATIVE_IMAGE_API
10230 w32_gdiplus_shutdown (); 10230 w32_gdiplus_shutdown ();
10231#endif 10231#endif
10232} 10232}
diff --git a/src/w32gui.h b/src/w32gui.h
index 5cc64287291..dfec1f08617 100644
--- a/src/w32gui.h
+++ b/src/w32gui.h
@@ -41,6 +41,12 @@ typedef struct _XImage
41 /* Optional RGBQUAD array for palette follows (see BITMAPINFO docs). */ 41 /* Optional RGBQUAD array for palette follows (see BITMAPINFO docs). */
42} XImage; 42} XImage;
43 43
44struct image;
45extern int w32_load_image (struct frame *f, struct image *img,
46 Lisp_Object spec_file, Lisp_Object spec_data);
47extern bool w32_can_use_native_image_api (Lisp_Object);
48extern void w32_gdiplus_shutdown (void);
49
44#define FACE_DEFAULT (~0) 50#define FACE_DEFAULT (~0)
45 51
46extern HINSTANCE hinst; 52extern HINSTANCE hinst;
diff --git a/src/w32image.c b/src/w32image.c
index fe32660f712..6a3e37ce0ee 100644
--- a/src/w32image.c
+++ b/src/w32image.c
@@ -23,7 +23,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
23#include "lisp.h" 23#include "lisp.h"
24#include "dispextern.h" 24#include "dispextern.h"
25#define COBJMACROS 25#define COBJMACROS
26#ifdef MINGW_W64
27/* FIXME: Do we need to include objidl.h? */
26#include <objidl.h> 28#include <objidl.h>
29#endif
27#include <wtypes.h> 30#include <wtypes.h>
28#include <gdiplus.h> 31#include <gdiplus.h>
29#include <shlwapi.h> 32#include <shlwapi.h>
@@ -32,53 +35,39 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
32#include "frame.h" 35#include "frame.h"
33#include "coding.h" 36#include "coding.h"
34 37
35/*#define LINK_GDIPLUS_STATICALLY 1*/ 38#ifdef WINDOWSNT
36 39
37#ifndef LINK_GDIPLUS_STATICALLY 40DEF_DLL_FN (GpStatus, GdiplusStartup,
38DEF_DLL_FN (GpStatus, GdiplusStartup, (ULONG_PTR *, GdiplusStartupInput *, GdiplusStartupOutput *)); 41 (ULONG_PTR *, GdiplusStartupInput *, GdiplusStartupOutput *));
39DEF_DLL_FN (VOID, GdiplusShutdown, (ULONG_PTR)); 42DEF_DLL_FN (VOID, GdiplusShutdown, (ULONG_PTR));
40DEF_DLL_FN (GpStatus, GdipGetPropertyItemSize, (GpImage *, PROPID, UINT *)); 43DEF_DLL_FN (GpStatus, GdipGetPropertyItemSize,
41DEF_DLL_FN (GpStatus, GdipGetPropertyItem, (GpImage *, PROPID, UINT, PropertyItem *)); 44 (GpImage *, PROPID, UINT *));
45DEF_DLL_FN (GpStatus, GdipGetPropertyItem,
46 (GpImage *, PROPID, UINT, PropertyItem *));
42DEF_DLL_FN (GpStatus, GdipImageGetFrameDimensionsCount, (GpImage *, UINT *)); 47DEF_DLL_FN (GpStatus, GdipImageGetFrameDimensionsCount, (GpImage *, UINT *));
43DEF_DLL_FN (GpStatus, GdipImageGetFrameDimensionsList, (GpImage *, GUID *, UINT)); 48DEF_DLL_FN (GpStatus, GdipImageGetFrameDimensionsList,
44DEF_DLL_FN (GpStatus, GdipImageGetFrameCount, (GpImage *, GDIPCONST GUID *, UINT *)); 49 (GpImage *, GUID *, UINT));
45DEF_DLL_FN (GpStatus, GdipImageSelectActiveFrame, (GpImage*, GDIPCONST GUID *, UINT)); 50DEF_DLL_FN (GpStatus, GdipImageGetFrameCount,
51 (GpImage *, GDIPCONST GUID *, UINT *));
52DEF_DLL_FN (GpStatus, GdipImageSelectActiveFrame,
53 (GpImage*, GDIPCONST GUID *, UINT));
46DEF_DLL_FN (GpStatus, GdipCreateBitmapFromFile, (WCHAR *, GpBitmap **)); 54DEF_DLL_FN (GpStatus, GdipCreateBitmapFromFile, (WCHAR *, GpBitmap **));
47DEF_DLL_FN (GpStatus, GdipCreateBitmapFromStream, (IStream *, GpBitmap **)); 55DEF_DLL_FN (GpStatus, GdipCreateBitmapFromStream, (IStream *, GpBitmap **));
48DEF_DLL_FN (IStream *, SHCreateMemStream, (const BYTE *pInit, UINT cbInit)); 56DEF_DLL_FN (IStream *, SHCreateMemStream, (const BYTE *pInit, UINT cbInit));
49DEF_DLL_FN (GpStatus, GdipCreateHBITMAPFromBitmap, (GpBitmap *, HBITMAP *, ARGB)); 57DEF_DLL_FN (GpStatus, GdipCreateHBITMAPFromBitmap,
58 (GpBitmap *, HBITMAP *, ARGB));
50DEF_DLL_FN (GpStatus, GdipDisposeImage, (GpImage *)); 59DEF_DLL_FN (GpStatus, GdipDisposeImage, (GpImage *));
51DEF_DLL_FN (GpStatus, GdipGetImageHeight, (GpImage *, UINT *)); 60DEF_DLL_FN (GpStatus, GdipGetImageHeight, (GpImage *, UINT *));
52DEF_DLL_FN (GpStatus, GdipGetImageWidth, (GpImage *, UINT *)); 61DEF_DLL_FN (GpStatus, GdipGetImageWidth, (GpImage *, UINT *));
53#endif
54
55static int gdip_initialized = 0;
56static ULONG_PTR token;
57static GdiplusStartupInput input;
58static GdiplusStartupOutput output;
59 62
60bool 63static bool
61w32_gdiplus_startup (void) 64gdiplus_init (void)
62{ 65{
63 HANDLE gdiplus_lib, shlwapi_lib; 66 HANDLE gdiplus_lib, shlwapi_lib;
64 GpStatus status;
65 67
66 if (gdip_initialized < 0) 68 if (!((gdiplus_lib = w32_delayed_load (Qgdiplus))
67 return 0; 69 && (shlwapi_lib = w32_delayed_load (Qshlwapi))))
68 else if (gdip_initialized) 70 return false;
69 return 1;
70
71#ifndef LINK_GDIPLUS_STATICALLY
72 DEFSYM (Qgdiplus, "gdiplus");
73 DEFSYM (Qshlwapi, "shlwapi");
74 if (!(gdiplus_lib = w32_delayed_load (Qgdiplus))) {
75 gdip_initialized = -1;
76 return 0;
77 }
78 if (!(shlwapi_lib = w32_delayed_load (Qshlwapi))) {
79 gdip_initialized = -1;
80 return 0;
81 }
82 71
83 LOAD_DLL_FN (gdiplus_lib, GdiplusStartup); 72 LOAD_DLL_FN (gdiplus_lib, GdiplusStartup);
84 LOAD_DLL_FN (gdiplus_lib, GdiplusShutdown); 73 LOAD_DLL_FN (gdiplus_lib, GdiplusShutdown);
@@ -94,7 +83,41 @@ w32_gdiplus_startup (void)
94 LOAD_DLL_FN (gdiplus_lib, GdipDisposeImage); 83 LOAD_DLL_FN (gdiplus_lib, GdipDisposeImage);
95 LOAD_DLL_FN (gdiplus_lib, GdipGetImageHeight); 84 LOAD_DLL_FN (gdiplus_lib, GdipGetImageHeight);
96 LOAD_DLL_FN (gdiplus_lib, GdipGetImageWidth); 85 LOAD_DLL_FN (gdiplus_lib, GdipGetImageWidth);
97 LOAD_DLL_FN (shlwapi_lib, SHCreateMemStream); 86 /* LOAD_DLL_FN (shlwapi_lib, SHCreateMemStream); */
87
88 /* The following terrible kludge is required to use native image API
89 on Windows before Vista, because SHCreateMemStream was not
90 exported by name in those versions, only by ordinal number. */
91 fn_SHCreateMemStream =
92 (W32_PFN_SHCreateMemStream) get_proc_addr (shlwapi_lib,
93 "SHCreateMemStream");
94 if (!fn_SHCreateMemStream)
95 {
96 fn_SHCreateMemStream =
97 (W32_PFN_SHCreateMemStream) get_proc_addr (shlwapi_lib,
98 MAKEINTRESOURCEA (12));
99 if (!fn_SHCreateMemStream)
100 return false;
101 }
102
103 return true;
104}
105
106# undef GdiplusStartup
107# undef GdiplusShutdown
108# undef GdipGetPropertyItemSize
109# undef GdipGetPropertyItem
110# undef GdipImageGetFrameDimensionsCount
111# undef GdipImageGetFrameDimensionsList
112# undef GdipImageGetFrameCount
113# undef GdipImageSelectActiveFrame
114# undef GdipCreateBitmapFromFile
115# undef GdipCreateBitmapFromStream
116# undef SHCreateMemStream
117# undef GdipCreateHBITMAPFromBitmap
118# undef GdipDisposeImage
119# undef GdipGetImageHeight
120# undef GdipGetImageWidth
98 121
99# define GdiplusStartup fn_GdiplusStartup 122# define GdiplusStartup fn_GdiplusStartup
100# define GdiplusShutdown fn_GdiplusShutdown 123# define GdiplusShutdown fn_GdiplusShutdown
@@ -111,32 +134,71 @@ w32_gdiplus_startup (void)
111# define GdipDisposeImage fn_GdipDisposeImage 134# define GdipDisposeImage fn_GdipDisposeImage
112# define GdipGetImageHeight fn_GdipGetImageHeight 135# define GdipGetImageHeight fn_GdipGetImageHeight
113# define GdipGetImageWidth fn_GdipGetImageWidth 136# define GdipGetImageWidth fn_GdipGetImageWidth
114#endif
115 137
116 input.GdiplusVersion = 1; 138#endif /* WINDOWSNT */
117 input.DebugEventCallback = NULL;
118 input.SuppressBackgroundThread = FALSE;
119 input.SuppressExternalCodecs = FALSE;
120 139
121 status = GdiplusStartup (&token, &input, &output); 140static int gdip_initialized;
122 if (status == Ok) 141static bool gdiplus_started;
123 { 142static ULONG_PTR token;
124 gdip_initialized = 1; 143static GdiplusStartupInput input;
125 return 1; 144static GdiplusStartupOutput output;
126 } 145
127 else 146
147/* Initialize GDI+, return true if successful. */
148static bool
149gdiplus_startup (void)
150{
151 GpStatus status;
152
153 if (gdiplus_started)
154 return true;
155#ifdef WINDOWSNT
156 if (!gdip_initialized)
157 gdip_initialized = gdiplus_init () ? 1 : -1;
158#else
159 gdip_initialized = 1;
160#endif
161 if (gdip_initialized > 0)
128 { 162 {
129 gdip_initialized = -1; 163 input.GdiplusVersion = 1;
130 return 0; 164 input.DebugEventCallback = NULL;
165 input.SuppressBackgroundThread = FALSE;
166 input.SuppressExternalCodecs = FALSE;
167
168 status = GdiplusStartup (&token, &input, &output);
169 if (status == Ok)
170 gdiplus_started = true;
171 return (status == Ok);
131 } 172 }
173 return false;
132} 174}
133 175
176/* This is called from term_ntproc. */
134void 177void
135w32_gdiplus_shutdown (void) 178w32_gdiplus_shutdown (void)
136{ 179{
137 GdiplusShutdown (token); 180 if (gdiplus_started)
181 GdiplusShutdown (token);
182 gdiplus_started = false;
138} 183}
139 184
185bool
186w32_can_use_native_image_api (Lisp_Object type)
187{
188 if (!w32_use_native_image_api)
189 return false;
190 if (!(EQ (type, Qjpeg)
191 || EQ (type, Qpng)
192 || EQ (type, Qgif)
193 || EQ (type, Qtiff)
194 || EQ (type, Qnative_image)))
195 {
196 /* GDI+ can also display BMP, Exif, ICON, WMF, and EMF images.
197 But we don't yet support these in image.c. */
198 return false;
199 }
200 return gdiplus_startup ();
201}
140 202
141static double 203static double
142w32_frame_delay (GpBitmap *pBitmap, int frame) 204w32_frame_delay (GpBitmap *pBitmap, int frame)
@@ -150,25 +212,26 @@ w32_frame_delay (GpBitmap *pBitmap, int frame)
150 GdipGetPropertyItemSize (pBitmap, PropertyTagFrameDelay, &size); 212 GdipGetPropertyItemSize (pBitmap, PropertyTagFrameDelay, &size);
151 213
152 /* Allocate a buffer to receive the property item. */ 214 /* Allocate a buffer to receive the property item. */
153 propertyItem = (PropertyItem*)malloc (size); 215 propertyItem = malloc (size);
154 if (propertyItem != NULL) 216 if (propertyItem != NULL)
155 { 217 {
156 /* Get the property item. */ 218 /* Get the property item. */
157 GdipGetPropertyItem (pBitmap, PropertyTagFrameDelay, size, propertyItem); 219 GdipGetPropertyItem (pBitmap, PropertyTagFrameDelay, size, propertyItem);
158 delay = ((double)propertyItem[frame].length) / 100; 220 delay = propertyItem[frame].length / 100.0;
159 if (delay == 0) 221 if (delay == 0)
160 { 222 {
161 /* In GIF files, unfortunately, delay is only specified for the first 223 /* In GIF files, unfortunately, delay is only specified for the first
162 frame. */ 224 frame. */
163 delay = ((double)propertyItem[0].length) / 100; 225 delay = propertyItem[0].length / 100.0;
164 } 226 }
165 free (propertyItem); 227 free (propertyItem);
166 } 228 }
167 return delay; 229 return delay;
168} 230}
169 231
170static UINT 232static GpStatus
171w32_select_active_frame (GpBitmap *pBitmap, int frame, int *nframes, double *delay) 233w32_select_active_frame (GpBitmap *pBitmap, int frame, int *nframes,
234 double *delay)
172{ 235{
173 UINT count, frameCount; 236 UINT count, frameCount;
174 GUID pDimensionIDs[1]; 237 GUID pDimensionIDs[1];
@@ -181,15 +244,14 @@ w32_select_active_frame (GpBitmap *pBitmap, int frame, int *nframes, double *del
181 { 244 {
182 status = GdipImageGetFrameDimensionsList (pBitmap, pDimensionIDs, 1); 245 status = GdipImageGetFrameDimensionsList (pBitmap, pDimensionIDs, 1);
183 status = GdipImageGetFrameCount (pBitmap, &pDimensionIDs[0], &frameCount); 246 status = GdipImageGetFrameCount (pBitmap, &pDimensionIDs[0], &frameCount);
184 if ((status == Ok) && (frameCount > 1)) 247 if (status == Ok && frameCount > 1)
185 { 248 {
186 if (frame < 0 || frame >= frameCount) 249 if (frame < 0 || frame >= frameCount)
187 { 250 status = GenericError;
188 status = GenericError;
189 }
190 else 251 else
191 { 252 {
192 status = GdipImageSelectActiveFrame (pBitmap, &pDimensionIDs[0], frame); 253 status = GdipImageSelectActiveFrame (pBitmap, &pDimensionIDs[0],
254 frame);
193 *delay = w32_frame_delay (pBitmap, frame); 255 *delay = w32_frame_delay (pBitmap, frame);
194 *nframes = frameCount; 256 *nframes = frameCount;
195 } 257 }
@@ -201,9 +263,7 @@ w32_select_active_frame (GpBitmap *pBitmap, int frame, int *nframes, double *del
201static ARGB 263static ARGB
202w32_image_bg_color (struct frame *f, struct image *img) 264w32_image_bg_color (struct frame *f, struct image *img)
203{ 265{
204 /* png_color_16 *image_bg; */ 266 Lisp_Object specified_bg = Fplist_get (XCDR (img->spec), QCbackground);
205 Lisp_Object specified_bg
206 = Fplist_get (XCDR (img->spec), QCbackground);
207 Emacs_Color color; 267 Emacs_Color color;
208 268
209 /* If the user specified a color, try to use it; if not, use the 269 /* If the user specified a color, try to use it; if not, use the
@@ -212,38 +272,34 @@ w32_image_bg_color (struct frame *f, struct image *img)
212 if (STRINGP (specified_bg) 272 if (STRINGP (specified_bg)
213 ? w32_defined_color (f, SSDATA (specified_bg), &color, false, false) 273 ? w32_defined_color (f, SSDATA (specified_bg), &color, false, false)
214 : (w32_query_frame_background_color (f, &color), true)) 274 : (w32_query_frame_background_color (f, &color), true))
215 /* The user specified `:background', use that. */ 275 /* The user specified ':background', use that. */
216 { 276 {
217 DWORD red = (((DWORD) color.red) & 0xff00) << 8; 277 DWORD red = (((DWORD) color.red) & 0xff00) << 8;
218 DWORD green = ((DWORD) color.green) & 0xff00; 278 DWORD green = ((DWORD) color.green) & 0xff00;
219 DWORD blue = ((DWORD) color.blue) >> 8; 279 DWORD blue = ((DWORD) color.blue) >> 8;
220 return red | green | blue; 280 return (ARGB) (red | green | blue);
221 } 281 }
222 return ((DWORD) 0xff000000); 282 return (ARGB) 0xff000000;
223} 283}
224 284
225int 285int
226w32_load_image (struct frame *f, struct image *img, 286w32_load_image (struct frame *f, struct image *img,
227 Lisp_Object spec_file, Lisp_Object spec_data) 287 Lisp_Object spec_file, Lisp_Object spec_data)
228{ 288{
229 Emacs_Pixmap pixmap;
230 GpStatus status = GenericError; 289 GpStatus status = GenericError;
231 GpBitmap *pBitmap; 290 GpBitmap *pBitmap;
232 wchar_t filename[MAX_PATH]; 291 Lisp_Object metadata;
233 ARGB bg_color;
234 Lisp_Object lisp_index, metadata;
235 unsigned int index, nframes;
236 double delay;
237 292
238 eassert (valid_image_p (img->spec)); 293 eassert (valid_image_p (img->spec));
239 294
240 /* This function only gets called if init_w32_gdiplus () was invoked. We have 295 /* This function only gets called if w32_gdiplus_startup was invoked
241 a valid token and GDI+ is active. */ 296 and succeeded. We have a valid token and GDI+ is active. */
242 if (STRINGP (spec_file)) 297 if (STRINGP (spec_file))
243 { 298 {
244 if (w32_unicode_filenames) 299 if (w32_unicode_filenames)
245 { 300 {
246 filename_to_utf16 (SSDATA (spec_file) , filename); 301 wchar_t filename[MAX_PATH];
302 filename_to_utf16 (SSDATA (spec_file), filename);
247 status = GdipCreateBitmapFromFile (filename, &pBitmap); 303 status = GdipCreateBitmapFromFile (filename, &pBitmap);
248 } 304 }
249 else 305 else
@@ -254,7 +310,7 @@ w32_load_image (struct frame *f, struct image *img,
254 } 310 }
255 else if (STRINGP (spec_data)) 311 else if (STRINGP (spec_data))
256 { 312 {
257 IStream *pStream = SHCreateMemStream ((BYTE *) SSDATA (spec_data), 313 IStream *pStream = SHCreateMemStream ((BYTE *) SDATA (spec_data),
258 SBYTES (spec_data)); 314 SBYTES (spec_data));
259 if (pStream != NULL) 315 if (pStream != NULL)
260 { 316 {
@@ -266,22 +322,28 @@ w32_load_image (struct frame *f, struct image *img,
266 metadata = Qnil; 322 metadata = Qnil;
267 if (status == Ok) 323 if (status == Ok)
268 { 324 {
269 /* In multiframe pictures, select the first one */ 325 /* In multiframe pictures, select the first frame. */
270 lisp_index = Fplist_get (XCDR (img->spec), QCindex); 326 Lisp_Object lisp_index = Fplist_get (XCDR (img->spec), QCindex);
271 index = FIXNUMP (lisp_index) ? XFIXNAT (lisp_index) : 0; 327 int index = FIXNATP (lisp_index) ? XFIXNAT (lisp_index) : 0;
328 int nframes;
329 double delay;
272 status = w32_select_active_frame (pBitmap, index, &nframes, &delay); 330 status = w32_select_active_frame (pBitmap, index, &nframes, &delay);
273 if ((status == Ok)) 331 if (status == Ok)
274 { 332 {
275 if (nframes > 1) 333 if (nframes > 1)
276 metadata = Fcons (Qcount, Fcons (make_fixnum (nframes), metadata)); 334 metadata = Fcons (Qcount, Fcons (make_fixnum (nframes), metadata));
277 if (delay) 335 if (delay)
278 metadata = Fcons (Qdelay, Fcons (make_float (delay), metadata)); 336 metadata = Fcons (Qdelay, Fcons (make_float (delay), metadata));
279 } 337 }
338 else if (status == Win32Error) /* FIXME! */
339 status = Ok;
280 } 340 }
281 341
282 if (status == Ok) 342 if (status == Ok)
283 { 343 {
284 bg_color = w32_image_bg_color (f, img); 344 ARGB bg_color = w32_image_bg_color (f, img);
345 Emacs_Pixmap pixmap;
346
285 status = GdipCreateHBITMAPFromBitmap (pBitmap, &pixmap, bg_color); 347 status = GdipCreateHBITMAPFromBitmap (pBitmap, &pixmap, bg_color);
286 if (status == Ok) 348 if (status == Ok)
287 { 349 {
diff --git a/src/w32term.c b/src/w32term.c
index f19754df02c..108cb7922fb 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -7658,6 +7658,25 @@ Windows 8. It is set to nil on Windows 9X. */);
7658 else 7658 else
7659 w32_unicode_filenames = 1; 7659 w32_unicode_filenames = 1;
7660 7660
7661 DEFVAR_BOOL ("w32-use-native-image-API",
7662 w32_use_native_image_api,
7663 doc: /* Non-nil means use the native MS-Windows image API to display images.
7664
7665A value of nil means displaying images other than PBM and XBM requires
7666optional supporting libraries to be installed.
7667The native image API library used is GDI+ via GDIPLUS.DLL. This
7668library is available only since W2K, therefore this variable is
7669unconditionally set to nil on older systems. */);
7670
7671 /* For now, disabled by default, since this is an experimental feature. */
7672#if 0 && HAVE_NATIVE_IMAGE_API
7673 if (os_subtype == OS_9X)
7674 w32_use_native_image_api = 0;
7675 else
7676 w32_use_native_image_api = 1;
7677#else
7678 w32_use_native_image_api = 0;
7679#endif
7661 7680
7662 /* FIXME: The following variable will be (hopefully) removed 7681 /* FIXME: The following variable will be (hopefully) removed
7663 before Emacs 25.1 gets released. */ 7682 before Emacs 25.1 gets released. */
diff --git a/src/w32term.h b/src/w32term.h
index 7ca00d0a099..8ba248013c7 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -75,10 +75,6 @@ struct w32_palette_entry {
75extern void w32_regenerate_palette (struct frame *f); 75extern void w32_regenerate_palette (struct frame *f);
76extern void w32_fullscreen_rect (HWND hwnd, int fsmode, RECT normal, 76extern void w32_fullscreen_rect (HWND hwnd, int fsmode, RECT normal,
77 RECT *rect); 77 RECT *rect);
78extern int w32_load_image (struct frame *f, struct image *img,
79 Lisp_Object spec_file, Lisp_Object spec_data);
80extern bool w32_gdiplus_startup (void);
81extern void w32_gdiplus_shutdown (void);
82 78
83/* For each display (currently only one on w32), we have a structure that 79/* For each display (currently only one on w32), we have a structure that
84 records information about it. */ 80 records information about it. */