diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index 5d499a07..7b695d45 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -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: diff --git a/patches/user32-PNG_Support/0001-user32-Add-support-for-PNG-icons.-v4.patch b/patches/user32-PNG_Support/0001-user32-Add-support-for-PNG-icons.-v4.patch new file mode 100644 index 00000000..fd48bc15 --- /dev/null +++ b/patches/user32-PNG_Support/0001-user32-Add-support-for-PNG-icons.-v4.patch @@ -0,0 +1,418 @@ +From 58d0e4f6ca6beaa0e0e470fb288780c0001d58b3 Mon Sep 17 00:00:00 2001 +From: Dmitry Timoshkov +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 + #include + #include ++#ifdef HAVE_PNG_H ++#include ++#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 + diff --git a/patches/user32-PNG_Support/definition b/patches/user32-PNG_Support/definition new file mode 100644 index 00000000..903a9098 --- /dev/null +++ b/patches/user32-PNG_Support/definition @@ -0,0 +1 @@ +Fixes: [38959] Add support for loading PNG icon files