[PATCH] user32: Add support for PNG icons.

Dmitry Timoshkov dmitry at baikal.ru
Thu Mar 29 09:45:14 CDT 2018


Signed-off-by: Dmitry Timoshkov <dmitry at baikal.ru>
---
 dlls/user32/Makefile.in  |   1 +
 dlls/user32/cursoricon.c | 337 ++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 334 insertions(+), 4 deletions(-)

diff --git a/dlls/user32/Makefile.in b/dlls/user32/Makefile.in
index 246c424155..931a715763 100644
--- a/dlls/user32/Makefile.in
+++ b/dlls/user32/Makefile.in
@@ -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 = \
diff --git a/dlls/user32/cursoricon.c b/dlls/user32/cursoricon.c
index 8e272f09ce..1cb46050d4 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,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,307 @@ 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
+{
+    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;
@@ -523,6 +835,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 +994,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 +1142,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 +1334,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.16.2




More information about the wine-devel mailing list