aboutsummaryrefslogtreecommitdiffstats
path: root/src/w32image.c
diff options
context:
space:
mode:
authorJuan José García-Ripoll2020-04-13 12:04:39 +0200
committerEli Zaretskii2020-04-14 09:52:55 +0300
commitdf254a7445a86dc25d133f2d79be8096190a8b96 (patch)
treec6094f663793c61f4243483c0c6d310205005a83 /src/w32image.c
parent7a9fb5d55c9bf612a38348d59e769ee915175e28 (diff)
downloademacs-df254a7445a86dc25d133f2d79be8096190a8b96.tar.gz
emacs-df254a7445a86dc25d133f2d79be8096190a8b96.zip
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.
Diffstat (limited to 'src/w32image.c')
-rw-r--r--src/w32image.c306
1 files changed, 306 insertions, 0 deletions
diff --git a/src/w32image.c b/src/w32image.c
new file mode 100644
index 00000000000..fe32660f712
--- /dev/null
+++ b/src/w32image.c
@@ -0,0 +1,306 @@
1/* Implementation of MS-Windows native image API via the GDI+ library.
2
3Copyright (C) 2020 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or (at
10your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
19
20/* Written by Juan Jose Garcia-Ripoll <juanjose.garciaripoll@gmail.com>. */
21
22#include <config.h>
23#include "lisp.h"
24#include "dispextern.h"
25#define COBJMACROS
26#include <objidl.h>
27#include <wtypes.h>
28#include <gdiplus.h>
29#include <shlwapi.h>
30#include "w32common.h"
31#include "w32term.h"
32#include "frame.h"
33#include "coding.h"
34
35/*#define LINK_GDIPLUS_STATICALLY 1*/
36
37#ifndef LINK_GDIPLUS_STATICALLY
38DEF_DLL_FN (GpStatus, GdiplusStartup, (ULONG_PTR *, GdiplusStartupInput *, GdiplusStartupOutput *));
39DEF_DLL_FN (VOID, GdiplusShutdown, (ULONG_PTR));
40DEF_DLL_FN (GpStatus, GdipGetPropertyItemSize, (GpImage *, PROPID, UINT *));
41DEF_DLL_FN (GpStatus, GdipGetPropertyItem, (GpImage *, PROPID, UINT, PropertyItem *));
42DEF_DLL_FN (GpStatus, GdipImageGetFrameDimensionsCount, (GpImage *, UINT *));
43DEF_DLL_FN (GpStatus, GdipImageGetFrameDimensionsList, (GpImage *, GUID *, UINT));
44DEF_DLL_FN (GpStatus, GdipImageGetFrameCount, (GpImage *, GDIPCONST GUID *, UINT *));
45DEF_DLL_FN (GpStatus, GdipImageSelectActiveFrame, (GpImage*, GDIPCONST GUID *, UINT));
46DEF_DLL_FN (GpStatus, GdipCreateBitmapFromFile, (WCHAR *, GpBitmap **));
47DEF_DLL_FN (GpStatus, GdipCreateBitmapFromStream, (IStream *, GpBitmap **));
48DEF_DLL_FN (IStream *, SHCreateMemStream, (const BYTE *pInit, UINT cbInit));
49DEF_DLL_FN (GpStatus, GdipCreateHBITMAPFromBitmap, (GpBitmap *, HBITMAP *, ARGB));
50DEF_DLL_FN (GpStatus, GdipDisposeImage, (GpImage *));
51DEF_DLL_FN (GpStatus, GdipGetImageHeight, (GpImage *, UINT *));
52DEF_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
60bool
61w32_gdiplus_startup (void)
62{
63 HANDLE gdiplus_lib, shlwapi_lib;
64 GpStatus status;
65
66 if (gdip_initialized < 0)
67 return 0;
68 else if (gdip_initialized)
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
83 LOAD_DLL_FN (gdiplus_lib, GdiplusStartup);
84 LOAD_DLL_FN (gdiplus_lib, GdiplusShutdown);
85 LOAD_DLL_FN (gdiplus_lib, GdipGetPropertyItemSize);
86 LOAD_DLL_FN (gdiplus_lib, GdipGetPropertyItem);
87 LOAD_DLL_FN (gdiplus_lib, GdipImageGetFrameDimensionsCount);
88 LOAD_DLL_FN (gdiplus_lib, GdipImageGetFrameDimensionsList);
89 LOAD_DLL_FN (gdiplus_lib, GdipImageGetFrameCount);
90 LOAD_DLL_FN (gdiplus_lib, GdipImageSelectActiveFrame);
91 LOAD_DLL_FN (gdiplus_lib, GdipCreateBitmapFromFile);
92 LOAD_DLL_FN (gdiplus_lib, GdipCreateBitmapFromStream);
93 LOAD_DLL_FN (gdiplus_lib, GdipCreateHBITMAPFromBitmap);
94 LOAD_DLL_FN (gdiplus_lib, GdipDisposeImage);
95 LOAD_DLL_FN (gdiplus_lib, GdipGetImageHeight);
96 LOAD_DLL_FN (gdiplus_lib, GdipGetImageWidth);
97 LOAD_DLL_FN (shlwapi_lib, SHCreateMemStream);
98
99# define GdiplusStartup fn_GdiplusStartup
100# define GdiplusShutdown fn_GdiplusShutdown
101# define GdipGetPropertyItemSize fn_GdipGetPropertyItemSize
102# define GdipGetPropertyItem fn_GdipGetPropertyItem
103# define GdipImageGetFrameDimensionsCount fn_GdipImageGetFrameDimensionsCount
104# define GdipImageGetFrameDimensionsList fn_GdipImageGetFrameDimensionsList
105# define GdipImageGetFrameCount fn_GdipImageGetFrameCount
106# define GdipImageSelectActiveFrame fn_GdipImageSelectActiveFrame
107# define GdipCreateBitmapFromFile fn_GdipCreateBitmapFromFile
108# define GdipCreateBitmapFromStream fn_GdipCreateBitmapFromStream
109# define SHCreateMemStream fn_SHCreateMemStream
110# define GdipCreateHBITMAPFromBitmap fn_GdipCreateHBITMAPFromBitmap
111# define GdipDisposeImage fn_GdipDisposeImage
112# define GdipGetImageHeight fn_GdipGetImageHeight
113# define GdipGetImageWidth fn_GdipGetImageWidth
114#endif
115
116 input.GdiplusVersion = 1;
117 input.DebugEventCallback = NULL;
118 input.SuppressBackgroundThread = FALSE;
119 input.SuppressExternalCodecs = FALSE;
120
121 status = GdiplusStartup (&token, &input, &output);
122 if (status == Ok)
123 {
124 gdip_initialized = 1;
125 return 1;
126 }
127 else
128 {
129 gdip_initialized = -1;
130 return 0;
131 }
132}
133
134void
135w32_gdiplus_shutdown (void)
136{
137 GdiplusShutdown (token);
138}
139
140
141static double
142w32_frame_delay (GpBitmap *pBitmap, int frame)
143{
144 UINT size;
145 PropertyItem *propertyItem;
146 double delay = 0.0;
147
148 /* Assume that the image has a property item of type PropertyItemEquipMake.
149 Get the size of that property item. */
150 GdipGetPropertyItemSize (pBitmap, PropertyTagFrameDelay, &size);
151
152 /* Allocate a buffer to receive the property item. */
153 propertyItem = (PropertyItem*)malloc (size);
154 if (propertyItem != NULL)
155 {
156 /* Get the property item. */
157 GdipGetPropertyItem (pBitmap, PropertyTagFrameDelay, size, propertyItem);
158 delay = ((double)propertyItem[frame].length) / 100;
159 if (delay == 0)
160 {
161 /* In GIF files, unfortunately, delay is only specified for the first
162 frame. */
163 delay = ((double)propertyItem[0].length) / 100;
164 }
165 free (propertyItem);
166 }
167 return delay;
168}
169
170static UINT
171w32_select_active_frame (GpBitmap *pBitmap, int frame, int *nframes, double *delay)
172{
173 UINT count, frameCount;
174 GUID pDimensionIDs[1];
175 GpStatus status = Ok;
176
177 status = GdipImageGetFrameDimensionsCount (pBitmap, &count);
178 frameCount = *nframes = 0;
179 *delay = 0.0;
180 if (count)
181 {
182 status = GdipImageGetFrameDimensionsList (pBitmap, pDimensionIDs, 1);
183 status = GdipImageGetFrameCount (pBitmap, &pDimensionIDs[0], &frameCount);
184 if ((status == Ok) && (frameCount > 1))
185 {
186 if (frame < 0 || frame >= frameCount)
187 {
188 status = GenericError;
189 }
190 else
191 {
192 status = GdipImageSelectActiveFrame (pBitmap, &pDimensionIDs[0], frame);
193 *delay = w32_frame_delay (pBitmap, frame);
194 *nframes = frameCount;
195 }
196 }
197 }
198 return status;
199}
200
201static ARGB
202w32_image_bg_color (struct frame *f, struct image *img)
203{
204 /* png_color_16 *image_bg; */
205 Lisp_Object specified_bg
206 = Fplist_get (XCDR (img->spec), QCbackground);
207 Emacs_Color color;
208
209 /* If the user specified a color, try to use it; if not, use the
210 current frame background, ignoring any default background
211 color set by the image. */
212 if (STRINGP (specified_bg)
213 ? w32_defined_color (f, SSDATA (specified_bg), &color, false, false)
214 : (w32_query_frame_background_color (f, &color), true))
215 /* The user specified `:background', use that. */
216 {
217 DWORD red = (((DWORD) color.red) & 0xff00) << 8;
218 DWORD green = ((DWORD) color.green) & 0xff00;
219 DWORD blue = ((DWORD) color.blue) >> 8;
220 return red | green | blue;
221 }
222 return ((DWORD) 0xff000000);
223}
224
225int
226w32_load_image (struct frame *f, struct image *img,
227 Lisp_Object spec_file, Lisp_Object spec_data)
228{
229 Emacs_Pixmap pixmap;
230 GpStatus status = GenericError;
231 GpBitmap *pBitmap;
232 wchar_t filename[MAX_PATH];
233 ARGB bg_color;
234 Lisp_Object lisp_index, metadata;
235 unsigned int index, nframes;
236 double delay;
237
238 eassert (valid_image_p (img->spec));
239
240 /* This function only gets called if init_w32_gdiplus () was invoked. We have
241 a valid token and GDI+ is active. */
242 if (STRINGP (spec_file))
243 {
244 if (w32_unicode_filenames)
245 {
246 filename_to_utf16 (SSDATA (spec_file) , filename);
247 status = GdipCreateBitmapFromFile (filename, &pBitmap);
248 }
249 else
250 {
251 add_to_log ("GDI+ requires w32-unicode-filenames to be T");
252 status = GenericError;
253 }
254 }
255 else if (STRINGP (spec_data))
256 {
257 IStream *pStream = SHCreateMemStream ((BYTE *) SSDATA (spec_data),
258 SBYTES (spec_data));
259 if (pStream != NULL)
260 {
261 status = GdipCreateBitmapFromStream (pStream, &pBitmap);
262 IStream_Release (pStream);
263 }
264 }
265
266 metadata = Qnil;
267 if (status == Ok)
268 {
269 /* In multiframe pictures, select the first one */
270 lisp_index = Fplist_get (XCDR (img->spec), QCindex);
271 index = FIXNUMP (lisp_index) ? XFIXNAT (lisp_index) : 0;
272 status = w32_select_active_frame (pBitmap, index, &nframes, &delay);
273 if ((status == Ok))
274 {
275 if (nframes > 1)
276 metadata = Fcons (Qcount, Fcons (make_fixnum (nframes), metadata));
277 if (delay)
278 metadata = Fcons (Qdelay, Fcons (make_float (delay), metadata));
279 }
280 }
281
282 if (status == Ok)
283 {
284 bg_color = w32_image_bg_color (f, img);
285 status = GdipCreateHBITMAPFromBitmap (pBitmap, &pixmap, bg_color);
286 if (status == Ok)
287 {
288 UINT width, height;
289 GdipGetImageWidth (pBitmap, &width);
290 GdipGetImageHeight (pBitmap, &height);
291 img->width = width;
292 img->height = height;
293 img->pixmap = pixmap;
294 img->lisp_data = metadata;
295 }
296
297 GdipDisposeImage (pBitmap);
298 }
299
300 if (status != Ok)
301 {
302 add_to_log ("Unable to load image %s", img->spec);
303 return 0;
304 }
305 return 1;
306}