hidclass.sys: Return STATUS_INVALID_USER_BUFFER if buffer_len is 0.
[wine.git] / dlls / user32 / png.c
bloba994c6de5c29a65ed0b931e72f129bd29df253e1
1 /*
2 * PNG icon support
4 * Copyright 2018 Dmitry Timoshkov
5 * Copyright 2019 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #if 0
23 #pragma makedep unix
24 #endif
26 #include "config.h"
27 #include "wine/port.h"
29 #include <assert.h>
30 #include <stdarg.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #ifdef SONAME_LIBPNG
34 #include <png.h>
35 #endif
37 #include "ntstatus.h"
38 #define WIN32_NO_STATUS
39 #include "windef.h"
40 #include "winbase.h"
41 #include "wingdi.h"
42 #include "winerror.h"
43 #include "winnls.h"
44 #include "winternl.h"
45 #include "wine/exception.h"
46 #include "wine/server.h"
47 #include "controls.h"
48 #include "win.h"
49 #include "user_private.h"
50 #include "wine/list.h"
51 #include "wine/unicode.h"
52 #include "wine/debug.h"
54 #ifdef SONAME_LIBPNG
56 WINE_DEFAULT_DEBUG_CHANNEL(cursor);
58 static void *libpng_handle;
59 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
60 MAKE_FUNCPTR(png_create_read_struct);
61 MAKE_FUNCPTR(png_create_info_struct);
62 MAKE_FUNCPTR(png_destroy_read_struct);
63 MAKE_FUNCPTR(png_error);
64 MAKE_FUNCPTR(png_get_bit_depth);
65 MAKE_FUNCPTR(png_get_color_type);
66 MAKE_FUNCPTR(png_get_error_ptr);
67 MAKE_FUNCPTR(png_get_image_height);
68 MAKE_FUNCPTR(png_get_image_width);
69 MAKE_FUNCPTR(png_get_io_ptr);
70 MAKE_FUNCPTR(png_read_image);
71 MAKE_FUNCPTR(png_read_info);
72 MAKE_FUNCPTR(png_read_update_info);
73 MAKE_FUNCPTR(png_set_bgr);
74 MAKE_FUNCPTR(png_set_crc_action);
75 MAKE_FUNCPTR(png_set_error_fn);
76 MAKE_FUNCPTR(png_set_expand);
77 MAKE_FUNCPTR(png_set_gray_to_rgb);
78 MAKE_FUNCPTR(png_set_read_fn);
79 #undef MAKE_FUNCPTR
81 static void user_error_fn(png_structp png_ptr, png_const_charp error_message)
83 jmp_buf *pjmpbuf;
85 /* This uses setjmp/longjmp just like the default. We can't use the
86 * default because there's no way to access the jmp buffer in the png_struct
87 * that works in 1.2 and 1.4 and allows us to dynamically load libpng. */
88 WARN("PNG error: %s\n", debugstr_a(error_message));
89 pjmpbuf = ppng_get_error_ptr(png_ptr);
90 longjmp(*pjmpbuf, 1);
93 static void user_warning_fn(png_structp png_ptr, png_const_charp warning_message)
95 WARN("PNG warning: %s\n", debugstr_a(warning_message));
98 struct png_wrapper
100 const char *buffer;
101 size_t size, pos;
104 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
106 struct png_wrapper *png = ppng_get_io_ptr(png_ptr);
108 if (png->size - png->pos >= length)
110 memcpy(data, png->buffer + png->pos, length);
111 png->pos += length;
113 else
115 ppng_error(png_ptr, "failed to read PNG data");
119 static unsigned be_uint(unsigned val)
121 union
123 unsigned val;
124 unsigned char c[4];
125 } u;
127 u.val = val;
128 return (u.c[0] << 24) | (u.c[1] << 16) | (u.c[2] << 8) | u.c[3];
131 static BOOL CDECL get_png_info(const void *png_data, DWORD size, int *width, int *height, int *bpp)
133 static const char png_sig[8] = { 0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a };
134 static const char png_IHDR[8] = { 0,0,0,0x0d,'I','H','D','R' };
135 const struct
137 char png_sig[8];
138 char ihdr_sig[8];
139 unsigned width, height;
140 char bit_depth, color_type, compression, filter, interlace;
141 } *png = png_data;
143 if (size < sizeof(*png)) return FALSE;
144 if (memcmp(png->png_sig, png_sig, sizeof(png_sig)) != 0) return FALSE;
145 if (memcmp(png->ihdr_sig, png_IHDR, sizeof(png_IHDR)) != 0) return FALSE;
147 *bpp = (png->color_type == PNG_COLOR_TYPE_RGB_ALPHA) ? 32 : 24;
148 *width = be_uint(png->width);
149 *height = be_uint(png->height);
151 return TRUE;
154 static BITMAPINFO * CDECL load_png(const char *png_data, DWORD *size)
156 struct png_wrapper png;
157 png_structp png_ptr;
158 png_infop info_ptr;
159 png_bytep *row_pointers = NULL;
160 jmp_buf jmpbuf;
161 int color_type, bit_depth, bpp, width, height;
162 int rowbytes, image_size, mask_size = 0, i;
163 BITMAPINFO *info = NULL;
164 unsigned char *image_data;
166 if (!get_png_info(png_data, *size, &width, &height, &bpp)) return NULL;
168 png.buffer = png_data;
169 png.size = *size;
170 png.pos = 0;
172 /* initialize libpng */
173 png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
174 if (!png_ptr) return NULL;
176 info_ptr = ppng_create_info_struct(png_ptr);
177 if (!info_ptr)
179 ppng_destroy_read_struct(&png_ptr, NULL, NULL);
180 return NULL;
183 /* set up setjmp/longjmp error handling */
184 if (setjmp(jmpbuf))
186 free(row_pointers);
187 RtlFreeHeap(GetProcessHeap(), 0, info);
188 ppng_destroy_read_struct(&png_ptr, &info_ptr, NULL);
189 return NULL;
192 ppng_set_error_fn(png_ptr, jmpbuf, user_error_fn, user_warning_fn);
193 ppng_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
195 /* set up custom i/o handling */
196 ppng_set_read_fn(png_ptr, &png, user_read_data);
198 /* read the header */
199 ppng_read_info(png_ptr, info_ptr);
201 color_type = ppng_get_color_type(png_ptr, info_ptr);
202 bit_depth = ppng_get_bit_depth(png_ptr, info_ptr);
204 /* expand grayscale image data to rgb */
205 if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
206 ppng_set_gray_to_rgb(png_ptr);
208 /* expand palette image data to rgb */
209 if (color_type == PNG_COLOR_TYPE_PALETTE || bit_depth < 8)
210 ppng_set_expand(png_ptr);
212 /* update color type information */
213 ppng_read_update_info(png_ptr, info_ptr);
215 color_type = ppng_get_color_type(png_ptr, info_ptr);
216 bit_depth = ppng_get_bit_depth(png_ptr, info_ptr);
218 bpp = 0;
220 switch (color_type)
222 case PNG_COLOR_TYPE_RGB:
223 if (bit_depth == 8)
224 bpp = 24;
225 break;
227 case PNG_COLOR_TYPE_RGB_ALPHA:
228 if (bit_depth == 8)
230 ppng_set_bgr(png_ptr);
231 bpp = 32;
233 break;
235 default:
236 break;
239 if (!bpp)
241 FIXME("unsupported PNG color format %d, %d bpp\n", color_type, bit_depth);
242 ppng_destroy_read_struct(&png_ptr, &info_ptr, NULL);
243 return NULL;
246 width = ppng_get_image_width(png_ptr, info_ptr);
247 height = ppng_get_image_height(png_ptr, info_ptr);
249 rowbytes = (width * bpp + 7) / 8;
250 image_size = height * rowbytes;
251 if (bpp != 32) /* add a mask if there is no alpha */
252 mask_size = (width + 7) / 8 * height;
254 info = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(BITMAPINFOHEADER) + image_size + mask_size);
255 if (!info)
257 ppng_destroy_read_struct(&png_ptr, &info_ptr, NULL);
258 return NULL;
261 image_data = (unsigned char *)info + sizeof(BITMAPINFOHEADER);
262 memset(image_data + image_size, 0, mask_size);
264 row_pointers = malloc(height * sizeof(png_bytep));
265 if (!row_pointers)
267 RtlFreeHeap(GetProcessHeap(), 0, info);
268 ppng_destroy_read_struct(&png_ptr, &info_ptr, NULL);
269 return NULL;
272 /* upside down */
273 for (i = 0; i < height; i++)
274 row_pointers[i] = image_data + (height - i - 1) * rowbytes;
276 ppng_read_image(png_ptr, row_pointers);
277 free(row_pointers);
278 ppng_destroy_read_struct(&png_ptr, &info_ptr, NULL);
280 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
281 info->bmiHeader.biWidth = width;
282 info->bmiHeader.biHeight = height * 2;
283 info->bmiHeader.biPlanes = 1;
284 info->bmiHeader.biBitCount = bpp;
285 info->bmiHeader.biCompression = BI_RGB;
286 info->bmiHeader.biSizeImage = image_size;
287 info->bmiHeader.biXPelsPerMeter = 0;
288 info->bmiHeader.biYPelsPerMeter = 0;
289 info->bmiHeader.biClrUsed = 0;
290 info->bmiHeader.biClrImportant = 0;
292 *size = sizeof(BITMAPINFOHEADER) + image_size + mask_size;
293 return info;
296 static const struct png_funcs png_funcs =
298 get_png_info,
299 load_png
302 NTSTATUS CDECL __wine_init_unix_lib( HMODULE module, DWORD reason, const void *ptr_in, void *ptr_out )
304 if (reason != DLL_PROCESS_ATTACH) return STATUS_SUCCESS;
306 if (!(libpng_handle = dlopen( SONAME_LIBPNG, RTLD_NOW )))
308 WARN( "failed to load %s\n", SONAME_LIBPNG );
309 return STATUS_DLL_NOT_FOUND;
311 #define LOAD_FUNCPTR(f) \
312 if (!(p##f = dlsym( libpng_handle, #f ))) \
314 WARN( "%s not found in %s\n", #f, SONAME_LIBPNG ); \
315 return STATUS_PROCEDURE_NOT_FOUND; \
317 LOAD_FUNCPTR(png_create_read_struct);
318 LOAD_FUNCPTR(png_create_info_struct);
319 LOAD_FUNCPTR(png_destroy_read_struct);
320 LOAD_FUNCPTR(png_error);
321 LOAD_FUNCPTR(png_get_bit_depth);
322 LOAD_FUNCPTR(png_get_color_type);
323 LOAD_FUNCPTR(png_get_error_ptr);
324 LOAD_FUNCPTR(png_get_image_height);
325 LOAD_FUNCPTR(png_get_image_width);
326 LOAD_FUNCPTR(png_get_io_ptr);
327 LOAD_FUNCPTR(png_read_image);
328 LOAD_FUNCPTR(png_read_info);
329 LOAD_FUNCPTR(png_read_update_info);
330 LOAD_FUNCPTR(png_set_bgr);
331 LOAD_FUNCPTR(png_set_crc_action);
332 LOAD_FUNCPTR(png_set_error_fn);
333 LOAD_FUNCPTR(png_set_expand);
334 LOAD_FUNCPTR(png_set_gray_to_rgb);
335 LOAD_FUNCPTR(png_set_read_fn);
336 #undef LOAD_FUNCPTR
338 *(const struct png_funcs **)ptr_out = &png_funcs;
339 return STATUS_SUCCESS;
342 #endif /* SONAME_LIBPNG */