Added patch to implement support for loading PNG icon files.

This commit is contained in:
Sebastian Lackner 2016-04-09 04:44:30 +02:00
parent 38db26b6d9
commit b4a404a22a
3 changed files with 438 additions and 0 deletions

View File

@ -327,6 +327,7 @@ patch_enable_all ()
enable_user32_ListBox_Size="$1"
enable_user32_MessageBox_WS_EX_TOPMOST="$1"
enable_user32_Mouse_Message_Hwnd="$1"
enable_user32_PNG_Support="$1"
enable_user32_Refresh_MDI_Menus="$1"
enable_user32_ScrollWindowEx="$1"
enable_user32_SetCoalescableTimer="$1"
@ -1152,6 +1153,9 @@ patch_enable ()
user32-Mouse_Message_Hwnd)
enable_user32_Mouse_Message_Hwnd="$2"
;;
user32-PNG_Support)
enable_user32_PNG_Support="$2"
;;
user32-Refresh_MDI_Menus)
enable_user32_Refresh_MDI_Menus="$2"
;;
@ -6736,6 +6740,21 @@ if test "$enable_user32_Mouse_Message_Hwnd" -eq 1; then
) >> "$patchlist"
fi
# Patchset user32-PNG_Support
# |
# | This patchset fixes the following Wine bugs:
# | * [#38959] Add support for loading PNG icon files
# |
# | Modified files:
# | * dlls/user32/cursoricon.c
# |
if test "$enable_user32_PNG_Support" -eq 1; then
patch_apply user32-PNG_Support/0001-user32-Add-support-for-PNG-icons.-v4.patch
(
echo '+ { "Dmitry Timoshkov", "user32: Add support for PNG icons.", 5 },';
) >> "$patchlist"
fi
# Patchset user32-Refresh_MDI_Menus
# |
# | This patchset fixes the following Wine bugs:

View File

