winemac: Use unixlib interface for dragdrop.c calls.
[wine.git] / dlls / user32 / cursoricon.c
blob3e83ada3ee5309a39d66ae485cb53e4e79ab3968
1 /*
2 * Cursor and icon support
4 * Copyright 1995 Alexandre Julliard
5 * Copyright 1996 Martin Von Loewis
6 * Copyright 1997 Alex Korobka
7 * Copyright 1998 Turchanov Sergey
8 * Copyright 2007 Henri Verbeet
9 * Copyright 2009 Vincent Povirk for CodeWeavers
10 * Copyright 2016 Dmitry Timoshkov
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include <assert.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <png.h>
33 #include "windef.h"
34 #include "winbase.h"
35 #include "wingdi.h"
36 #include "winerror.h"
37 #include "winnls.h"
38 #include "wine/exception.h"
39 #include "wine/server.h"
40 #include "controls.h"
41 #include "win.h"
42 #include "user_private.h"
43 #include "wine/list.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(cursor);
47 WINE_DECLARE_DEBUG_CHANNEL(icon);
48 WINE_DECLARE_DEBUG_CHANNEL(resource);
50 #define RIFF_FOURCC( c0, c1, c2, c3 ) \
51 ( (DWORD)(BYTE)(c0) | ( (DWORD)(BYTE)(c1) << 8 ) | \
52 ( (DWORD)(BYTE)(c2) << 16 ) | ( (DWORD)(BYTE)(c3) << 24 ) )
53 #define PNG_SIGN RIFF_FOURCC(0x89,'P','N','G')
55 /**********************************************************************
56 * User objects management
59 static HBITMAP create_color_bitmap( int width, int height )
61 HDC hdc = get_display_dc();
62 HBITMAP ret = CreateCompatibleBitmap( hdc, width, height );
63 release_display_dc( hdc );
64 return ret;
67 static int get_display_bpp(void)
69 HDC hdc = get_display_dc();
70 int ret = GetDeviceCaps( hdc, BITSPIXEL );
71 release_display_dc( hdc );
72 return ret;
75 static void free_icon_frame( struct cursoricon_frame *frame )
77 if (frame->color) DeleteObject( frame->color );
78 if (frame->alpha) DeleteObject( frame->alpha );
79 if (frame->mask) DeleteObject( frame->mask );
83 /***********************************************************************
84 * map_fileW
86 * Helper function to map a file to memory:
87 * name - file name
88 * [RETURN] ptr - pointer to mapped file
89 * [RETURN] filesize - pointer size of file to be stored if not NULL
91 static const void *map_fileW( LPCWSTR name, LPDWORD filesize )
93 HANDLE hFile, hMapping;
94 LPVOID ptr = NULL;
96 hFile = CreateFileW( name, GENERIC_READ, FILE_SHARE_READ, NULL,
97 OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0 );
98 if (hFile != INVALID_HANDLE_VALUE)
100 hMapping = CreateFileMappingW( hFile, NULL, PAGE_READONLY, 0, 0, NULL );
101 if (hMapping)
103 ptr = MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 );
104 CloseHandle( hMapping );
105 if (filesize)
106 *filesize = GetFileSize( hFile, NULL );
108 CloseHandle( hFile );
110 return ptr;
114 /***********************************************************************
115 * get_dib_image_size
117 * Return the size of a DIB bitmap in bytes.
119 static int get_dib_image_size( int width, int height, int depth )
121 return (((width * depth + 31) / 8) & ~3) * abs( height );
125 /***********************************************************************
126 * bitmap_info_size
128 * Return the size of the bitmap info structure including color table.
130 int bitmap_info_size( const BITMAPINFO * info, WORD coloruse )
132 unsigned int colors, size, masks = 0;
134 if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
136 const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *)info;
137 colors = (core->bcBitCount <= 8) ? 1 << core->bcBitCount : 0;
138 return sizeof(BITMAPCOREHEADER) + colors *
139 ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBTRIPLE) : sizeof(WORD));
141 else /* assume BITMAPINFOHEADER */
143 colors = info->bmiHeader.biClrUsed;
144 if (colors > 256) /* buffer overflow otherwise */
145 colors = 256;
146 if (!colors && (info->bmiHeader.biBitCount <= 8))
147 colors = 1 << info->bmiHeader.biBitCount;
148 if (info->bmiHeader.biCompression == BI_BITFIELDS) masks = 3;
149 size = max( info->bmiHeader.biSize, sizeof(BITMAPINFOHEADER) + masks * sizeof(DWORD) );
150 return size + colors * ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBQUAD) : sizeof(WORD));
155 /***********************************************************************
156 * is_dib_monochrome
158 * Returns whether a DIB can be converted to a monochrome DDB.
160 * A DIB can be converted if its color table contains only black and
161 * white. Black must be the first color in the color table.
163 * Note : If the first color in the color table is white followed by
164 * black, we can't convert it to a monochrome DDB with
165 * SetDIBits, because black and white would be inverted.
167 static BOOL is_dib_monochrome( const BITMAPINFO* info )
169 if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
171 const RGBTRIPLE *rgb = ((const BITMAPCOREINFO*)info)->bmciColors;
173 if (((const BITMAPCOREINFO*)info)->bmciHeader.bcBitCount != 1) return FALSE;
175 /* Check if the first color is black */
176 if ((rgb->rgbtRed == 0) && (rgb->rgbtGreen == 0) && (rgb->rgbtBlue == 0))
178 rgb++;
180 /* Check if the second color is white */
181 return ((rgb->rgbtRed == 0xff) && (rgb->rgbtGreen == 0xff)
182 && (rgb->rgbtBlue == 0xff));
184 else return FALSE;
186 else /* assume BITMAPINFOHEADER */
188 const RGBQUAD *rgb = info->bmiColors;
190 if (info->bmiHeader.biBitCount != 1) return FALSE;
192 /* Check if the first color is black */
193 if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) &&
194 (rgb->rgbBlue == 0) && (rgb->rgbReserved == 0))
196 rgb++;
198 /* Check if the second color is white */
199 return ((rgb->rgbRed == 0xff) && (rgb->rgbGreen == 0xff)
200 && (rgb->rgbBlue == 0xff) && (rgb->rgbReserved == 0));
202 else return FALSE;
206 /***********************************************************************
207 * DIB_GetBitmapInfo
209 * Get the info from a bitmap header.
210 * Return 1 for INFOHEADER, 0 for COREHEADER, -1 in case of failure.
212 static int DIB_GetBitmapInfo( const BITMAPINFOHEADER *header, LONG *width,
213 LONG *height, WORD *bpp, DWORD *compr )
215 if (header->biSize == sizeof(BITMAPCOREHEADER))
217 const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *)header;
218 *width = core->bcWidth;
219 *height = core->bcHeight;
220 *bpp = core->bcBitCount;
221 *compr = 0;
222 return 0;
224 else if (header->biSize == sizeof(BITMAPINFOHEADER) ||
225 header->biSize == sizeof(BITMAPV4HEADER) ||
226 header->biSize == sizeof(BITMAPV5HEADER))
228 *width = header->biWidth;
229 *height = header->biHeight;
230 *bpp = header->biBitCount;
231 *compr = header->biCompression;
232 return 1;
234 WARN("unknown/wrong size (%lu) for header\n", header->biSize);
235 return -1;
238 /**********************************************************************
239 * get_icon_size
241 BOOL get_icon_size( HICON handle, SIZE *size )
243 BOOL ret = NtUserGetIconSize( handle, 0, &size->cx, &size->cy );
244 if (ret) size->cy /= 2;
245 return ret;
248 struct png_wrapper
250 const char *buffer;
251 size_t size, pos;
254 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
256 struct png_wrapper *png = png_get_io_ptr(png_ptr);
258 if (png->size - png->pos >= length)
260 memcpy(data, png->buffer + png->pos, length);
261 png->pos += length;
263 else
265 png_error(png_ptr, "failed to read PNG data");
269 static unsigned be_uint(unsigned val)
271 union
273 unsigned val;
274 unsigned char c[4];
275 } u;
277 u.val = val;
278 return (u.c[0] << 24) | (u.c[1] << 16) | (u.c[2] << 8) | u.c[3];
281 static BOOL get_png_info(const void *png_data, DWORD size, int *width, int *height, int *bpp)
283 static const char png_sig[8] = { 0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a };
284 static const char png_IHDR[8] = { 0,0,0,0x0d,'I','H','D','R' };
285 const struct
287 char png_sig[8];
288 char ihdr_sig[8];
289 unsigned width, height;
290 char bit_depth, color_type, compression, filter, interlace;
291 } *png = png_data;
293 if (size < sizeof(*png)) return FALSE;
294 if (memcmp(png->png_sig, png_sig, sizeof(png_sig)) != 0) return FALSE;
295 if (memcmp(png->ihdr_sig, png_IHDR, sizeof(png_IHDR)) != 0) return FALSE;
297 *bpp = (png->color_type == PNG_COLOR_TYPE_RGB_ALPHA) ? 32 : 24;
298 *width = be_uint(png->width);
299 *height = be_uint(png->height);
301 return TRUE;
304 static BITMAPINFO *load_png(const char *png_data, DWORD *size)
306 struct png_wrapper png;
307 png_structp png_ptr;
308 png_infop info_ptr;
309 png_bytep *row_pointers = NULL;
310 int color_type, bit_depth, bpp, width, height;
311 int rowbytes, image_size, mask_size = 0, i;
312 BITMAPINFO *info = NULL;
313 unsigned char *image_data;
315 if (!get_png_info(png_data, *size, &width, &height, &bpp)) return NULL;
317 png.buffer = png_data;
318 png.size = *size;
319 png.pos = 0;
321 /* initialize libpng */
322 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
323 if (!png_ptr) return NULL;
325 info_ptr = png_create_info_struct(png_ptr);
326 if (!info_ptr)
328 png_destroy_read_struct(&png_ptr, NULL, NULL);
329 return NULL;
332 /* set up setjmp/longjmp error handling */
333 if (setjmp(png_jmpbuf(png_ptr)))
335 free(row_pointers);
336 RtlFreeHeap(GetProcessHeap(), 0, info);
337 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
338 return NULL;
341 png_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
343 /* set up custom i/o handling */
344 png_set_read_fn(png_ptr, &png, user_read_data);
346 /* read the header */
347 png_read_info(png_ptr, info_ptr);
349 color_type = png_get_color_type(png_ptr, info_ptr);
350 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
352 /* expand grayscale image data to rgb */
353 if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
354 png_set_gray_to_rgb(png_ptr);
356 /* expand palette image data to rgb */
357 if (color_type == PNG_COLOR_TYPE_PALETTE || bit_depth < 8)
358 png_set_expand(png_ptr);
360 /* update color type information */
361 png_read_update_info(png_ptr, info_ptr);
363 color_type = png_get_color_type(png_ptr, info_ptr);
364 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
366 bpp = 0;
368 switch (color_type)
370 case PNG_COLOR_TYPE_RGB:
371 if (bit_depth == 8)
372 bpp = 24;
373 break;
375 case PNG_COLOR_TYPE_RGB_ALPHA:
376 if (bit_depth == 8)
378 png_set_bgr(png_ptr);
379 bpp = 32;
381 break;
383 default:
384 break;
387 if (!bpp)
389 FIXME("unsupported PNG color format %d, %d bpp\n", color_type, bit_depth);
390 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
391 return NULL;
394 width = png_get_image_width(png_ptr, info_ptr);
395 height = png_get_image_height(png_ptr, info_ptr);
397 rowbytes = (width * bpp + 7) / 8;
398 image_size = height * rowbytes;
399 if (bpp != 32) /* add a mask if there is no alpha */
400 mask_size = (width + 7) / 8 * height;
402 info = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(BITMAPINFOHEADER) + image_size + mask_size);
403 if (!info)
405 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
406 return NULL;
409 image_data = (unsigned char *)info + sizeof(BITMAPINFOHEADER);
410 memset(image_data + image_size, 0, mask_size);
412 row_pointers = malloc(height * sizeof(png_bytep));
413 if (!row_pointers)
415 RtlFreeHeap(GetProcessHeap(), 0, info);
416 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
417 return NULL;
420 /* upside down */
421 for (i = 0; i < height; i++)
422 row_pointers[i] = image_data + (height - i - 1) * rowbytes;
424 png_read_image(png_ptr, row_pointers);
425 free(row_pointers);
426 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
428 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
429 info->bmiHeader.biWidth = width;
430 info->bmiHeader.biHeight = height * 2;
431 info->bmiHeader.biPlanes = 1;
432 info->bmiHeader.biBitCount = bpp;
433 info->bmiHeader.biCompression = BI_RGB;
434 info->bmiHeader.biSizeImage = image_size;
435 info->bmiHeader.biXPelsPerMeter = 0;
436 info->bmiHeader.biYPelsPerMeter = 0;
437 info->bmiHeader.biClrUsed = 0;
438 info->bmiHeader.biClrImportant = 0;
440 *size = sizeof(BITMAPINFOHEADER) + image_size + mask_size;
441 return info;
446 * The following macro functions account for the irregularities of
447 * accessing cursor and icon resources in files and resource entries.
449 typedef BOOL (*fnGetCIEntry)( LPCVOID dir, DWORD size, int n,
450 int *width, int *height, int *bits );
452 /**********************************************************************
453 * CURSORICON_FindBestIcon
455 * Find the icon closest to the requested size and bit depth.
457 static int CURSORICON_FindBestIcon( LPCVOID dir, DWORD size, fnGetCIEntry get_entry,
458 int width, int height, int depth, UINT loadflags )
460 int i, cx, cy, bits, bestEntry = -1;
461 UINT iTotalDiff, iXDiff=0, iYDiff=0, iColorDiff;
462 UINT iTempXDiff, iTempYDiff, iTempColorDiff;
464 /* Find Best Fit */
465 iTotalDiff = 0xFFFFFFFF;
466 iColorDiff = 0xFFFFFFFF;
468 if (loadflags & LR_DEFAULTSIZE)
470 if (!width) width = GetSystemMetrics( SM_CXICON );
471 if (!height) height = GetSystemMetrics( SM_CYICON );
473 else if (!width && !height)
475 /* use the size of the first entry */
476 if (!get_entry( dir, size, 0, &width, &height, &bits )) return -1;
477 iTotalDiff = 0;
480 for ( i = 0; iTotalDiff && get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
482 iTempXDiff = abs(width - cx);
483 iTempYDiff = abs(height - cy);
485 if(iTotalDiff > (iTempXDiff + iTempYDiff))
487 iXDiff = iTempXDiff;
488 iYDiff = iTempYDiff;
489 iTotalDiff = iXDiff + iYDiff;
493 /* Find Best Colors for Best Fit */
494 for ( i = 0; get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
496 TRACE("entry %d: %d x %d, %d bpp\n", i, cx, cy, bits);
498 if(abs(width - cx) == iXDiff && abs(height - cy) == iYDiff)
500 iTempColorDiff = abs(depth - bits);
501 if(iColorDiff > iTempColorDiff)
503 bestEntry = i;
504 iColorDiff = iTempColorDiff;
509 return bestEntry;
512 static BOOL CURSORICON_GetResIconEntry( LPCVOID dir, DWORD size, int n,
513 int *width, int *height, int *bits )
515 const CURSORICONDIR *resdir = dir;
516 const ICONRESDIR *icon;
518 if ( resdir->idCount <= n )
519 return FALSE;
520 if ((const char *)&resdir->idEntries[n + 1] - (const char *)dir > size)
521 return FALSE;
522 icon = &resdir->idEntries[n].ResInfo.icon;
523 *width = icon->bWidth;
524 *height = icon->bHeight;
525 *bits = resdir->idEntries[n].wBitCount;
526 if (!*width && !*height) *width = *height = 256;
527 return TRUE;
530 /**********************************************************************
531 * CURSORICON_FindBestCursor
533 * Find the cursor closest to the requested size.
535 * FIXME: parameter 'color' ignored.
537 static int CURSORICON_FindBestCursor( LPCVOID dir, DWORD size, fnGetCIEntry get_entry,
538 int width, int height, int depth, UINT loadflags )
540 int i, maxwidth, maxheight, maxbits, cx, cy, bits, bestEntry = -1;
542 if (loadflags & LR_DEFAULTSIZE)
544 if (!width) width = GetSystemMetrics( SM_CXCURSOR );
545 if (!height) height = GetSystemMetrics( SM_CYCURSOR );
547 else if (!width && !height)
549 /* use the first entry */
550 if (!get_entry( dir, size, 0, &width, &height, &bits )) return -1;
551 return 0;
554 /* First find the largest one smaller than or equal to the requested size*/
556 maxwidth = maxheight = maxbits = 0;
557 for ( i = 0; get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
559 if (cx > width || cy > height) continue;
560 if (cx < maxwidth || cy < maxheight) continue;
561 if (cx == maxwidth && cy == maxheight)
563 if (loadflags & LR_MONOCHROME)
565 if (maxbits && bits >= maxbits) continue;
567 else if (bits <= maxbits) continue;
569 bestEntry = i;
570 maxwidth = cx;
571 maxheight = cy;
572 maxbits = bits;
574 if (bestEntry != -1) return bestEntry;
576 /* Now find the smallest one larger than the requested size */
578 maxwidth = maxheight = 255;
579 for ( i = 0; get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
581 if (cx > maxwidth || cy > maxheight) continue;
582 if (cx == maxwidth && cy == maxheight)
584 if (loadflags & LR_MONOCHROME)
586 if (maxbits && bits >= maxbits) continue;
588 else if (bits <= maxbits) continue;
590 bestEntry = i;
591 maxwidth = cx;
592 maxheight = cy;
593 maxbits = bits;
595 if (bestEntry == -1) bestEntry = 0;
597 return bestEntry;
600 static BOOL CURSORICON_GetResCursorEntry( LPCVOID dir, DWORD size, int n,
601 int *width, int *height, int *bits )
603 const CURSORICONDIR *resdir = dir;
604 const CURSORDIR *cursor;
606 if ( resdir->idCount <= n )
607 return FALSE;
608 if ((const char *)&resdir->idEntries[n + 1] - (const char *)dir > size)
609 return FALSE;
610 cursor = &resdir->idEntries[n].ResInfo.cursor;
611 *width = cursor->wWidth;
612 *height = cursor->wHeight;
613 *bits = resdir->idEntries[n].wBitCount;
614 if (*height == *width * 2) *height /= 2;
615 return TRUE;
618 static const CURSORICONDIRENTRY *CURSORICON_FindBestIconRes( const CURSORICONDIR * dir, DWORD size,
619 int width, int height, int depth,
620 UINT loadflags )
622 int n;
624 n = CURSORICON_FindBestIcon( dir, size, CURSORICON_GetResIconEntry,
625 width, height, depth, loadflags );
626 if ( n < 0 )
627 return NULL;
628 return &dir->idEntries[n];
631 static const CURSORICONDIRENTRY *CURSORICON_FindBestCursorRes( const CURSORICONDIR *dir, DWORD size,
632 int width, int height, int depth,
633 UINT loadflags )
635 int n = CURSORICON_FindBestCursor( dir, size, CURSORICON_GetResCursorEntry,
636 width, height, depth, loadflags );
637 if ( n < 0 )
638 return NULL;
639 return &dir->idEntries[n];
642 static BOOL CURSORICON_GetFileEntry( LPCVOID dir, DWORD size, int n,
643 int *width, int *height, int *bits )
645 const CURSORICONFILEDIR *filedir = dir;
646 const CURSORICONFILEDIRENTRY *entry;
647 const BITMAPINFOHEADER *info;
649 if ( filedir->idCount <= n )
650 return FALSE;
651 if ((const char *)&filedir->idEntries[n + 1] - (const char *)dir > size)
652 return FALSE;
653 entry = &filedir->idEntries[n];
654 if (entry->dwDIBOffset > size - sizeof(info->biSize)) return FALSE;
655 info = (const BITMAPINFOHEADER *)((const char *)dir + entry->dwDIBOffset);
657 if (info->biSize == PNG_SIGN) return get_png_info(info, size, width, height, bits);
659 if (info->biSize != sizeof(BITMAPCOREHEADER))
661 if ((const char *)(info + 1) - (const char *)dir > size) return FALSE;
662 *bits = info->biBitCount;
664 else
666 const BITMAPCOREHEADER *coreinfo = (const BITMAPCOREHEADER *)((const char *)dir + entry->dwDIBOffset);
667 if ((const char *)(coreinfo + 1) - (const char *)dir > size) return FALSE;
668 *bits = coreinfo->bcBitCount;
670 *width = entry->bWidth;
671 *height = entry->bHeight;
672 return TRUE;
675 static const CURSORICONFILEDIRENTRY *CURSORICON_FindBestCursorFile( const CURSORICONFILEDIR *dir, DWORD size,
676 int width, int height, int depth,
677 UINT loadflags )
679 int n = CURSORICON_FindBestCursor( dir, size, CURSORICON_GetFileEntry,
680 width, height, depth, loadflags );
681 if ( n < 0 )
682 return NULL;
683 return &dir->idEntries[n];
686 static const CURSORICONFILEDIRENTRY *CURSORICON_FindBestIconFile( const CURSORICONFILEDIR *dir, DWORD size,
687 int width, int height, int depth,
688 UINT loadflags )
690 int n = CURSORICON_FindBestIcon( dir, size, CURSORICON_GetFileEntry,
691 width, height, depth, loadflags );
692 if ( n < 0 )
693 return NULL;
694 return &dir->idEntries[n];
697 /***********************************************************************
698 * bmi_has_alpha
700 static BOOL bmi_has_alpha( const BITMAPINFO *info, const void *bits )
702 int i;
703 BOOL has_alpha = FALSE;
704 const unsigned char *ptr = bits;
706 if (info->bmiHeader.biBitCount != 32) return FALSE;
707 for (i = 0; i < info->bmiHeader.biWidth * abs(info->bmiHeader.biHeight); i++, ptr += 4)
708 if ((has_alpha = (ptr[3] != 0))) break;
709 return has_alpha;
712 /***********************************************************************
713 * create_alpha_bitmap
715 * Create the alpha bitmap for a 32-bpp icon that has an alpha channel.
717 static HBITMAP create_alpha_bitmap( HBITMAP color, const BITMAPINFO *src_info, const void *color_bits )
719 HBITMAP alpha = 0;
720 BITMAPINFO *info = NULL;
721 BITMAP bm;
722 HDC hdc;
723 void *bits;
724 unsigned char *ptr;
725 int i;
727 if (!GetObjectW( color, sizeof(bm), &bm )) return 0;
728 if (bm.bmBitsPixel != 32) return 0;
730 if (!(hdc = CreateCompatibleDC( 0 ))) return 0;
731 if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) goto done;
732 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
733 info->bmiHeader.biWidth = bm.bmWidth;
734 info->bmiHeader.biHeight = -bm.bmHeight;
735 info->bmiHeader.biPlanes = 1;
736 info->bmiHeader.biBitCount = 32;
737 info->bmiHeader.biCompression = BI_RGB;
738 info->bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4;
739 info->bmiHeader.biXPelsPerMeter = 0;
740 info->bmiHeader.biYPelsPerMeter = 0;
741 info->bmiHeader.biClrUsed = 0;
742 info->bmiHeader.biClrImportant = 0;
743 if (!(alpha = CreateDIBSection( hdc, info, DIB_RGB_COLORS, &bits, NULL, 0 ))) goto done;
745 if (src_info)
747 SelectObject( hdc, alpha );
748 StretchDIBits( hdc, 0, 0, bm.bmWidth, bm.bmHeight,
749 0, 0, src_info->bmiHeader.biWidth, src_info->bmiHeader.biHeight,
750 color_bits, src_info, DIB_RGB_COLORS, SRCCOPY );
753 else
755 GetDIBits( hdc, color, 0, bm.bmHeight, bits, info, DIB_RGB_COLORS );
756 if (!bmi_has_alpha( info, bits ))
758 DeleteObject( alpha );
759 alpha = 0;
760 goto done;
764 /* pre-multiply by alpha */
765 for (i = 0, ptr = bits; i < bm.bmWidth * bm.bmHeight; i++, ptr += 4)
767 unsigned int alpha = ptr[3];
768 ptr[0] = (ptr[0] * alpha + 127) / 255;
769 ptr[1] = (ptr[1] * alpha + 127) / 255;
770 ptr[2] = (ptr[2] * alpha + 127) / 255;
773 done:
774 DeleteDC( hdc );
775 HeapFree( GetProcessHeap(), 0, info );
776 return alpha;
779 static BOOL create_icon_frame( const BITMAPINFO *bmi, DWORD maxsize, POINT hotspot, BOOL is_icon,
780 INT width, INT height, UINT flags, struct cursoricon_frame *frame )
782 DWORD size, color_size, mask_size, compr;
783 const void *color_bits, *mask_bits;
784 void *alpha_mask_bits = NULL;
785 LONG bmi_width, bmi_height;
786 BITMAPINFO *bmi_copy;
787 BOOL do_stretch;
788 HDC hdc = 0;
789 WORD bpp;
790 BOOL ret = FALSE;
792 memset( frame, 0, sizeof(*frame) );
794 /* Check bitmap header */
796 if (bmi->bmiHeader.biSize == PNG_SIGN)
798 BITMAPINFO *bmi_png;
800 if (!(bmi_png = load_png( (const char *)bmi, &maxsize ))) return FALSE;
801 ret = create_icon_frame( bmi_png, maxsize, hotspot, is_icon, width, height, flags, frame );
802 HeapFree( GetProcessHeap(), 0, bmi_png );
803 return ret;
806 if (maxsize < sizeof(BITMAPCOREHEADER))
808 WARN( "invalid size %lu\n", maxsize );
809 return FALSE;
811 if (maxsize < bmi->bmiHeader.biSize)
813 WARN( "invalid header size %lu\n", bmi->bmiHeader.biSize );
814 return FALSE;
816 if ( (bmi->bmiHeader.biSize != sizeof(BITMAPCOREHEADER)) &&
817 (bmi->bmiHeader.biSize != sizeof(BITMAPINFOHEADER) ||
818 (bmi->bmiHeader.biCompression != BI_RGB &&
819 bmi->bmiHeader.biCompression != BI_BITFIELDS)) )
821 WARN( "invalid bitmap header %lu\n", bmi->bmiHeader.biSize );
822 return FALSE;
825 size = bitmap_info_size( bmi, DIB_RGB_COLORS );
826 DIB_GetBitmapInfo(&bmi->bmiHeader, &bmi_width, &bmi_height, &bpp, &compr);
827 color_size = get_dib_image_size( bmi_width, bmi_height / 2,
828 bpp );
829 mask_size = get_dib_image_size( bmi_width, bmi_height / 2, 1 );
830 if (size > maxsize || color_size > maxsize - size)
832 WARN( "truncated file %lu < %lu+%lu+%lu\n", maxsize, size, color_size, mask_size );
833 return 0;
835 if (mask_size > maxsize - size - color_size) mask_size = 0; /* no mask */
837 if (flags & LR_DEFAULTSIZE)
839 if (!width) width = GetSystemMetrics( is_icon ? SM_CXICON : SM_CXCURSOR );
840 if (!height) height = GetSystemMetrics( is_icon ? SM_CYICON : SM_CYCURSOR );
842 else
844 if (!width) width = bmi_width;
845 if (!height) height = bmi_height/2;
847 do_stretch = (bmi_height/2 != height) || (bmi_width != width);
849 /* Scale the hotspot */
850 if (is_icon)
852 hotspot.x = width / 2;
853 hotspot.y = height / 2;
855 else if (do_stretch)
857 hotspot.x = (hotspot.x * width) / bmi_width;
858 hotspot.y = (hotspot.y * height) / (bmi_height / 2);
861 if (!(bmi_copy = HeapAlloc( GetProcessHeap(), 0, max( size, FIELD_OFFSET( BITMAPINFO, bmiColors[2] )))))
862 return 0;
863 if (!(hdc = CreateCompatibleDC( 0 ))) goto done;
865 memcpy( bmi_copy, bmi, size );
866 if (bmi_copy->bmiHeader.biSize != sizeof(BITMAPCOREHEADER))
867 bmi_copy->bmiHeader.biHeight /= 2;
868 else
869 ((BITMAPCOREINFO *)bmi_copy)->bmciHeader.bcHeight /= 2;
870 bmi_height /= 2;
872 color_bits = (const char*)bmi + size;
873 mask_bits = (const char*)color_bits + color_size;
875 if (is_dib_monochrome( bmi ))
877 if (!(frame->mask = CreateBitmap( width, height * 2, 1, 1, NULL ))) goto done;
879 /* copy color data into second half of mask bitmap */
880 SelectObject( hdc, frame->mask );
881 StretchDIBits( hdc, 0, height, width, height,
882 0, 0, bmi_width, bmi_height,
883 color_bits, bmi_copy, DIB_RGB_COLORS, SRCCOPY );
885 else
887 if (!(frame->mask = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
888 if (!(frame->color = create_color_bitmap( width, height ))) goto done;
889 SelectObject( hdc, frame->color );
890 StretchDIBits( hdc, 0, 0, width, height,
891 0, 0, bmi_width, bmi_height,
892 color_bits, bmi_copy, DIB_RGB_COLORS, SRCCOPY );
894 if (bmi_has_alpha( bmi_copy, color_bits ))
896 frame->alpha = create_alpha_bitmap( frame->color, bmi_copy, color_bits );
897 if (!mask_size) /* generate mask from alpha */
899 LONG x, y, dst_stride = ((bmi_width + 31) / 8) & ~3;
901 if ((alpha_mask_bits = heap_calloc( bmi_height, dst_stride )))
903 static const unsigned char masks[] = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 };
904 const DWORD *src = color_bits;
905 unsigned char *dst = alpha_mask_bits;
907 for (y = 0; y < bmi_height; y++, src += bmi_width, dst += dst_stride)
908 for (x = 0; x < bmi_width; x++)
909 if (src[x] >> 24 != 0xff) dst[x >> 3] |= masks[x & 7];
911 mask_bits = alpha_mask_bits;
912 mask_size = bmi_height * dst_stride;
917 /* convert info to monochrome to copy the mask */
918 if (bmi_copy->bmiHeader.biSize != sizeof(BITMAPCOREHEADER))
920 RGBQUAD *rgb = bmi_copy->bmiColors;
922 bmi_copy->bmiHeader.biBitCount = 1;
923 bmi_copy->bmiHeader.biClrUsed = bmi_copy->bmiHeader.biClrImportant = 2;
924 rgb[0].rgbBlue = rgb[0].rgbGreen = rgb[0].rgbRed = 0x00;
925 rgb[1].rgbBlue = rgb[1].rgbGreen = rgb[1].rgbRed = 0xff;
926 rgb[0].rgbReserved = rgb[1].rgbReserved = 0;
928 else
930 RGBTRIPLE *rgb = (RGBTRIPLE *)(((BITMAPCOREHEADER *)bmi_copy) + 1);
932 ((BITMAPCOREINFO *)bmi_copy)->bmciHeader.bcBitCount = 1;
933 rgb[0].rgbtBlue = rgb[0].rgbtGreen = rgb[0].rgbtRed = 0x00;
934 rgb[1].rgbtBlue = rgb[1].rgbtGreen = rgb[1].rgbtRed = 0xff;
938 if (mask_size)
940 SelectObject( hdc, frame->mask );
941 StretchDIBits( hdc, 0, 0, width, height,
942 0, 0, bmi_width, bmi_height,
943 mask_bits, bmi_copy, DIB_RGB_COLORS, SRCCOPY );
946 frame->width = width;
947 frame->height = height;
948 frame->hotspot = hotspot;
949 ret = TRUE;
951 done:
952 if (!ret) free_icon_frame( frame );
953 DeleteDC( hdc );
954 HeapFree( GetProcessHeap(), 0, bmi_copy );
955 HeapFree( GetProcessHeap(), 0, alpha_mask_bits );
956 return ret;
959 static HICON create_cursoricon_object( struct cursoricon_desc *desc, BOOL is_icon, HINSTANCE module,
960 const WCHAR *resname, HRSRC rsrc )
962 WCHAR buf[MAX_PATH];
963 UNICODE_STRING module_name = { 0, sizeof(buf), buf };
964 UNICODE_STRING res_str = { 0 };
965 HICON handle;
967 if (!(handle = NtUserCreateCursorIcon( is_icon ))) return 0;
969 if (module) LdrGetDllFullName( module, &module_name );
971 res_str.Buffer = (WCHAR *)resname;
972 if (!IS_INTRESOURCE(resname))
974 res_str.Length = lstrlenW( resname ) * sizeof(WCHAR);
975 res_str.MaximumLength = res_str.Length + sizeof(WCHAR);
977 desc->rsrc = rsrc; /* FIXME: we should probably avoid storing rsrc */
979 if (!NtUserSetCursorIconData( handle, &module_name, &res_str, desc ))
981 NtUserDestroyCursor( handle, 0 );
982 return 0;
985 return handle;
988 /***********************************************************************
989 * create_icon_from_bmi
991 * Create an icon from its BITMAPINFO.
993 static HICON create_icon_from_bmi( const BITMAPINFO *bmi, DWORD maxsize, HMODULE module, LPCWSTR resname,
994 HRSRC rsrc, POINT hotspot, BOOL bIcon, INT width, INT height,
995 UINT flags )
997 struct cursoricon_frame frame;
998 struct cursoricon_desc desc =
1000 .flags = flags,
1001 .frames = &frame,
1003 HICON ret;
1005 if (!create_icon_frame( bmi, maxsize, hotspot, bIcon, width, height, flags, &frame )) return 0;
1007 ret = create_cursoricon_object( &desc, bIcon, module, resname, rsrc );
1008 if (!ret) free_icon_frame( &frame );
1009 return ret;
1013 /**********************************************************************
1014 * .ANI cursor support
1016 #define ANI_RIFF_ID RIFF_FOURCC('R', 'I', 'F', 'F')
1017 #define ANI_LIST_ID RIFF_FOURCC('L', 'I', 'S', 'T')
1018 #define ANI_ACON_ID RIFF_FOURCC('A', 'C', 'O', 'N')
1019 #define ANI_anih_ID RIFF_FOURCC('a', 'n', 'i', 'h')
1020 #define ANI_seq__ID RIFF_FOURCC('s', 'e', 'q', ' ')
1021 #define ANI_fram_ID RIFF_FOURCC('f', 'r', 'a', 'm')
1022 #define ANI_rate_ID RIFF_FOURCC('r', 'a', 't', 'e')
1024 #define ANI_FLAG_ICON 0x1
1025 #define ANI_FLAG_SEQUENCE 0x2
1027 typedef struct {
1028 DWORD header_size;
1029 DWORD num_frames;
1030 DWORD num_steps;
1031 DWORD width;
1032 DWORD height;
1033 DWORD bpp;
1034 DWORD num_planes;
1035 DWORD display_rate;
1036 DWORD flags;
1037 } ani_header;
1039 typedef struct {
1040 DWORD data_size;
1041 const unsigned char *data;
1042 } riff_chunk_t;
1044 static void dump_ani_header( const ani_header *header )
1046 TRACE(" header size: %ld\n", header->header_size);
1047 TRACE(" frames: %ld\n", header->num_frames);
1048 TRACE(" steps: %ld\n", header->num_steps);
1049 TRACE(" width: %ld\n", header->width);
1050 TRACE(" height: %ld\n", header->height);
1051 TRACE(" bpp: %ld\n", header->bpp);
1052 TRACE(" planes: %ld\n", header->num_planes);
1053 TRACE(" display rate: %ld\n", header->display_rate);
1054 TRACE(" flags: 0x%08lx\n", header->flags);
1059 * RIFF:
1060 * DWORD "RIFF"
1061 * DWORD size
1062 * DWORD riff_id
1063 * BYTE[] data
1065 * LIST:
1066 * DWORD "LIST"
1067 * DWORD size
1068 * DWORD list_id
1069 * BYTE[] data
1071 * CHUNK:
1072 * DWORD chunk_id
1073 * DWORD size
1074 * BYTE[] data
1076 static void riff_find_chunk( DWORD chunk_id, DWORD chunk_type, const riff_chunk_t *parent_chunk, riff_chunk_t *chunk )
1078 const unsigned char *ptr = parent_chunk->data;
1079 const unsigned char *end = parent_chunk->data + (parent_chunk->data_size - (2 * sizeof(DWORD)));
1081 if (chunk_type == ANI_LIST_ID || chunk_type == ANI_RIFF_ID) end -= sizeof(DWORD);
1083 while (ptr < end)
1085 if ((!chunk_type && *(const DWORD *)ptr == chunk_id )
1086 || (chunk_type && *(const DWORD *)ptr == chunk_type && *((const DWORD *)ptr + 2) == chunk_id ))
1088 ptr += sizeof(DWORD);
1089 chunk->data_size = (*(const DWORD *)ptr + 1) & ~1;
1090 ptr += sizeof(DWORD);
1091 if (chunk_type == ANI_LIST_ID || chunk_type == ANI_RIFF_ID) ptr += sizeof(DWORD);
1092 chunk->data = ptr;
1094 return;
1097 ptr += sizeof(DWORD);
1098 if (ptr >= end)
1099 break;
1100 ptr += (*(const DWORD *)ptr + 1) & ~1;
1101 ptr += sizeof(DWORD);
1107 * .ANI layout:
1109 * RIFF:'ACON' RIFF chunk
1110 * |- CHUNK:'anih' Header
1111 * |- CHUNK:'seq ' Sequence information (optional)
1112 * \- LIST:'fram' Frame list
1113 * |- CHUNK:icon Cursor frames
1114 * |- CHUNK:icon
1115 * |- ...
1116 * \- CHUNK:icon
1118 static HCURSOR CURSORICON_CreateIconFromANI( const BYTE *bits, DWORD bits_size, INT width, INT height,
1119 INT depth, BOOL is_icon, UINT loadflags )
1121 struct cursoricon_desc desc = { 0 };
1122 ani_header header;
1123 HCURSOR cursor;
1124 UINT i;
1125 BOOL error = FALSE;
1127 riff_chunk_t root_chunk = { bits_size, bits };
1128 riff_chunk_t ACON_chunk = {0};
1129 riff_chunk_t anih_chunk = {0};
1130 riff_chunk_t fram_chunk = {0};
1131 riff_chunk_t rate_chunk = {0};
1132 riff_chunk_t seq_chunk = {0};
1133 const unsigned char *icon_chunk;
1134 const unsigned char *icon_data;
1136 TRACE("bits %p, bits_size %ld\n", bits, bits_size);
1138 riff_find_chunk( ANI_ACON_ID, ANI_RIFF_ID, &root_chunk, &ACON_chunk );
1139 if (!ACON_chunk.data)
1141 ERR("Failed to get root chunk.\n");
1142 return 0;
1145 riff_find_chunk( ANI_anih_ID, 0, &ACON_chunk, &anih_chunk );
1146 if (!anih_chunk.data)
1148 ERR("Failed to get 'anih' chunk.\n");
1149 return 0;
1151 memcpy( &header, anih_chunk.data, sizeof(header) );
1152 dump_ani_header( &header );
1154 if (!(header.flags & ANI_FLAG_ICON))
1156 FIXME("Raw animated icon/cursor data is not currently supported.\n");
1157 return 0;
1160 if (header.flags & ANI_FLAG_SEQUENCE)
1162 riff_find_chunk( ANI_seq__ID, 0, &ACON_chunk, &seq_chunk );
1163 if (seq_chunk.data)
1165 desc.frame_seq = (DWORD *)seq_chunk.data;
1167 else
1169 FIXME("Sequence data expected but not found, assuming steps == frames.\n");
1170 header.num_steps = header.num_frames;
1174 riff_find_chunk( ANI_rate_ID, 0, &ACON_chunk, &rate_chunk );
1175 if (rate_chunk.data)
1176 desc.frame_rates = (DWORD *)rate_chunk.data;
1178 riff_find_chunk( ANI_fram_ID, ANI_LIST_ID, &ACON_chunk, &fram_chunk );
1179 if (!fram_chunk.data)
1181 ERR("Failed to get icon list.\n");
1182 return 0;
1185 /* The .ANI stores the display rate in jiffies (1/60s) */
1186 desc.delay = header.display_rate;
1187 desc.num_frames = header.num_frames;
1188 desc.num_steps = header.num_steps;
1189 if (!(desc.frames = HeapAlloc( GetProcessHeap(), 0, sizeof(*desc.frames) * desc.num_frames )))
1190 return 0;
1192 icon_chunk = fram_chunk.data;
1193 icon_data = fram_chunk.data + (2 * sizeof(DWORD));
1194 for (i = 0; i < desc.num_frames; i++)
1196 const DWORD chunk_size = *(const DWORD *)(icon_chunk + sizeof(DWORD));
1197 const CURSORICONFILEDIRENTRY *entry;
1198 INT frameWidth, frameHeight;
1199 const BITMAPINFO *bmi;
1200 POINT hotspot;
1202 entry = CURSORICON_FindBestIconFile((const CURSORICONFILEDIR *) icon_data,
1203 bits + bits_size - icon_data,
1204 width, height, depth, loadflags );
1206 hotspot.x = entry->xHotspot;
1207 hotspot.y = entry->yHotspot;
1208 if (!header.width || !header.height)
1210 frameWidth = entry->bWidth;
1211 frameHeight = entry->bHeight;
1213 else
1215 frameWidth = header.width;
1216 frameHeight = header.height;
1219 if (!(error = entry->dwDIBOffset >= bits + bits_size - icon_data))
1221 bmi = (const BITMAPINFO *) (icon_data + entry->dwDIBOffset);
1222 /* Grab a frame from the animation */
1223 error = !create_icon_frame( bmi, bits + bits_size - (const BYTE *)bmi, hotspot,
1224 is_icon, frameWidth, frameHeight, loadflags, &desc.frames[i] );
1227 if (error)
1229 if (i == 0)
1231 FIXME_(cursor)("Completely failed to create animated cursor!\n");
1232 HeapFree( GetProcessHeap(), 0, desc.frames );
1233 return 0;
1235 /* There was an error but we at least decoded the first frame, so just use that frame */
1236 FIXME_(cursor)("Error creating animated cursor, only using first frame!\n");
1237 while (i > 1) free_icon_frame( &desc.frames[--i] );
1238 desc.num_frames = desc.num_steps = 1;
1239 desc.frame_seq = NULL;
1240 desc.delay = 0;
1241 break;
1244 /* Advance to the next chunk */
1245 icon_chunk += chunk_size + (2 * sizeof(DWORD));
1246 icon_data = icon_chunk + (2 * sizeof(DWORD));
1249 cursor = create_cursoricon_object( &desc, is_icon, 0, 0, 0 );
1250 if (!cursor) for (i = 0; i < desc.num_frames; i++) free_icon_frame( &desc.frames[i] );
1251 HeapFree( GetProcessHeap(), 0, desc.frames );
1252 return cursor;
1256 /**********************************************************************
1257 * CreateIconFromResourceEx (USER32.@)
1259 * FIXME: Convert to mono when cFlag is LR_MONOCHROME.
1261 HICON WINAPI CreateIconFromResourceEx( LPBYTE bits, UINT cbSize,
1262 BOOL bIcon, DWORD dwVersion,
1263 INT width, INT height,
1264 UINT cFlag )
1266 POINT hotspot;
1267 const BITMAPINFO *bmi;
1269 TRACE_(cursor)("%p (%u bytes), ver %08lx, %ix%i %s %s\n",
1270 bits, cbSize, dwVersion, width, height,
1271 bIcon ? "icon" : "cursor", (cFlag & LR_MONOCHROME) ? "mono" : "" );
1273 if (!bits) return 0;
1275 if (dwVersion == 0x00020000)
1277 FIXME_(cursor)("\t2.xx resources are not supported\n");
1278 return 0;
1281 /* Check if the resource is an animated icon/cursor */
1282 if (!memcmp(bits, "RIFF", 4))
1283 return CURSORICON_CreateIconFromANI( bits, cbSize, width, height,
1284 0 /* default depth */, bIcon, cFlag );
1286 if (bIcon)
1288 hotspot.x = width / 2;
1289 hotspot.y = height / 2;
1290 bmi = (BITMAPINFO *)bits;
1292 else /* get the hotspot */
1294 const SHORT *pt = (const SHORT *)bits;
1295 hotspot.x = pt[0];
1296 hotspot.y = pt[1];
1297 bmi = (const BITMAPINFO *)(pt + 2);
1298 cbSize -= 2 * sizeof(*pt);
1301 return create_icon_from_bmi( bmi, cbSize, NULL, NULL, NULL, hotspot, bIcon, width, height, cFlag );
1305 /**********************************************************************
1306 * CreateIconFromResource (USER32.@)
1308 HICON WINAPI CreateIconFromResource( LPBYTE bits, UINT cbSize,
1309 BOOL bIcon, DWORD dwVersion)
1311 return CreateIconFromResourceEx( bits, cbSize, bIcon, dwVersion, 0, 0, LR_DEFAULTSIZE | LR_SHARED );
1315 static HICON CURSORICON_LoadFromFile( LPCWSTR filename,
1316 INT width, INT height, INT depth,
1317 BOOL fCursor, UINT loadflags)
1319 const CURSORICONFILEDIRENTRY *entry;
1320 const CURSORICONFILEDIR *dir;
1321 DWORD filesize = 0;
1322 HICON hIcon = 0;
1323 const BYTE *bits;
1324 POINT hotspot;
1326 TRACE("loading %s\n", debugstr_w( filename ));
1328 bits = map_fileW( filename, &filesize );
1329 if (!bits)
1330 return hIcon;
1332 /* Check for .ani. */
1333 if (memcmp( bits, "RIFF", 4 ) == 0)
1335 hIcon = CURSORICON_CreateIconFromANI( bits, filesize, width, height, depth, !fCursor, loadflags );
1336 goto end;
1339 dir = (const CURSORICONFILEDIR*) bits;
1340 if ( filesize < FIELD_OFFSET( CURSORICONFILEDIR, idEntries[dir->idCount] ))
1341 goto end;
1343 if ( fCursor )
1344 entry = CURSORICON_FindBestCursorFile( dir, filesize, width, height, depth, loadflags );
1345 else
1346 entry = CURSORICON_FindBestIconFile( dir, filesize, width, height, depth, loadflags );
1348 if ( !entry )
1349 goto end;
1351 /* check that we don't run off the end of the file */
1352 if ( entry->dwDIBOffset > filesize )
1353 goto end;
1354 if ( entry->dwDIBOffset + entry->dwDIBSize > filesize )
1355 goto end;
1357 hotspot.x = entry->xHotspot;
1358 hotspot.y = entry->yHotspot;
1359 hIcon = create_icon_from_bmi( (const BITMAPINFO *)&bits[entry->dwDIBOffset], filesize - entry->dwDIBOffset,
1360 NULL, NULL, NULL, hotspot, !fCursor, width, height, loadflags );
1361 end:
1362 TRACE("loaded %s -> %p\n", debugstr_w( filename ), hIcon );
1363 UnmapViewOfFile( bits );
1364 return hIcon;
1367 /**********************************************************************
1368 * CURSORICON_Load
1370 * Load a cursor or icon from resource or file.
1372 static HICON CURSORICON_Load(HINSTANCE hInstance, LPCWSTR name,
1373 INT width, INT height, INT depth,
1374 BOOL fCursor, UINT loadflags)
1376 HANDLE handle = 0;
1377 HICON hIcon = 0;
1378 HRSRC hRsrc;
1379 DWORD size;
1380 const CURSORICONDIR *dir;
1381 const CURSORICONDIRENTRY *dirEntry;
1382 const BYTE *bits;
1383 WORD wResId;
1384 POINT hotspot;
1386 TRACE("%p, %s, %dx%d, depth %d, fCursor %d, flags 0x%04x\n",
1387 hInstance, debugstr_w(name), width, height, depth, fCursor, loadflags);
1389 if ( loadflags & LR_LOADFROMFILE ) /* Load from file */
1390 return CURSORICON_LoadFromFile( name, width, height, depth, fCursor, loadflags );
1392 if (!hInstance) hInstance = user32_module; /* Load OEM cursor/icon */
1394 /* don't cache 16-bit instances (FIXME: should never get 16-bit instances in the first place) */
1395 if ((ULONG_PTR)hInstance >> 16 == 0) loadflags &= ~LR_SHARED;
1397 /* Get directory resource ID */
1399 if (!(hRsrc = FindResourceW( hInstance, name,
1400 (LPWSTR)(fCursor ? RT_GROUP_CURSOR : RT_GROUP_ICON) )))
1402 /* try animated resource */
1403 if (!(hRsrc = FindResourceW( hInstance, name,
1404 (LPWSTR)(fCursor ? RT_ANICURSOR : RT_ANIICON) ))) return 0;
1405 if (!(handle = LoadResource( hInstance, hRsrc ))) return 0;
1406 bits = LockResource( handle );
1407 return CURSORICON_CreateIconFromANI( bits, SizeofResource( hInstance, handle ),
1408 width, height, depth, !fCursor, loadflags );
1411 /* Find the best entry in the directory */
1413 if (!(handle = LoadResource( hInstance, hRsrc ))) return 0;
1414 if (!(dir = LockResource( handle ))) return 0;
1415 size = SizeofResource( hInstance, hRsrc );
1416 if (fCursor)
1417 dirEntry = CURSORICON_FindBestCursorRes( dir, size, width, height, depth, loadflags );
1418 else
1419 dirEntry = CURSORICON_FindBestIconRes( dir, size, width, height, depth, loadflags );
1420 if (!dirEntry) return 0;
1421 wResId = dirEntry->wResId;
1422 FreeResource( handle );
1424 /* Load the resource */
1426 if (!(hRsrc = FindResourceW(hInstance,MAKEINTRESOURCEW(wResId),
1427 (LPWSTR)(fCursor ? RT_CURSOR : RT_ICON) ))) return 0;
1429 /* If shared icon, check whether it was already loaded */
1430 if (loadflags & LR_SHARED)
1432 WCHAR module_buf[MAX_PATH];
1433 UNICODE_STRING module_str, res_str;
1435 res_str.Length = 0;
1436 res_str.Buffer = MAKEINTRESOURCEW(wResId);
1437 module_str.Buffer = module_buf;
1438 module_str.MaximumLength = sizeof(module_buf);
1439 if (!LdrGetDllFullName( hInstance, &module_str ) &&
1440 (hIcon = NtUserFindExistingCursorIcon( &module_str, &res_str, hRsrc )))
1441 return hIcon;
1444 if (!(handle = LoadResource( hInstance, hRsrc ))) return 0;
1445 size = SizeofResource( hInstance, hRsrc );
1446 bits = LockResource( handle );
1448 if (!fCursor)
1450 hotspot.x = width / 2;
1451 hotspot.y = height / 2;
1453 else /* get the hotspot */
1455 const SHORT *pt = (const SHORT *)bits;
1456 hotspot.x = pt[0];
1457 hotspot.y = pt[1];
1458 bits += 2 * sizeof(SHORT);
1459 size -= 2 * sizeof(SHORT);
1461 hIcon = create_icon_from_bmi( (const BITMAPINFO *)bits, size, hInstance, name, hRsrc,
1462 hotspot, !fCursor, width, height, loadflags );
1463 FreeResource( handle );
1464 return hIcon;
1468 static HBITMAP create_masked_bitmap( int width, int height, const void *and, const void *xor )
1470 HBITMAP and_bitmap, xor_bitmap, bitmap;
1471 HDC src_dc, dst_dc;
1473 and_bitmap = CreateBitmap( width, height, 1, 1, and );
1474 xor_bitmap = CreateBitmap( width, height, 1, 1, xor );
1475 bitmap = CreateBitmap( width, height * 2, 1, 1, NULL );
1476 src_dc = CreateCompatibleDC( 0 );
1477 dst_dc = CreateCompatibleDC( 0 );
1479 SelectObject( dst_dc, bitmap );
1480 SelectObject( src_dc, and_bitmap );
1481 BitBlt( dst_dc, 0, 0, width, height, src_dc, 0, 0, SRCCOPY );
1482 SelectObject( src_dc, xor_bitmap );
1483 BitBlt( dst_dc, 0, height, width, height, src_dc, 0, 0, SRCCOPY );
1485 DeleteObject( and_bitmap );
1486 DeleteObject( xor_bitmap );
1487 DeleteDC( src_dc );
1488 DeleteDC( dst_dc );
1489 return bitmap;
1493 /***********************************************************************
1494 * CreateCursor (USER32.@)
1496 HCURSOR WINAPI CreateCursor( HINSTANCE instance, int hotspot_x, int hotspot_y,
1497 int width, int height, const void *and, const void *xor )
1499 ICONINFO info;
1500 HCURSOR cursor;
1502 TRACE( "hotspot (%d,%d), size %dx%d\n", hotspot_x, hotspot_y, width, height );
1504 info.fIcon = FALSE;
1505 info.xHotspot = hotspot_x;
1506 info.yHotspot = hotspot_y;
1507 info.hbmColor = NULL;
1508 info.hbmMask = create_masked_bitmap( width, height, and, xor );
1509 cursor = CreateIconIndirect( &info );
1510 DeleteObject( info.hbmMask );
1511 return cursor;
1515 /***********************************************************************
1516 * CreateIcon (USER32.@)
1518 * Creates an icon based on the specified bitmaps. The bitmaps must be
1519 * provided in a device dependent format and will be resized to
1520 * (SM_CXICON,SM_CYICON) and depth converted to match the screen's color
1521 * depth. The provided bitmaps must be top-down bitmaps.
1522 * Although Windows does not support 15bpp(*) this API must support it
1523 * for Winelib applications.
1525 * (*) Windows does not support 15bpp but it supports the 555 RGB 16bpp
1526 * format!
1528 * RETURNS
1529 * Success: handle to an icon
1530 * Failure: NULL
1532 * FIXME: Do we need to resize the bitmaps?
1534 HICON WINAPI CreateIcon( HINSTANCE instance, int width, int height, BYTE planes,
1535 BYTE depth, const void *and, const void *xor )
1537 ICONINFO info;
1538 HICON icon;
1540 TRACE_(icon)( "%dx%d, planes %d, depth %d\n", width, height, planes, depth );
1542 info.fIcon = TRUE;
1543 info.xHotspot = width / 2;
1544 info.yHotspot = height / 2;
1545 if (depth == 1)
1547 info.hbmColor = NULL;
1548 info.hbmMask = create_masked_bitmap( width, height, and, xor );
1550 else
1552 info.hbmColor = CreateBitmap( width, height, planes, depth, xor );
1553 info.hbmMask = CreateBitmap( width, height, 1, 1, and );
1556 icon = CreateIconIndirect( &info );
1558 DeleteObject( info.hbmMask );
1559 DeleteObject( info.hbmColor );
1561 return icon;
1565 /***********************************************************************
1566 * CopyIcon (USER32.@)
1568 HICON WINAPI CopyIcon( HICON icon )
1570 ICONINFO info;
1571 HICON res;
1573 if (!GetIconInfo( icon, &info ))
1574 return NULL;
1576 res = CopyImage( icon, info.fIcon ? IMAGE_ICON : IMAGE_CURSOR, 0, 0, 0 );
1577 DeleteObject( info.hbmColor );
1578 DeleteObject( info.hbmMask );
1579 return res;
1583 /***********************************************************************
1584 * DestroyIcon (USER32.@)
1586 BOOL WINAPI DestroyIcon( HICON hIcon )
1588 return NtUserDestroyCursor( hIcon, 0 );
1592 /***********************************************************************
1593 * DestroyCursor (USER32.@)
1595 BOOL WINAPI DestroyCursor( HCURSOR hCursor )
1597 return DestroyIcon( hCursor );
1600 /***********************************************************************
1601 * DrawIcon (USER32.@)
1603 BOOL WINAPI DrawIcon( HDC hdc, INT x, INT y, HICON hIcon )
1605 return NtUserDrawIconEx( hdc, x, y, hIcon, 0, 0, 0, 0, DI_NORMAL | DI_COMPAT | DI_DEFAULTSIZE );
1608 /***********************************************************************
1609 * GetClipCursor (USER32.@)
1611 BOOL WINAPI DECLSPEC_HOTPATCH GetClipCursor( RECT *rect )
1613 return NtUserGetClipCursor( rect );
1617 /***********************************************************************
1618 * SetSystemCursor (USER32.@)
1620 BOOL WINAPI SetSystemCursor(HCURSOR hcur, DWORD id)
1622 FIXME("(%p,%08lx),stub!\n", hcur, id);
1623 return TRUE;
1627 /**********************************************************************
1628 * LookupIconIdFromDirectoryEx (USER32.@)
1630 INT WINAPI LookupIconIdFromDirectoryEx( LPBYTE xdir, BOOL bIcon,
1631 INT width, INT height, UINT cFlag )
1633 const CURSORICONDIR *dir = (const CURSORICONDIR*)xdir;
1634 UINT retVal = 0;
1635 if( dir && !dir->idReserved && (dir->idType & 3) )
1637 const CURSORICONDIRENTRY* entry;
1638 int depth = (cFlag & LR_MONOCHROME) ? 1 : get_display_bpp();
1640 if( bIcon )
1641 entry = CURSORICON_FindBestIconRes( dir, ~0u, width, height, depth, LR_DEFAULTSIZE );
1642 else
1643 entry = CURSORICON_FindBestCursorRes( dir, ~0u, width, height, depth, LR_DEFAULTSIZE );
1645 if( entry ) retVal = entry->wResId;
1647 else WARN_(cursor)("invalid resource directory\n");
1648 return retVal;
1651 /**********************************************************************
1652 * LookupIconIdFromDirectory (USER32.@)
1654 INT WINAPI LookupIconIdFromDirectory( LPBYTE dir, BOOL bIcon )
1656 return LookupIconIdFromDirectoryEx( dir, bIcon, 0, 0, bIcon ? 0 : LR_MONOCHROME );
1659 /***********************************************************************
1660 * LoadCursorW (USER32.@)
1662 HCURSOR WINAPI LoadCursorW(HINSTANCE hInstance, LPCWSTR name)
1664 TRACE("%p, %s\n", hInstance, debugstr_w(name));
1666 return LoadImageW( hInstance, name, IMAGE_CURSOR, 0, 0,
1667 LR_SHARED | LR_DEFAULTSIZE );
1670 /***********************************************************************
1671 * LoadCursorA (USER32.@)
1673 HCURSOR WINAPI LoadCursorA(HINSTANCE hInstance, LPCSTR name)
1675 TRACE("%p, %s\n", hInstance, debugstr_a(name));
1677 return LoadImageA( hInstance, name, IMAGE_CURSOR, 0, 0,
1678 LR_SHARED | LR_DEFAULTSIZE );
1681 /***********************************************************************
1682 * LoadCursorFromFileW (USER32.@)
1684 HCURSOR WINAPI LoadCursorFromFileW (LPCWSTR name)
1686 TRACE("%s\n", debugstr_w(name));
1688 return LoadImageW( 0, name, IMAGE_CURSOR, 0, 0,
1689 LR_LOADFROMFILE | LR_DEFAULTSIZE );
1692 /***********************************************************************
1693 * LoadCursorFromFileA (USER32.@)
1695 HCURSOR WINAPI LoadCursorFromFileA (LPCSTR name)
1697 TRACE("%s\n", debugstr_a(name));
1699 return LoadImageA( 0, name, IMAGE_CURSOR, 0, 0,
1700 LR_LOADFROMFILE | LR_DEFAULTSIZE );
1703 /***********************************************************************
1704 * LoadIconW (USER32.@)
1706 HICON WINAPI LoadIconW(HINSTANCE hInstance, LPCWSTR name)
1708 TRACE("%p, %s\n", hInstance, debugstr_w(name));
1710 return LoadImageW( hInstance, name, IMAGE_ICON, 0, 0,
1711 LR_SHARED | LR_DEFAULTSIZE );
1714 /***********************************************************************
1715 * LoadIconA (USER32.@)
1717 HICON WINAPI LoadIconA(HINSTANCE hInstance, LPCSTR name)
1719 TRACE("%p, %s\n", hInstance, debugstr_a(name));
1721 return LoadImageA( hInstance, name, IMAGE_ICON, 0, 0,
1722 LR_SHARED | LR_DEFAULTSIZE );
1725 /**********************************************************************
1726 * GetCursorFrameInfo (USER32.@)
1728 * NOTES
1729 * So far no use has been found for the second parameter, it is currently presumed
1730 * that this parameter is reserved for future use.
1732 * PARAMS
1733 * hCursor [I] Handle to cursor for which to retrieve information
1734 * reserved [I] No purpose has been found for this parameter (may be NULL)
1735 * istep [I] The step of the cursor for which to retrieve information
1736 * rate_jiffies [O] Pointer to DWORD that receives the frame-specific delay (cannot be NULL)
1737 * num_steps [O] Pointer to DWORD that receives the number of steps in the cursor (cannot be NULL)
1739 * RETURNS
1740 * Success: Handle to a frame of the cursor (specified by istep)
1741 * Failure: NULL cursor (0)
1743 HCURSOR WINAPI GetCursorFrameInfo( HCURSOR handle, DWORD reserved, DWORD istep,
1744 DWORD *rate_jiffies, DWORD *num_steps )
1746 return NtUserGetCursorFrameInfo( handle, istep, rate_jiffies, num_steps );
1749 /**********************************************************************
1750 * GetIconInfo (USER32.@)
1752 BOOL WINAPI GetIconInfo( HICON icon, ICONINFO *info )
1754 return NtUserGetIconInfo( icon, info, NULL, NULL, NULL, 0 );
1757 /**********************************************************************
1758 * GetIconInfoExA (USER32.@)
1760 BOOL WINAPI GetIconInfoExA( HICON icon, ICONINFOEXA *info )
1762 ICONINFOEXW infoW;
1764 if (info->cbSize != sizeof(*info))
1766 SetLastError( ERROR_INVALID_PARAMETER );
1767 return FALSE;
1769 infoW.cbSize = sizeof(infoW);
1770 if (!GetIconInfoExW( icon, &infoW )) return FALSE;
1771 info->fIcon = infoW.fIcon;
1772 info->xHotspot = infoW.xHotspot;
1773 info->yHotspot = infoW.yHotspot;
1774 info->hbmColor = infoW.hbmColor;
1775 info->hbmMask = infoW.hbmMask;
1776 info->wResID = infoW.wResID;
1777 WideCharToMultiByte( CP_ACP, 0, infoW.szModName, -1, info->szModName, MAX_PATH, NULL, NULL );
1778 WideCharToMultiByte( CP_ACP, 0, infoW.szResName, -1, info->szResName, MAX_PATH, NULL, NULL );
1779 return TRUE;
1782 /**********************************************************************
1783 * GetIconInfoExW (USER32.@)
1785 BOOL WINAPI GetIconInfoExW( HICON handle, ICONINFOEXW *ret )
1787 UNICODE_STRING module, res_name;
1788 ICONINFO info;
1790 if (ret->cbSize != sizeof(*ret))
1792 SetLastError( ERROR_INVALID_PARAMETER );
1793 return FALSE;
1796 module.Buffer = ret->szModName;
1797 module.MaximumLength = sizeof(ret->szModName) - sizeof(WCHAR);
1798 res_name.Buffer = ret->szResName;
1799 res_name.MaximumLength = sizeof(ret->szResName) - sizeof(WCHAR);
1800 if (!NtUserGetIconInfo( handle, &info, &module, &res_name, NULL, 0 )) return FALSE;
1801 ret->fIcon = info.fIcon;
1802 ret->xHotspot = info.xHotspot;
1803 ret->yHotspot = info.yHotspot;
1804 ret->hbmColor = info.hbmColor;
1805 ret->hbmMask = info.hbmMask;
1806 ret->wResID = res_name.Length ? 0 : LOWORD( res_name.Buffer );
1807 ret->szModName[module.Length] = 0;
1808 ret->szResName[res_name.Length] = 0;
1809 return TRUE;
1812 /* copy an icon bitmap, even when it can't be selected into a DC */
1813 /* helper for CreateIconIndirect */
1814 static void stretch_blt_icon( HDC hdc_dst, int dst_x, int dst_y, int dst_width, int dst_height,
1815 HBITMAP src, int width, int height )
1817 HDC hdc = CreateCompatibleDC( 0 );
1819 if (!SelectObject( hdc, src )) /* do it the hard way */
1821 BITMAPINFO *info;
1822 void *bits;
1824 if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) return;
1825 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1826 info->bmiHeader.biWidth = width;
1827 info->bmiHeader.biHeight = height;
1828 info->bmiHeader.biPlanes = GetDeviceCaps( hdc_dst, PLANES );
1829 info->bmiHeader.biBitCount = GetDeviceCaps( hdc_dst, BITSPIXEL );
1830 info->bmiHeader.biCompression = BI_RGB;
1831 info->bmiHeader.biSizeImage = get_dib_image_size( width, height, info->bmiHeader.biBitCount );
1832 info->bmiHeader.biXPelsPerMeter = 0;
1833 info->bmiHeader.biYPelsPerMeter = 0;
1834 info->bmiHeader.biClrUsed = 0;
1835 info->bmiHeader.biClrImportant = 0;
1836 bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage );
1837 if (bits && GetDIBits( hdc, src, 0, height, bits, info, DIB_RGB_COLORS ))
1838 StretchDIBits( hdc_dst, dst_x, dst_y, dst_width, dst_height,
1839 0, 0, width, height, bits, info, DIB_RGB_COLORS, SRCCOPY );
1841 HeapFree( GetProcessHeap(), 0, bits );
1842 HeapFree( GetProcessHeap(), 0, info );
1844 else StretchBlt( hdc_dst, dst_x, dst_y, dst_width, dst_height, hdc, 0, 0, width, height, SRCCOPY );
1846 DeleteDC( hdc );
1849 /**********************************************************************
1850 * CreateIconIndirect (USER32.@)
1852 HICON WINAPI CreateIconIndirect(PICONINFO iconinfo)
1854 struct cursoricon_frame frame = { 0 };
1855 struct cursoricon_desc desc = { .frames = &frame };
1856 BITMAP bmpXor, bmpAnd;
1857 HICON ret;
1858 HDC hdc;
1860 TRACE("color %p, mask %p, hotspot %lux%lu, fIcon %d\n",
1861 iconinfo->hbmColor, iconinfo->hbmMask,
1862 iconinfo->xHotspot, iconinfo->yHotspot, iconinfo->fIcon);
1864 if (!iconinfo->hbmMask) return 0;
1866 GetObjectW( iconinfo->hbmMask, sizeof(bmpAnd), &bmpAnd );
1867 TRACE("mask: width %d, height %d, width bytes %d, planes %u, bpp %u\n",
1868 bmpAnd.bmWidth, bmpAnd.bmHeight, bmpAnd.bmWidthBytes,
1869 bmpAnd.bmPlanes, bmpAnd.bmBitsPixel);
1871 if (iconinfo->hbmColor)
1873 GetObjectW( iconinfo->hbmColor, sizeof(bmpXor), &bmpXor );
1874 TRACE("color: width %d, height %d, width bytes %d, planes %u, bpp %u\n",
1875 bmpXor.bmWidth, bmpXor.bmHeight, bmpXor.bmWidthBytes,
1876 bmpXor.bmPlanes, bmpXor.bmBitsPixel);
1878 frame.width = bmpXor.bmWidth;
1879 frame.height = bmpXor.bmHeight;
1880 frame.color = create_color_bitmap( frame.width, frame.height );
1882 else
1884 frame.width = bmpAnd.bmWidth;
1885 frame.height = bmpAnd.bmHeight;
1887 frame.mask = CreateBitmap( frame.width, frame.height, 1, 1, NULL );
1889 hdc = CreateCompatibleDC( 0 );
1890 SelectObject( hdc, frame.mask );
1891 stretch_blt_icon( hdc, 0, 0, frame.width, frame.height, iconinfo->hbmMask,
1892 bmpAnd.bmWidth, bmpAnd.bmHeight );
1894 if (frame.color)
1896 SelectObject( hdc, frame.color );
1897 stretch_blt_icon( hdc, 0, 0, frame.width, frame.height, iconinfo->hbmColor,
1898 frame.width, frame.height );
1900 else frame.height /= 2;
1902 DeleteDC( hdc );
1904 frame.alpha = create_alpha_bitmap( iconinfo->hbmColor, NULL, NULL );
1906 if (iconinfo->fIcon)
1908 frame.hotspot.x = frame.width / 2;
1909 frame.hotspot.y = frame.height / 2;
1911 else
1913 frame.hotspot.x = iconinfo->xHotspot;
1914 frame.hotspot.y = iconinfo->yHotspot;
1917 ret = create_cursoricon_object( &desc, iconinfo->fIcon, 0, 0, 0 );
1918 if (!ret) free_icon_frame( &frame );
1919 return ret;
1922 /***********************************************************************
1923 * DIB_FixColorsToLoadflags
1925 * Change color table entries when LR_LOADTRANSPARENT or LR_LOADMAP3DCOLORS
1926 * are in loadflags
1928 static void DIB_FixColorsToLoadflags(BITMAPINFO * bmi, UINT loadflags, BYTE pix)
1930 int colors;
1931 COLORREF c_W, c_S, c_F, c_L, c_C;
1932 int incr,i;
1933 RGBQUAD *ptr;
1934 int bitmap_type;
1935 LONG width;
1936 LONG height;
1937 WORD bpp;
1938 DWORD compr;
1940 if (((bitmap_type = DIB_GetBitmapInfo((BITMAPINFOHEADER*) bmi, &width, &height, &bpp, &compr)) == -1))
1942 WARN_(resource)("Invalid bitmap\n");
1943 return;
1946 if (bpp > 8) return;
1948 if (bitmap_type == 0) /* BITMAPCOREHEADER */
1950 incr = 3;
1951 colors = 1 << bpp;
1953 else
1955 incr = 4;
1956 colors = bmi->bmiHeader.biClrUsed;
1957 if (colors > 256) colors = 256;
1958 if (!colors && (bpp <= 8)) colors = 1 << bpp;
1961 c_W = GetSysColor(COLOR_WINDOW);
1962 c_S = GetSysColor(COLOR_3DSHADOW);
1963 c_F = GetSysColor(COLOR_3DFACE);
1964 c_L = GetSysColor(COLOR_3DLIGHT);
1966 if (loadflags & LR_LOADTRANSPARENT) {
1967 switch (bpp) {
1968 case 1: pix = pix >> 7; break;
1969 case 4: pix = pix >> 4; break;
1970 case 8: break;
1971 default:
1972 WARN_(resource)("(%d): Unsupported depth\n", bpp);
1973 return;
1975 if (pix >= colors) {
1976 WARN_(resource)("pixel has color index greater than biClrUsed!\n");
1977 return;
1979 if (loadflags & LR_LOADMAP3DCOLORS) c_W = c_F;
1980 ptr = (RGBQUAD*)((char*)bmi->bmiColors+pix*incr);
1981 ptr->rgbBlue = GetBValue(c_W);
1982 ptr->rgbGreen = GetGValue(c_W);
1983 ptr->rgbRed = GetRValue(c_W);
1985 if (loadflags & LR_LOADMAP3DCOLORS)
1986 for (i=0; i<colors; i++) {
1987 ptr = (RGBQUAD*)((char*)bmi->bmiColors+i*incr);
1988 c_C = RGB(ptr->rgbRed, ptr->rgbGreen, ptr->rgbBlue);
1989 if (c_C == RGB(128, 128, 128)) {
1990 ptr->rgbRed = GetRValue(c_S);
1991 ptr->rgbGreen = GetGValue(c_S);
1992 ptr->rgbBlue = GetBValue(c_S);
1993 } else if (c_C == RGB(192, 192, 192)) {
1994 ptr->rgbRed = GetRValue(c_F);
1995 ptr->rgbGreen = GetGValue(c_F);
1996 ptr->rgbBlue = GetBValue(c_F);
1997 } else if (c_C == RGB(223, 223, 223)) {
1998 ptr->rgbRed = GetRValue(c_L);
1999 ptr->rgbGreen = GetGValue(c_L);
2000 ptr->rgbBlue = GetBValue(c_L);
2006 /**********************************************************************
2007 * BITMAP_Load
2009 static HBITMAP BITMAP_Load( HINSTANCE instance, LPCWSTR name,
2010 INT desiredx, INT desiredy, UINT loadflags )
2012 HBITMAP hbitmap = 0, orig_bm;
2013 HRSRC hRsrc;
2014 HGLOBAL handle;
2015 const char *ptr = NULL;
2016 BITMAPINFO *info, *fix_info = NULL, *scaled_info = NULL;
2017 int size;
2018 BYTE pix;
2019 char *bits;
2020 LONG width, height, new_width, new_height;
2021 WORD bpp_dummy;
2022 DWORD compr_dummy, offbits = 0;
2023 INT bm_type;
2024 HDC screen_mem_dc = NULL;
2026 if (!(loadflags & LR_LOADFROMFILE))
2028 if (!instance)
2030 /* OEM bitmap: try to load the resource from user32.dll */
2031 instance = user32_module;
2034 if (!(hRsrc = FindResourceW( instance, name, (LPWSTR)RT_BITMAP ))) return 0;
2035 if (!(handle = LoadResource( instance, hRsrc ))) return 0;
2037 if ((info = LockResource( handle )) == NULL) return 0;
2039 else
2041 BITMAPFILEHEADER * bmfh;
2043 if (!(ptr = map_fileW( name, NULL ))) return 0;
2044 info = (BITMAPINFO *)(ptr + sizeof(BITMAPFILEHEADER));
2045 bmfh = (BITMAPFILEHEADER *)ptr;
2046 if (bmfh->bfType != 0x4d42 /* 'BM' */)
2048 WARN("Invalid/unsupported bitmap format!\n");
2049 goto end;
2051 if (bmfh->bfOffBits) offbits = bmfh->bfOffBits - sizeof(BITMAPFILEHEADER);
2054 bm_type = DIB_GetBitmapInfo( &info->bmiHeader, &width, &height,
2055 &bpp_dummy, &compr_dummy);
2056 if (bm_type == -1)
2058 WARN("Invalid bitmap format!\n");
2059 goto end;
2062 size = bitmap_info_size(info, DIB_RGB_COLORS);
2063 fix_info = HeapAlloc(GetProcessHeap(), 0, size);
2064 scaled_info = HeapAlloc(GetProcessHeap(), 0, size);
2066 if (!fix_info || !scaled_info) goto end;
2067 memcpy(fix_info, info, size);
2069 pix = *((LPBYTE)info + size);
2070 DIB_FixColorsToLoadflags(fix_info, loadflags, pix);
2072 memcpy(scaled_info, fix_info, size);
2074 if(desiredx != 0)
2075 new_width = desiredx;
2076 else
2077 new_width = width;
2079 if(desiredy != 0)
2080 new_height = height > 0 ? desiredy : -desiredy;
2081 else
2082 new_height = height;
2084 if(bm_type == 0)
2086 BITMAPCOREHEADER *core = (BITMAPCOREHEADER *)&scaled_info->bmiHeader;
2087 core->bcWidth = new_width;
2088 core->bcHeight = new_height;
2090 else
2092 /* Some sanity checks for BITMAPINFO (not applicable to BITMAPCOREINFO) */
2093 if (info->bmiHeader.biHeight > 65535 || info->bmiHeader.biWidth > 65535) {
2094 WARN("Broken BitmapInfoHeader!\n");
2095 goto end;
2098 scaled_info->bmiHeader.biWidth = new_width;
2099 scaled_info->bmiHeader.biHeight = new_height;
2102 if (new_height < 0) new_height = -new_height;
2104 if (!(screen_mem_dc = CreateCompatibleDC( 0 ))) goto end;
2106 bits = (char *)info + (offbits ? offbits : size);
2108 if (loadflags & LR_CREATEDIBSECTION)
2110 scaled_info->bmiHeader.biCompression = 0; /* DIBSection can't be compressed */
2111 hbitmap = CreateDIBSection(0, scaled_info, DIB_RGB_COLORS, NULL, 0, 0);
2113 else
2115 if (is_dib_monochrome(fix_info))
2116 hbitmap = CreateBitmap(new_width, new_height, 1, 1, NULL);
2117 else
2118 hbitmap = create_color_bitmap(new_width, new_height);
2121 orig_bm = SelectObject(screen_mem_dc, hbitmap);
2122 if (info->bmiHeader.biBitCount > 1)
2123 SetStretchBltMode(screen_mem_dc, HALFTONE);
2124 StretchDIBits(screen_mem_dc, 0, 0, new_width, new_height, 0, 0, width, height, bits, fix_info, DIB_RGB_COLORS, SRCCOPY);
2125 SelectObject(screen_mem_dc, orig_bm);
2127 end:
2128 if (screen_mem_dc) DeleteDC(screen_mem_dc);
2129 HeapFree(GetProcessHeap(), 0, scaled_info);
2130 HeapFree(GetProcessHeap(), 0, fix_info);
2131 if (loadflags & LR_LOADFROMFILE) UnmapViewOfFile( ptr );
2133 return hbitmap;
2136 /**********************************************************************
2137 * LoadImageA (USER32.@)
2139 * See LoadImageW.
2141 HANDLE WINAPI LoadImageA( HINSTANCE hinst, LPCSTR name, UINT type,
2142 INT desiredx, INT desiredy, UINT loadflags)
2144 HANDLE res;
2145 LPWSTR u_name;
2147 if (IS_INTRESOURCE(name))
2148 return LoadImageW(hinst, (LPCWSTR)name, type, desiredx, desiredy, loadflags);
2150 __TRY {
2151 DWORD len = MultiByteToWideChar( CP_ACP, 0, name, -1, NULL, 0 );
2152 u_name = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
2153 MultiByteToWideChar( CP_ACP, 0, name, -1, u_name, len );
2155 __EXCEPT_PAGE_FAULT {
2156 SetLastError( ERROR_INVALID_PARAMETER );
2157 return 0;
2159 __ENDTRY
2160 res = LoadImageW(hinst, u_name, type, desiredx, desiredy, loadflags);
2161 HeapFree(GetProcessHeap(), 0, u_name);
2162 return res;
2166 /******************************************************************************
2167 * LoadImageW (USER32.@) Loads an icon, cursor, or bitmap
2169 * PARAMS
2170 * hinst [I] Handle of instance that contains image
2171 * name [I] Name of image
2172 * type [I] Type of image
2173 * desiredx [I] Desired width
2174 * desiredy [I] Desired height
2175 * loadflags [I] Load flags
2177 * RETURNS
2178 * Success: Handle to newly loaded image
2179 * Failure: NULL
2181 * FIXME: Implementation lacks some features, see LR_ defines in winuser.h
2183 HANDLE WINAPI LoadImageW( HINSTANCE hinst, LPCWSTR name, UINT type,
2184 INT desiredx, INT desiredy, UINT loadflags )
2186 int depth;
2187 WCHAR path[MAX_PATH];
2189 TRACE_(resource)("(%p,%s,%d,%d,%d,0x%08x)\n",
2190 hinst,debugstr_w(name),type,desiredx,desiredy,loadflags);
2192 if (loadflags & LR_LOADFROMFILE)
2194 loadflags &= ~LR_SHARED;
2195 /* relative paths are not only relative to the current working directory */
2196 if (SearchPathW(NULL, name, NULL, ARRAY_SIZE(path), path, NULL)) name = path;
2198 switch (type) {
2199 case IMAGE_BITMAP:
2200 return BITMAP_Load( hinst, name, desiredx, desiredy, loadflags );
2202 case IMAGE_ICON:
2203 case IMAGE_CURSOR:
2204 depth = 1;
2205 if (!(loadflags & LR_MONOCHROME)) depth = get_display_bpp();
2206 return CURSORICON_Load(hinst, name, desiredx, desiredy, depth, (type == IMAGE_CURSOR), loadflags);
2208 return 0;
2212 /* StretchBlt from src to dest; helper for CopyImage(). */
2213 static void stretch_bitmap( HBITMAP dst, HBITMAP src, int dst_width, int dst_height, int src_width, int src_height )
2215 HDC src_dc = CreateCompatibleDC( 0 ), dst_dc = CreateCompatibleDC( 0 );
2217 SelectObject( src_dc, src );
2218 SelectObject( dst_dc, dst );
2219 StretchBlt( dst_dc, 0, 0, dst_width, dst_height, src_dc, 0, 0, src_width, src_height, SRCCOPY );
2221 DeleteDC( src_dc );
2222 DeleteDC( dst_dc );
2226 /******************************************************************************
2227 * CopyImage (USER32.@) Creates new image and copies attributes to it
2229 * PARAMS
2230 * hnd [I] Handle to image to copy
2231 * type [I] Type of image to copy
2232 * desiredx [I] Desired width of new image
2233 * desiredy [I] Desired height of new image
2234 * flags [I] Copy flags
2236 * RETURNS
2237 * Success: Handle to newly created image
2238 * Failure: NULL
2240 * BUGS
2241 * Only Windows NT 4.0 supports the LR_COPYRETURNORG flag for bitmaps,
2242 * all other versions (95/2000/XP have been tested) ignore it.
2244 * NOTES
2245 * If LR_CREATEDIBSECTION is absent, the copy will be monochrome for
2246 * a monochrome source bitmap or if LR_MONOCHROME is present, otherwise
2247 * the copy will have the same depth as the screen.
2248 * The content of the image will only be copied if the bit depth of the
2249 * original image is compatible with the bit depth of the screen, or
2250 * if the source is a DIB section.
2251 * The LR_MONOCHROME flag is ignored if LR_CREATEDIBSECTION is present.
2253 HANDLE WINAPI CopyImage( HANDLE hnd, UINT type, INT desiredx,
2254 INT desiredy, UINT flags )
2256 TRACE("hnd=%p, type=%u, desiredx=%d, desiredy=%d, flags=%x\n",
2257 hnd, type, desiredx, desiredy, flags);
2259 switch (type)
2261 case IMAGE_BITMAP:
2263 HBITMAP res = NULL;
2264 DIBSECTION ds;
2265 int objSize;
2266 BITMAPINFO * bi;
2268 objSize = GetObjectW( hnd, sizeof(ds), &ds );
2269 if (!objSize) return 0;
2270 if ((desiredx < 0) || (desiredy < 0)) return 0;
2272 if (flags & LR_COPYFROMRESOURCE)
2274 FIXME("The flag LR_COPYFROMRESOURCE is not implemented for bitmaps\n");
2277 if (desiredx == 0) desiredx = ds.dsBm.bmWidth;
2278 if (desiredy == 0) desiredy = ds.dsBm.bmHeight;
2280 /* Allocate memory for a BITMAPINFOHEADER structure and a
2281 color table. The maximum number of colors in a color table
2282 is 256 which corresponds to a bitmap with depth 8.
2283 Bitmaps with higher depths don't have color tables. */
2284 bi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
2285 if (!bi) return 0;
2287 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
2288 bi->bmiHeader.biPlanes = ds.dsBm.bmPlanes;
2289 bi->bmiHeader.biBitCount = ds.dsBm.bmBitsPixel;
2290 bi->bmiHeader.biCompression = BI_RGB;
2292 if (flags & LR_CREATEDIBSECTION)
2294 /* Create a DIB section. LR_MONOCHROME is ignored */
2295 void * bits;
2296 HDC dc = CreateCompatibleDC(NULL);
2298 if (objSize == sizeof(DIBSECTION))
2300 /* The source bitmap is a DIB.
2301 Get its attributes to create an exact copy */
2302 memcpy(bi, &ds.dsBmih, sizeof(BITMAPINFOHEADER));
2305 bi->bmiHeader.biWidth = desiredx;
2306 bi->bmiHeader.biHeight = desiredy;
2308 /* Get the color table or the color masks */
2309 GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
2311 res = CreateDIBSection(dc, bi, DIB_RGB_COLORS, &bits, NULL, 0);
2312 DeleteDC(dc);
2314 else
2316 /* Create a device-dependent bitmap */
2318 BOOL monochrome = (flags & LR_MONOCHROME);
2320 if (objSize == sizeof(DIBSECTION))
2322 /* The source bitmap is a DIB section.
2323 Get its attributes */
2324 HDC dc = CreateCompatibleDC(NULL);
2325 bi->bmiHeader.biWidth = ds.dsBm.bmWidth;
2326 bi->bmiHeader.biHeight = ds.dsBm.bmHeight;
2327 GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
2328 DeleteDC(dc);
2330 if (!monochrome && ds.dsBm.bmBitsPixel == 1)
2332 /* Look if the colors of the DIB are black and white */
2334 monochrome =
2335 (bi->bmiColors[0].rgbRed == 0xff
2336 && bi->bmiColors[0].rgbGreen == 0xff
2337 && bi->bmiColors[0].rgbBlue == 0xff
2338 && bi->bmiColors[0].rgbReserved == 0
2339 && bi->bmiColors[1].rgbRed == 0
2340 && bi->bmiColors[1].rgbGreen == 0
2341 && bi->bmiColors[1].rgbBlue == 0
2342 && bi->bmiColors[1].rgbReserved == 0)
2344 (bi->bmiColors[0].rgbRed == 0
2345 && bi->bmiColors[0].rgbGreen == 0
2346 && bi->bmiColors[0].rgbBlue == 0
2347 && bi->bmiColors[0].rgbReserved == 0
2348 && bi->bmiColors[1].rgbRed == 0xff
2349 && bi->bmiColors[1].rgbGreen == 0xff
2350 && bi->bmiColors[1].rgbBlue == 0xff
2351 && bi->bmiColors[1].rgbReserved == 0);
2354 else if (!monochrome)
2356 monochrome = ds.dsBm.bmBitsPixel == 1;
2359 if (monochrome)
2360 res = CreateBitmap(desiredx, desiredy, 1, 1, NULL);
2361 else
2362 res = create_color_bitmap(desiredx, desiredy);
2365 if (res)
2367 /* Only copy the bitmap if it's a DIB section or if it's
2368 compatible to the screen */
2369 if (objSize == sizeof(DIBSECTION) ||
2370 ds.dsBm.bmBitsPixel == 1 ||
2371 ds.dsBm.bmBitsPixel == get_display_bpp())
2373 /* The source bitmap may already be selected in a device context,
2374 use GetDIBits/StretchDIBits and not StretchBlt */
2376 HDC dc;
2377 void * bits;
2379 dc = CreateCompatibleDC(NULL);
2380 if (ds.dsBm.bmBitsPixel > 1)
2381 SetStretchBltMode(dc, HALFTONE);
2383 bi->bmiHeader.biWidth = ds.dsBm.bmWidth;
2384 bi->bmiHeader.biHeight = ds.dsBm.bmHeight;
2385 bi->bmiHeader.biSizeImage = 0;
2386 bi->bmiHeader.biClrUsed = 0;
2387 bi->bmiHeader.biClrImportant = 0;
2389 /* Fill in biSizeImage */
2390 GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
2391 bits = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bi->bmiHeader.biSizeImage);
2393 if (bits)
2395 HBITMAP oldBmp;
2397 /* Get the image bits of the source bitmap */
2398 GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, bits, bi, DIB_RGB_COLORS);
2400 /* Copy it to the destination bitmap */
2401 oldBmp = SelectObject(dc, res);
2402 StretchDIBits(dc, 0, 0, desiredx, desiredy,
2403 0, 0, ds.dsBm.bmWidth, ds.dsBm.bmHeight,
2404 bits, bi, DIB_RGB_COLORS, SRCCOPY);
2405 SelectObject(dc, oldBmp);
2407 HeapFree(GetProcessHeap(), 0, bits);
2410 DeleteDC(dc);
2413 if (flags & LR_COPYDELETEORG)
2415 DeleteObject(hnd);
2418 HeapFree(GetProcessHeap(), 0, bi);
2419 return res;
2421 case IMAGE_ICON:
2422 case IMAGE_CURSOR:
2424 ICONINFOEXW icon_info;
2425 int depth = (flags & LR_MONOCHROME) ? 1 : get_display_bpp();
2426 HICON resource_icon = 0;
2427 LONG width, height;
2428 ICONINFO info;
2429 HANDLE module;
2430 HICON res;
2432 icon_info.cbSize = sizeof(icon_info);
2433 if (!GetIconInfoExW( hnd, &icon_info )) return 0;
2435 if (icon_info.szModName[0] && (flags & LR_COPYFROMRESOURCE) &&
2436 (module = GetModuleHandleW( icon_info.szModName )))
2438 const WCHAR *res = icon_info.szResName[0] ? icon_info.szResName
2439 : MAKEINTRESOURCEW( icon_info.wResID );
2440 resource_icon = CURSORICON_Load( module, res, desiredx, desiredy, depth,
2441 !icon_info.fIcon, flags );
2442 DeleteObject( icon_info.hbmColor );
2443 DeleteObject( icon_info.hbmMask );
2444 NtUserGetIconSize( resource_icon, 0, &width, &height );
2445 if (!GetIconInfoExW( resource_icon, &icon_info )) return 0;
2447 else NtUserGetIconSize( hnd, 0, &width, &height );
2448 height /= 2;
2450 if (flags & LR_DEFAULTSIZE)
2452 if (!desiredx) desiredx = GetSystemMetrics( type == IMAGE_ICON ? SM_CXICON : SM_CXCURSOR );
2453 if (!desiredy) desiredy = GetSystemMetrics( type == IMAGE_ICON ? SM_CYICON : SM_CYCURSOR );
2455 else
2457 if (!desiredx) desiredx = width;
2458 if (!desiredy) desiredy = height;
2461 info.fIcon = icon_info.fIcon;
2462 info.xHotspot = icon_info.xHotspot;
2463 info.yHotspot = icon_info.yHotspot;
2465 if (desiredx == width && desiredy == height)
2467 info.hbmColor = icon_info.hbmColor;
2468 info.hbmMask = icon_info.hbmMask;
2469 res = CreateIconIndirect( &info );
2471 else
2473 if (icon_info.hbmColor)
2475 if (!(info.hbmColor = create_color_bitmap( desiredx, desiredy )))
2477 DeleteObject( icon_info.hbmColor );
2478 DeleteObject( icon_info.hbmMask );
2479 if (resource_icon) DestroyIcon( resource_icon );
2480 return 0;
2482 stretch_bitmap( info.hbmColor, icon_info.hbmColor, desiredx, desiredy,
2483 width, height );
2485 if (!(info.hbmMask = CreateBitmap( desiredx, desiredy, 1, 1, NULL )))
2487 DeleteObject( icon_info.hbmColor );
2488 DeleteObject( icon_info.hbmMask );
2489 DeleteObject( info.hbmColor );
2490 if (resource_icon) DestroyIcon( resource_icon );
2491 return 0;
2493 stretch_bitmap( info.hbmMask, icon_info.hbmMask, desiredx, desiredy,
2494 width, height );
2496 else
2498 info.hbmColor = NULL;
2500 if (!(info.hbmMask = CreateBitmap( desiredx, desiredy * 2, 1, 1, NULL )))
2502 DeleteObject( icon_info.hbmColor );
2503 DeleteObject( icon_info.hbmMask );
2504 if (resource_icon) DestroyIcon( resource_icon );
2505 return 0;
2507 stretch_bitmap( info.hbmMask, icon_info.hbmMask, desiredx, desiredy * 2,
2508 width, height * 2 );
2511 res = CreateIconIndirect( &info );
2513 DeleteObject( info.hbmColor );
2514 DeleteObject( info.hbmMask );
2517 DeleteObject( icon_info.hbmColor );
2518 DeleteObject( icon_info.hbmMask );
2520 if (res && (flags & LR_COPYDELETEORG)) DestroyIcon( hnd );
2521 if (resource_icon) DestroyIcon( resource_icon );
2522 return res;
2525 return 0;
2529 /******************************************************************************
2530 * LoadBitmapW (USER32.@) Loads bitmap from the executable file
2532 * RETURNS
2533 * Success: Handle to specified bitmap
2534 * Failure: NULL
2536 HBITMAP WINAPI LoadBitmapW(
2537 HINSTANCE instance, /* [in] Handle to application instance */
2538 LPCWSTR name) /* [in] Address of bitmap resource name */
2540 return LoadImageW( instance, name, IMAGE_BITMAP, 0, 0, 0 );
2543 /**********************************************************************
2544 * LoadBitmapA (USER32.@)
2546 * See LoadBitmapW.
2548 HBITMAP WINAPI LoadBitmapA( HINSTANCE instance, LPCSTR name )
2550 return LoadImageA( instance, name, IMAGE_BITMAP, 0, 0, 0 );