user32: Add support for PNG icons.
Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
f80b1f45ff
commit
728255cf2c
|
@ -2,6 +2,7 @@ EXTRADEFS = -D_USER32_ -D_WINABLE_
|
|||
MODULE = user32.dll
|
||||
IMPORTLIB = user32
|
||||
IMPORTS = gdi32 version advapi32
|
||||
EXTRAINCL = $(PNG_CFLAGS)
|
||||
DELAYIMPORTS = imm32 usp10
|
||||
|
||||
C_SRCS = \
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
* Cursor and icon support
|
||||
*
|
||||
* Copyright 1995 Alexandre Julliard
|
||||
* 1996 Martin Von Loewis
|
||||
* 1997 Alex Korobka
|
||||
* 1998 Turchanov Sergey
|
||||
* 2007 Henri Verbeet
|
||||
* Copyright 1996 Martin Von Loewis
|
||||
* Copyright 1997 Alex Korobka
|
||||
* Copyright 1998 Turchanov Sergey
|
||||
* Copyright 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,11 +48,17 @@
|
|||
#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);
|
||||
WINE_DECLARE_DEBUG_CHANNEL(resource);
|
||||
|
||||
#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 struct list icon_cache = LIST_INIT( icon_cache );
|
||||
|
||||
/**********************************************************************
|
||||
|
@ -108,6 +119,303 @@ static int get_display_bpp(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#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
|
||||
{
|
||||
const char *buffer;
|
||||
size_t 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_ptr, "failed to read PNG data");
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned be_uint(unsigned val)
|
||||
{
|
||||
union
|
||||
{
|
||||
unsigned val;
|
||||
unsigned char c[4];
|
||||
} u;
|
||||
|
||||
u.val = val;
|
||||
return (u.c[0] << 24) | (u.c[1] << 16) | (u.c[2] << 8) | u.c[3];
|
||||
}
|
||||
|
||||
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 };
|
||||
static const char png_IHDR[8] = { 0,0,0,0x0d,'I','H','D','R' };
|
||||
const struct
|
||||
{
|
||||
char png_sig[8];
|
||||
char ihdr_sig[8];
|
||||
unsigned width, height;
|
||||
char bit_depth, color_type, compression, filter, interlace;
|
||||
} *png = png_data;
|
||||
|
||||
if (size < sizeof(*png)) return FALSE;
|
||||
if (memcmp(png->png_sig, png_sig, sizeof(png_sig)) != 0) return FALSE;
|
||||
if (memcmp(png->ihdr_sig, png_IHDR, sizeof(png_IHDR)) != 0) return FALSE;
|
||||
|
||||
*bpp = (png->color_type == PNG_COLOR_TYPE_RGB_ALPHA) ? 32 : 24;
|
||||
*width = be_uint(png->width);
|
||||
*height = be_uint(png->height);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BITMAPINFO *load_png(const char *png_data, DWORD *size)
|
||||
{
|
||||
struct png_wrapper png;
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
png_bytep *row_pointers = NULL;
|
||||
jmp_buf jmpbuf;
|
||||
int color_type, bit_depth, bpp, width, height;
|
||||
int rowbytes, image_size, mask_size = 0, i;
|
||||
BITMAPINFO *info = NULL;
|
||||
unsigned char *image_data;
|
||||
|
||||
if (!get_png_info(png_data, *size, &width, &height, &bpp))
|
||||
return NULL;
|
||||
|
||||
if (!load_libpng()) return NULL;
|
||||
|
||||
png.buffer = png_data;
|
||||
png.size = *size;
|
||||
png.pos = 0;
|
||||
|
||||
/* initialize libpng */
|
||||
png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if (!png_ptr) return NULL;
|
||||
|
||||
info_ptr = ppng_create_info_struct(png_ptr);
|
||||
if (!info_ptr)
|
||||
{
|
||||
ppng_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* set up setjmp/longjmp error handling */
|
||||
if (setjmp(jmpbuf))
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, row_pointers);
|
||||
HeapFree(GetProcessHeap(), 0, info);
|
||||
ppng_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ppng_set_error_fn(png_ptr, jmpbuf, user_error_fn, user_warning_fn);
|
||||
ppng_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
|
||||
|
||||
/* set up custom i/o handling */
|
||||
ppng_set_read_fn(png_ptr, &png, user_read_data);
|
||||
|
||||
/* read the header */
|
||||
ppng_read_info(png_ptr, info_ptr);
|
||||
|
||||
color_type = ppng_get_color_type(png_ptr, info_ptr);
|
||||
bit_depth = ppng_get_bit_depth(png_ptr, 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_ptr);
|
||||
|
||||
/* expand palette image data to rgb */
|
||||
if (color_type == PNG_COLOR_TYPE_PALETTE || bit_depth < 8)
|
||||
ppng_set_expand(png_ptr);
|
||||
|
||||
/* update color type information */
|
||||
ppng_read_update_info(png_ptr, info_ptr);
|
||||
|
||||
color_type = ppng_get_color_type(png_ptr, info_ptr);
|
||||
bit_depth = ppng_get_bit_depth(png_ptr, info_ptr);
|
||||
|
||||
bpp = 0;
|
||||
|
||||
switch (color_type)
|
||||
{
|
||||
case PNG_COLOR_TYPE_RGB:
|
||||
if (bit_depth == 8)
|
||||
bpp = 24;
|
||||
break;
|
||||
|
||||
case PNG_COLOR_TYPE_RGB_ALPHA:
|
||||
if (bit_depth == 8)
|
||||
{
|
||||
ppng_set_bgr(png_ptr);
|
||||
bpp = 32;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bpp)
|
||||
{
|
||||
FIXME("unsupported PNG color format %d, %d bpp\n", color_type, bit_depth);
|
||||
ppng_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
width = ppng_get_image_width(png_ptr, info_ptr);
|
||||
height = ppng_get_image_height(png_ptr, info_ptr);
|
||||
|
||||
rowbytes = (width * bpp + 7) / 8;
|
||||
image_size = height * rowbytes;
|
||||
if (bpp != 32) /* add a mask if there is no alpha */
|
||||
mask_size = (width + 7) / 8 * height;
|
||||
|
||||
info = HeapAlloc(GetProcessHeap(), 0, sizeof(BITMAPINFOHEADER) + image_size + mask_size);
|
||||
if (!info)
|
||||
{
|
||||
ppng_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image_data = (unsigned char *)info + sizeof(BITMAPINFOHEADER);
|
||||
memset(image_data + image_size, 0, mask_size);
|
||||
|
||||
row_pointers = HeapAlloc(GetProcessHeap(), 0, height * sizeof(png_bytep));
|
||||
if (!row_pointers)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, info);
|
||||
ppng_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* upside down */
|
||||
for (i = 0; i < height; i++)
|
||||
row_pointers[i] = image_data + (height - i - 1) * rowbytes;
|
||||
|
||||
ppng_read_image(png_ptr, row_pointers);
|
||||
HeapFree(GetProcessHeap(), 0, row_pointers);
|
||||
ppng_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
|
||||
info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
info->bmiHeader.biWidth = width;
|
||||
info->bmiHeader.biHeight = height * 2;
|
||||
info->bmiHeader.biPlanes = 1;
|
||||
info->bmiHeader.biBitCount = 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;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
#else /* SONAME_LIBPNG */
|
||||
|
||||
static BOOL get_png_info(const void *png_data, DWORD size, int *width, int *height, int *bpp)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BITMAPINFO *load_png( const char *png, DWORD *max_size )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static HICON alloc_icon_handle( BOOL is_ani, UINT num_steps )
|
||||
{
|
||||
struct cursoricon_object *obj;
|
||||
|
@ -523,6 +831,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);
|
||||
|
@ -680,6 +990,10 @@ static BOOL CURSORICON_GetFileEntry( LPCVOID dir, DWORD size, int n,
|
|||
entry = &filedir->idEntries[n];
|
||||
if (entry->dwDIBOffset > size - sizeof(info->biSize)) return FALSE;
|
||||
info = (const BITMAPINFOHEADER *)((const char *)dir + entry->dwDIBOffset);
|
||||
|
||||
if (info->biSize == PNG_SIGN)
|
||||
return get_png_info(info, size, width, height, bits);
|
||||
|
||||
if (info->biSize != sizeof(BITMAPCOREHEADER))
|
||||
{
|
||||
if ((const char *)(info + 1) - (const char *)dir > size) return FALSE;
|
||||
|
@ -824,6 +1138,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 );
|
||||
|
@ -1001,10 +1330,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')
|
||||
|
|
Loading…
Reference in New Issue