@ -0,0 +1,418 @@
From 58d0e4f6ca6beaa0e0e470fb288780c0001d58b3 Mon Sep 17 00:00:00 2001
From: Dmitry Timoshkov <dmitry@baikal.ru>
Date: Thu, 7 Apr 2016 21:18:44 +0800
Subject: user32: Add support for PNG icons. (v5)
---
dlls/user32/cursoricon.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 334 insertions(+), 5 deletions(-)
diff --git a/dlls/user32/cursoricon.c b/dlls/user32/cursoricon.c
index 4de6b28..a8cd135 100644
--- a/dlls/user32/cursoricon.c
+++ b/dlls/user32/cursoricon.c
@@ -6,6 +6,8 @@
* 1997 Alex Korobka
* 1998 Turchanov Sergey
* 2007 Henri Verbeet
+ * Copyright 2009 Vincent Povirk for CodeWeavers
+ * Copyright 2016 Dmitry Timoshkov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -29,6 +31,9 @@
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
+#ifdef HAVE_PNG_H
+#include <png.h>
+#endif
#include "windef.h"
#include "winbase.h"
@@ -43,6 +48,7 @@
#include "wine/list.h"
#include "wine/unicode.h"
#include "wine/debug.h"
+#include "wine/library.h"
WINE_DEFAULT_DEBUG_CHANNEL(cursor);
WINE_DECLARE_DEBUG_CHANNEL(icon);
@@ -71,6 +77,11 @@ typedef struct
#include "poppack.h"
+#define RIFF_FOURCC( c0, c1, c2, c3 ) \
+ ( (DWORD)(BYTE)(c0) | ( (DWORD)(BYTE)(c1) << 8 ) | \
+ ( (DWORD)(BYTE)(c2) << 16 ) | ( (DWORD)(BYTE)(c3) << 24 ) )
+#define PNG_SIGN RIFF_FOURCC(0x89,'P','N','G')
+
static HDC screen_dc;
static const WCHAR DISPLAYW[] = {'D','I','S','P','L','A','Y',0};
@@ -119,6 +130,307 @@ struct animated_cursoricon_object
HICON frames[1]; /* list of animated cursor frames */
};
+#ifdef SONAME_LIBPNG
+
+static void *libpng_handle;
+#define MAKE_FUNCPTR(f) static typeof(f) * p##f
+MAKE_FUNCPTR(png_create_read_struct);
+MAKE_FUNCPTR(png_create_info_struct);
+MAKE_FUNCPTR(png_destroy_read_struct);
+MAKE_FUNCPTR(png_error);
+MAKE_FUNCPTR(png_get_bit_depth);
+MAKE_FUNCPTR(png_get_color_type);
+MAKE_FUNCPTR(png_get_error_ptr);
+MAKE_FUNCPTR(png_get_image_height);
+MAKE_FUNCPTR(png_get_image_width);
+MAKE_FUNCPTR(png_get_io_ptr);
+MAKE_FUNCPTR(png_read_image);
+MAKE_FUNCPTR(png_read_info);
+MAKE_FUNCPTR(png_read_update_info);
+MAKE_FUNCPTR(png_set_bgr);
+MAKE_FUNCPTR(png_set_crc_action);
+MAKE_FUNCPTR(png_set_error_fn);
+MAKE_FUNCPTR(png_set_expand);
+MAKE_FUNCPTR(png_set_gray_to_rgb);
+MAKE_FUNCPTR(png_set_read_fn);
+#undef MAKE_FUNCPTR
+
+static BOOL load_libpng(void)
+{
+ USER_Lock();
+
+ if (!libpng_handle && (libpng_handle = wine_dlopen(SONAME_LIBPNG, RTLD_NOW, NULL, 0)) != NULL)
+ {
+#define LOAD_FUNCPTR(f) \
+ if ((p##f = wine_dlsym(libpng_handle, #f, NULL, 0)) == NULL) \
+ { \
+ libpng_handle = NULL; \
+ USER_Unlock(); \
+ return FALSE; \
+ }
+ LOAD_FUNCPTR(png_create_read_struct);
+ LOAD_FUNCPTR(png_create_info_struct);
+ LOAD_FUNCPTR(png_destroy_read_struct);
+ LOAD_FUNCPTR(png_error);
+ LOAD_FUNCPTR(png_get_bit_depth);
+ LOAD_FUNCPTR(png_get_color_type);
+ LOAD_FUNCPTR(png_get_error_ptr);
+ LOAD_FUNCPTR(png_get_image_height);
+ LOAD_FUNCPTR(png_get_image_width);
+ LOAD_FUNCPTR(png_get_io_ptr);
+ LOAD_FUNCPTR(png_read_image);
+ LOAD_FUNCPTR(png_read_info);
+ LOAD_FUNCPTR(png_read_update_info);
+ LOAD_FUNCPTR(png_set_bgr);
+ LOAD_FUNCPTR(png_set_crc_action);
+ LOAD_FUNCPTR(png_set_error_fn);
+ LOAD_FUNCPTR(png_set_expand);
+ LOAD_FUNCPTR(png_set_gray_to_rgb);
+ LOAD_FUNCPTR(png_set_read_fn);
+#undef LOAD_FUNCPTR
+ }
+
+ USER_Unlock();
+ return TRUE;
+}
+
+static void user_error_fn(png_structp png_ptr, png_const_charp error_message)
+{
+ jmp_buf *pjmpbuf;
+
+ /* This uses setjmp/longjmp just like the default. We can't use the
+ * default because there's no way to access the jmp buffer in the png_struct
+ * that works in 1.2 and 1.4 and allows us to dynamically load libpng. */
+ WARN("PNG error: %s\n", debugstr_a(error_message));
+ pjmpbuf = ppng_get_error_ptr(png_ptr);
+ longjmp(*pjmpbuf, 1);
+}
+
+static void user_warning_fn(png_structp png_ptr, png_const_charp warning_message)
+{
+ WARN("PNG warning: %s\n", debugstr_a(warning_message));
+}
+
+struct png_wrapper
+{
+ png_structp png_ptr;
+ png_infop info_ptr;
+ int width, height, bpp;
+ const char *buffer;
+ int size, pos;
+};
+
+static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ struct png_wrapper *png = ppng_get_io_ptr(png_ptr);
+
+ if (png->size - png->pos >= length)
+ {
+ memcpy(data, png->buffer + png->pos, length);
+ png->pos += length;
+ }
+ else
+ {
+ ppng_error(png->png_ptr, "failed to read PNG data");
+ }
+}
+
+static BOOL create_png_decoder(struct png_wrapper *png)
+{
+ jmp_buf jmpbuf;
+ int color_type, bit_depth;
+
+ if (!load_libpng()) return FALSE;
+
+ /* initialize libpng */
+ png->png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png->png_ptr) return FALSE;
+
+ png->info_ptr = ppng_create_info_struct(png->png_ptr);
+ if (!png->info_ptr)
+ {
+ ppng_destroy_read_struct(&png->png_ptr, NULL, NULL);
+ return FALSE;
+ }
+
+ /* set up setjmp/longjmp error handling */
+ if (setjmp(jmpbuf))
+ {
+ ppng_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
+ return FALSE;
+ }
+
+ ppng_set_error_fn(png->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
+ ppng_set_crc_action(png->png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
+
+ /* set up custom i/o handling */
+ ppng_set_read_fn(png->png_ptr, png, user_read_data);
+
+ /* read the header */
+ ppng_read_info(png->png_ptr, png->info_ptr);
+
+ color_type = ppng_get_color_type(png->png_ptr, png->info_ptr);
+ bit_depth = ppng_get_bit_depth(png->png_ptr, png->info_ptr);
+
+ /* expand grayscale image data to rgb */
+ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ ppng_set_gray_to_rgb(png->png_ptr);
+
+ /* expand palette image data to rgb */
+ if (color_type == PNG_COLOR_TYPE_PALETTE || bit_depth < 8)
+ ppng_set_expand(png->png_ptr);
+
+ /* update color type information */
+ ppng_read_update_info(png->png_ptr, png->info_ptr);
+
+ color_type = ppng_get_color_type(png->png_ptr, png->info_ptr);
+ bit_depth = ppng_get_bit_depth(png->png_ptr, png->info_ptr);
+
+ png->bpp = 0;
+
+ switch (color_type)
+ {
+ case PNG_COLOR_TYPE_RGB:
+ if (bit_depth == 8)
+ png->bpp = 24;
+ break;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ if (bit_depth == 8)
+ {
+ ppng_set_bgr(png->png_ptr);
+ png->bpp = 32;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (!png->bpp)
+ {
+ FIXME("unsupported PNG color format %d, %d bpp\n", color_type, bit_depth);
+ ppng_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
+ return FALSE;
+ }
+
+ png->width = ppng_get_image_width(png->png_ptr, png->info_ptr);
+ png->height = ppng_get_image_height(png->png_ptr, png->info_ptr);
+
+ return TRUE;
+}
+
+static void destroy_png_decoder(struct png_wrapper *png)
+{
+ ppng_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
+}
+
+static BOOL get_png_info(const void *png_data, DWORD size, int *width, int *height, int *bpp)
+{
+ static const char png_sig[8] = { 0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a };
+ struct png_wrapper png;
+
+ if (size < sizeof(png_sig) || memcmp(png_data, png_sig, sizeof(png_sig)) != 0)
+ return FALSE;
+
+ png.buffer = png_data;
+ png.size = size;
+ png.pos = 0;
+
+ if (!create_png_decoder(&png)) return FALSE;
+
+ *width = png.width;
+ *height = png.height;
+ *bpp = png.bpp;
+
+ destroy_png_decoder(&png);
+ return TRUE;
+}
+
+static BITMAPINFO *load_png(const char *png_data, DWORD *size)
+{
+ static const char png_sig[8] = { 0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a };
+ struct png_wrapper png;
+ png_bytep *row_pointers;
+ int rowbytes, image_size, mask_size = 0, i;
+ BITMAPINFO *info;
+ unsigned char *image_data;
+
+ if (*size < sizeof(png_sig) || memcmp(png_data, png_sig, sizeof(png_sig)) != 0)
+ return NULL;
+
+ png.buffer = png_data;
+ png.size = *size;
+ png.pos = 0;
+
+ if (!create_png_decoder(&png)) return NULL;
+
+ rowbytes = (png.width * png.bpp + 7) / 8;
+ image_size = png.height * rowbytes;
+ if (png.bpp != 32) /* add a mask if there is no alpha */
+ mask_size = (png.width + 7) / 8 * png.height;
+
+ info = HeapAlloc(GetProcessHeap(), 0, sizeof(BITMAPINFOHEADER) + image_size + mask_size);
+ if (!info)
+ {
+ destroy_png_decoder(&png);
+ return NULL;
+ }
+
+ image_data = (unsigned char *)info + sizeof(BITMAPINFOHEADER);
+ memset(image_data + image_size, 0, mask_size);
+
+ row_pointers = HeapAlloc(GetProcessHeap(), 0, png.height * sizeof(png_bytep));
+ if (!row_pointers)
+ {
+ HeapFree(GetProcessHeap(), 0, info);
+ destroy_png_decoder(&png);
+ return NULL;
+ }
+
+ /* upside down */
+ for (i = 0; i < png.height; i++)
+ row_pointers[i] = image_data + (png.height - i - 1) * rowbytes;
+
+ ppng_read_image(png.png_ptr, row_pointers);
+
+ HeapFree(GetProcessHeap(), 0, row_pointers);
+
+ info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ info->bmiHeader.biWidth = png.width;
+ info->bmiHeader.biHeight = png.height * 2;
+ info->bmiHeader.biPlanes = 1;
+ info->bmiHeader.biBitCount = png.bpp;
+ info->bmiHeader.biCompression = BI_RGB;
+ info->bmiHeader.biSizeImage = image_size;
+ info->bmiHeader.biXPelsPerMeter = 0;
+ info->bmiHeader.biYPelsPerMeter = 0;
+ info->bmiHeader.biClrUsed = 0;
+ info->bmiHeader.biClrImportant = 0;
+
+ *size = sizeof(BITMAPINFOHEADER) + image_size + mask_size;
+
+ destroy_png_decoder(&png);
+
+ return info;
+}
+
+#else /* SONAME_LIBPNG */
+
+static BOOL get_png_info(const void *png_data, DWORD size, int *width, int *height, int *bpp)
+{
+ ERR("Trying to load PNG icon, but PNG support is not compiled in.\n");
+ return FALSE;
+}
+
+static BITMAPINFO *load_png( const char *png, DWORD *max_size )
+{
+ ERR("Trying to load PNG icon, but PNG support is not compiled in.\n");
+ return NULL;
+}
+
+#endif
+
static HICON alloc_icon_handle( BOOL is_ani, UINT num_steps )
{
struct cursoricon_object *obj;
@@ -534,6 +846,8 @@ static int CURSORICON_FindBestIcon( LPCVOID dir, DWORD size, fnGetCIEntry get_en
/* Find Best Colors for Best Fit */
for ( i = 0; get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
{
+ TRACE("entry %d: %d x %d, %d bpp\n", i, cx, cy, bits);
+
if(abs(width - cx) == iXDiff && abs(height - cy) == iYDiff)
{
iTempColorDiff = abs(depth - bits);
@@ -678,7 +992,11 @@ static BOOL CURSORICON_GetFileEntry( LPCVOID dir, DWORD size, int n,
return FALSE;
entry = &filedir->idEntries[n];
info = (const BITMAPINFOHEADER *)((const char *)dir + entry->dwDIBOffset);
- if (info->biSize != sizeof(BITMAPCOREHEADER))
+ if (info->biSize == PNG_SIGN)
+ {
+ return get_png_info(info, size, width, height, bits);
+ }
+ else if (info->biSize != sizeof(BITMAPCOREHEADER))
{
if ((const char *)(info + 1) - (const char *)dir > size) return FALSE;
*bits = info->biBitCount;
@@ -822,6 +1140,21 @@ static HICON create_icon_from_bmi( const BITMAPINFO *bmi, DWORD maxsize, HMODULE
/* Check bitmap header */
+ if (bmi->bmiHeader.biSize == PNG_SIGN)
+ {
+ BITMAPINFO *bmi_png;
+
+ bmi_png = load_png( (const char *)bmi, &maxsize );
+ if (bmi_png)
+ {
+ hObj = create_icon_from_bmi( bmi_png, maxsize, module, resname,
+ rsrc, hotspot, bIcon, width, height, cFlag );
+ HeapFree( GetProcessHeap(), 0, bmi_png );
+ return hObj;
+ }
+ return 0;
+ }
+
if (maxsize < sizeof(BITMAPCOREHEADER))
{
WARN( "invalid size %u\n", maxsize );
@@ -1003,10 +1336,6 @@ done:
/**********************************************************************
* .ANI cursor support
*/
-#define RIFF_FOURCC( c0, c1, c2, c3 ) \
- ( (DWORD)(BYTE)(c0) | ( (DWORD)(BYTE)(c1) << 8 ) | \
- ( (DWORD)(BYTE)(c2) << 16 ) | ( (DWORD)(BYTE)(c3) << 24 ) )
-
#define ANI_RIFF_ID RIFF_FOURCC('R', 'I', 'F', 'F')
#define ANI_LIST_ID RIFF_FOURCC('L', 'I', 'S', 'T')
#define ANI_ACON_ID RIFF_FOURCC('A', 'C', 'O', 'N')
--
2.7.1

View File

@ -0,0 +1 @@
Fixes: [38959] Add support for loading PNG icon files