ntdll: Buffer pagemap reads in fill_working_set_info().
[wine.git] / dlls / user32 / cursoricon.c
blob0bfaca130d71a903c72d11a62b960222dcf6ccb7
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 <png.h>
28 #include <stdlib.h>
30 #include "user_private.h"
31 #include "controls.h"
32 #include "wine/exception.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(cursor);
36 WINE_DECLARE_DEBUG_CHANNEL(icon);
37 WINE_DECLARE_DEBUG_CHANNEL(resource);
39 #define RIFF_FOURCC( c0, c1, c2, c3 ) \
40 ( (DWORD)(BYTE)(c0) | ( (DWORD)(BYTE)(c1) << 8 ) | \
41 ( (DWORD)(BYTE)(c2) << 16 ) | ( (DWORD)(BYTE)(c3) << 24 ) )
42 #define PNG_SIGN RIFF_FOURCC(0x89,'P','N','G')
44 /**********************************************************************
45 * User objects management
48 static HBITMAP create_color_bitmap( int width, int height )
50 HDC hdc = get_display_dc();
51 HBITMAP ret = CreateCompatibleBitmap( hdc, width, height );
52 release_display_dc( hdc );
53 return ret;
56 static int get_display_bpp(void)
58 HDC hdc = get_display_dc();
59 int ret = GetDeviceCaps( hdc, BITSPIXEL );
60 release_display_dc( hdc );
61 return ret;
64 static void free_icon_frame( struct cursoricon_frame *frame )
66 if (frame->color) DeleteObject( frame->color );
67 if (frame->alpha) DeleteObject( frame->alpha );
68 if (frame->mask) DeleteObject( frame->mask );
72 /***********************************************************************
73 * map_fileW
75 * Helper function to map a file to memory:
76 * name - file name
77 * [RETURN] ptr - pointer to mapped file
78 * [RETURN] filesize - pointer size of file to be stored if not NULL
80 static const void *map_fileW( LPCWSTR name, LPDWORD filesize )
82 HANDLE hFile, hMapping;
83 LPVOID ptr = NULL;
85 hFile = CreateFileW( name, GENERIC_READ, FILE_SHARE_READ, NULL,
86 OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0 );
87 if (hFile != INVALID_HANDLE_VALUE)
89 hMapping = CreateFileMappingW( hFile, NULL, PAGE_READONLY, 0, 0, NULL );
90 if (hMapping)
92 ptr = MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 );
93 CloseHandle( hMapping );
94 if (filesize)
95 *filesize = GetFileSize( hFile, NULL );
97 CloseHandle( hFile );
99 return ptr;
103 /***********************************************************************
104 * get_dib_image_size
106 * Return the size of a DIB bitmap in bytes.
108 static int get_dib_image_size( int width, int height, int depth )
110 return (((width * depth + 31) / 8) & ~3) * abs( height );
114 /***********************************************************************
115 * bitmap_info_size
117 * Return the size of the bitmap info structure including color table.
119 int bitmap_info_size( const BITMAPINFO * info, WORD coloruse )
121 unsigned int colors, size, masks = 0;
123 if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
125 const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *)info;
126 colors = (core->bcBitCount <= 8) ? 1 << core->bcBitCount : 0;
127 return sizeof(BITMAPCOREHEADER) + colors *
128 ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBTRIPLE) : sizeof(WORD));
130 else /* assume BITMAPINFOHEADER */
132 colors = info->bmiHeader.biClrUsed;
133 if (colors > 256) /* buffer overflow otherwise */
134 colors = 256;
135 if (!colors && (info->bmiHeader.biBitCount <= 8))
136 colors = 1 << info->bmiHeader.biBitCount;
137 if (info->bmiHeader.biCompression == BI_BITFIELDS) masks = 3;
138 size = max( info->bmiHeader.biSize, sizeof(BITMAPINFOHEADER) + masks * sizeof(DWORD) );
139 return size + colors * ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBQUAD) : sizeof(WORD));
144 /***********************************************************************
145 * is_dib_monochrome
147 * Returns whether a DIB can be converted to a monochrome DDB.
149 * A DIB can be converted if its color table contains only black and
150 * white. Black must be the first color in the color table.
152 * Note : If the first color in the color table is white followed by
153 * black, we can't convert it to a monochrome DDB with
154 * SetDIBits, because black and white would be inverted.
156 static BOOL is_dib_monochrome( const BITMAPINFO* info )
158 if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
160 const RGBTRIPLE *rgb = ((const BITMAPCOREINFO*)info)->bmciColors;
162 if (((const BITMAPCOREINFO*)info)->bmciHeader.bcBitCount != 1) return FALSE;
164 /* Check if the first color is black */
165 if ((rgb->rgbtRed == 0) && (rgb->rgbtGreen == 0) && (rgb->rgbtBlue == 0))
167 rgb++;
169 /* Check if the second color is white */
170 return ((rgb->rgbtRed == 0xff) && (rgb->rgbtGreen == 0xff)
171 && (rgb->rgbtBlue == 0xff));
173 else return FALSE;
175 else /* assume BITMAPINFOHEADER */
177 const RGBQUAD *rgb = info->bmiColors;
179 if (info->bmiHeader.biBitCount != 1) return FALSE;
181 /* Check if the first color is black */
182 if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) &&
183 (rgb->rgbBlue == 0) && (rgb->rgbReserved == 0))
185 rgb++;
187 /* Check if the second color is white */
188 return ((rgb->rgbRed == 0xff) && (rgb->rgbGreen == 0xff)
189 && (rgb->rgbBlue == 0xff) && (rgb->rgbReserved == 0));
191 else return FALSE;
195 /***********************************************************************
196 * DIB_GetBitmapInfo
198 * Get the info from a bitmap header.
199 * Return 1 for INFOHEADER, 0 for COREHEADER, -1 in case of failure.
201 static int DIB_GetBitmapInfo( const BITMAPINFOHEADER *header, LONG *width,
202 LONG *height, WORD *bpp, DWORD *compr )
204 if (header->biSize == sizeof(BITMAPCOREHEADER))
206 const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *)header;
207 *width = core->bcWidth;
208 *height = core->bcHeight;
209 *bpp = core->bcBitCount;
210 *compr = 0;
211 return 0;
213 else if (header->biSize == sizeof(BITMAPINFOHEADER) ||
214 header->biSize == sizeof(BITMAPV4HEADER) ||
215 header->biSize == sizeof(BITMAPV5HEADER))
217 *width = header->biWidth;
218 *height = header->biHeight;
219 *bpp = header->biBitCount;
220 *compr = header->biCompression;
221 return 1;
223 WARN("unknown/wrong size (%lu) for header\n", header->biSize);
224 return -1;
227 /**********************************************************************
228 * get_icon_size
230 BOOL get_icon_size( HICON handle, SIZE *size )
232 BOOL ret = NtUserGetIconSize( handle, 0, &size->cx, &size->cy );
233 if (ret) size->cy /= 2;
234 return ret;
237 struct png_wrapper
239 const char *buffer;
240 size_t size, pos;
243 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
245 struct png_wrapper *png = png_get_io_ptr(png_ptr);
247 if (png->size - png->pos >= length)
249 memcpy(data, png->buffer + png->pos, length);
250 png->pos += length;
252 else
254 png_error(png_ptr, "failed to read PNG data");
258 static unsigned be_uint(unsigned val)
260 union
262 unsigned val;
263 unsigned char c[4];
264 } u;
266 u.val = val;
267 return (u.c[0] << 24) | (u.c[1] << 16) | (u.c[2] << 8) | u.c[3];
270 static BOOL get_png_info(const void *png_data, DWORD size, int *width, int *height, int *bpp)
272 static const char png_sig[8] = { 0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a };
273 static const char png_IHDR[8] = { 0,0,0,0x0d,'I','H','D','R' };
274 const struct
276 char png_sig[8];
277 char ihdr_sig[8];
278 unsigned width, height;
279 char bit_depth, color_type, compression, filter, interlace;
280 } *png = png_data;
282 if (size < sizeof(*png)) return FALSE;
283 if (memcmp(png->png_sig, png_sig, sizeof(png_sig)) != 0) return FALSE;
284 if (memcmp(png->ihdr_sig, png_IHDR, sizeof(png_IHDR)) != 0) return FALSE;
286 *bpp = (png->color_type == PNG_COLOR_TYPE_RGB_ALPHA) ? 32 : 24;
287 *width = be_uint(png->width);
288 *height = be_uint(png->height);
290 return TRUE;
293 static BITMAPINFO *load_png(const char *png_data, DWORD *size)
295 struct png_wrapper png;
296 png_structp png_ptr;
297 png_infop info_ptr;
298 png_bytep *row_pointers = NULL;
299 int color_type, bit_depth, bpp, width, height;
300 int rowbytes, image_size, mask_size = 0, i;
301 BITMAPINFO *info = NULL;
302 unsigned char *image_data;
304 if (!get_png_info(png_data, *size, &width, &height, &bpp)) return NULL;
306 png.buffer = png_data;
307 png.size = *size;
308 png.pos = 0;
310 /* initialize libpng */
311 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
312 if (!png_ptr) return NULL;
314 info_ptr = png_create_info_struct(png_ptr);
315 if (!info_ptr)
317 png_destroy_read_struct(&png_ptr, NULL, NULL);
318 return NULL;
321 /* set up setjmp/longjmp error handling */
322 if (setjmp(png_jmpbuf(png_ptr)))
324 free(row_pointers);
325 RtlFreeHeap(GetProcessHeap(), 0, info);
326 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
327 return NULL;
330 png_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
332 /* set up custom i/o handling */
333 png_set_read_fn(png_ptr, &png, user_read_data);
335 /* read the header */
336 png_read_info(png_ptr, info_ptr);
338 color_type = png_get_color_type(png_ptr, info_ptr);
339 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
341 /* expand grayscale image data to rgb */
342 if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
343 png_set_gray_to_rgb(png_ptr);
345 /* expand palette image data to rgb */
346 if (color_type == PNG_COLOR_TYPE_PALETTE || bit_depth < 8)
347 png_set_expand(png_ptr);
349 /* update color type information */
350 png_read_update_info(png_ptr, info_ptr);
352 color_type = png_get_color_type(png_ptr, info_ptr);
353 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
355 bpp = 0;
357 switch (color_type)
359 case PNG_COLOR_TYPE_RGB:
360 if (bit_depth == 8)
361 bpp = 24;
362 break;
364 case PNG_COLOR_TYPE_RGB_ALPHA:
365 if (bit_depth == 8)
367 png_set_bgr(png_ptr);
368 bpp = 32;
370 break;
372 default:
373 break;
376 if (!bpp)
378 FIXME("unsupported PNG color format %d, %d bpp\n", color_type, bit_depth);
379 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
380 return NULL;
383 width = png_get_image_width(png_ptr, info_ptr);
384 height = png_get_image_height(png_ptr, info_ptr);
386 rowbytes = (width * bpp + 7) / 8;
387 image_size = height * rowbytes;
388 if (bpp != 32) /* add a mask if there is no alpha */
389 mask_size = (width + 7) / 8 * height;
391 info = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(BITMAPINFOHEADER) + image_size + mask_size);
392 if (!info)
394 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
395 return NULL;
398 image_data = (unsigned char *)info + sizeof(BITMAPINFOHEADER);
399 memset(image_data + image_size, 0, mask_size);
401 row_pointers = malloc(height * sizeof(png_bytep));
402 if (!row_pointers)
404 RtlFreeHeap(GetProcessHeap(), 0, info);
405 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
406 return NULL;
409 /* upside down */
410 for (i = 0; i < height; i++)
411 row_pointers[i] = image_data + (height - i - 1) * rowbytes;
413 png_read_image(png_ptr, row_pointers);
414 free(row_pointers);
415 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
417 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
418 info->bmiHeader.biWidth = width;
419 info->bmiHeader.biHeight = height * 2;
420 info->bmiHeader.biPlanes = 1;
421 info->bmiHeader.biBitCount = bpp;
422 info->bmiHeader.biCompression = BI_RGB;
423 info->bmiHeader.biSizeImage = image_size;
424 info->bmiHeader.biXPelsPerMeter = 0;
425 info->bmiHeader.biYPelsPerMeter = 0;
426 info->bmiHeader.biClrUsed = 0;
427 info->bmiHeader.biClrImportant = 0;
429 *size = sizeof(BITMAPINFOHEADER) + image_size + mask_size;
430 return info;
435 * The following macro functions account for the irregularities of
436 * accessing cursor and icon resources in files and resource entries.
438 typedef BOOL (*fnGetCIEntry)( LPCVOID dir, DWORD size, int n,
439 int *width, int *height, int *bits );
441 /**********************************************************************
442 * CURSORICON_FindBestIcon
444 * Find the icon closest to the requested size and bit depth.
446 static int CURSORICON_FindBestIcon( LPCVOID dir, DWORD size, fnGetCIEntry get_entry,
447 int width, int height, int depth, UINT loadflags )
449 int i, cx, cy, bits, bestEntry = -1;
450 UINT iTotalDiff, iXDiff=0, iYDiff=0, iColorDiff;
451 UINT iTempXDiff, iTempYDiff, iTempColorDiff;
453 /* Find Best Fit */
454 iTotalDiff = 0xFFFFFFFF;
455 iColorDiff = 0xFFFFFFFF;
457 if (loadflags & LR_DEFAULTSIZE)
459 if (!width) width = GetSystemMetrics( SM_CXICON );
460 if (!height) height = GetSystemMetrics( SM_CYICON );
462 else if (!width && !height)
464 /* use the size of the first entry */
465 if (!get_entry( dir, size, 0, &width, &height, &bits )) return -1;
466 iTotalDiff = 0;
469 for ( i = 0; iTotalDiff && get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
471 iTempXDiff = abs(width - cx);
472 iTempYDiff = abs(height - cy);
474 if(iTotalDiff > (iTempXDiff + iTempYDiff))
476 iXDiff = iTempXDiff;
477 iYDiff = iTempYDiff;
478 iTotalDiff = iXDiff + iYDiff;
482 /* Find Best Colors for Best Fit */
483 for ( i = 0; get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
485 TRACE("entry %d: %d x %d, %d bpp\n", i, cx, cy, bits);
487 if(abs(width - cx) == iXDiff && abs(height - cy) == iYDiff)
489 iTempColorDiff = abs(depth - bits);
490 if(iColorDiff > iTempColorDiff)
492 bestEntry = i;
493 iColorDiff = iTempColorDiff;
498 return bestEntry;
501 static BOOL CURSORICON_GetResIconEntry( LPCVOID dir, DWORD size, int n,
502 int *width, int *height, int *bits )
504 const CURSORICONDIR *resdir = dir;
505 const ICONRESDIR *icon;
507 if ( resdir->idCount <= n )
508 return FALSE;
509 if ((const char *)&resdir->idEntries[n + 1] - (const char *)dir > size)
510 return FALSE;
511 icon = &resdir->idEntries[n].ResInfo.icon;
512 *width = icon->bWidth;
513 *height = icon->bHeight;
514 *bits = resdir->idEntries[n].wBitCount;
515 if (!*width && !*height) *width = *height = 256;
516 return TRUE;
519 /**********************************************************************
520 * CURSORICON_FindBestCursor
522 * Find the cursor closest to the requested size.
524 * FIXME: parameter 'color' ignored.
526 static int CURSORICON_FindBestCursor( LPCVOID dir, DWORD size, fnGetCIEntry get_entry,
527 int width, int height, int depth, UINT loadflags )
529 int i, maxwidth, maxheight, maxbits, cx, cy, bits, bestEntry = -1;
531 if (loadflags & LR_DEFAULTSIZE)
533 if (!width) width = GetSystemMetrics( SM_CXCURSOR );
534 if (!height) height = GetSystemMetrics( SM_CYCURSOR );
536 else if (!width && !height)
538 /* use the first entry */
539 if (!get_entry( dir, size, 0, &width, &height, &bits )) return -1;
540 return 0;
543 /* First find the largest one smaller than or equal to the requested size*/
545 maxwidth = maxheight = maxbits = 0;
546 for ( i = 0; get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
548 if (cx > width || cy > height) continue;
549 if (cx < maxwidth || cy < maxheight) continue;
550 if (cx == maxwidth && cy == maxheight)
552 if (loadflags & LR_MONOCHROME)
554 if (maxbits && bits >= maxbits) continue;
556 else if (bits <= maxbits) continue;
558 bestEntry = i;
559 maxwidth = cx;
560 maxheight = cy;
561 maxbits = bits;
563 if (bestEntry != -1) return bestEntry;
565 /* Now find the smallest one larger than the requested size */
567 maxwidth = maxheight = 255;
568 for ( i = 0; get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
570 if (cx > maxwidth || cy > maxheight) continue;
571 if (cx == maxwidth && cy == maxheight)
573 if (loadflags & LR_MONOCHROME)
575 if (maxbits && bits >= maxbits) continue;
577 else if (bits <= maxbits) continue;
579 bestEntry = i;
580 maxwidth = cx;
581 maxheight = cy;
582 maxbits = bits;
584 if (bestEntry == -1) bestEntry = 0;
586 return bestEntry;
589 static BOOL CURSORICON_GetResCursorEntry( LPCVOID dir, DWORD size, int n,
590 int *width, int *height, int *bits )
592 const CURSORICONDIR *resdir = dir;
593 const CURSORDIR *cursor;
595 if ( resdir->idCount <= n )
596 return FALSE;
597 if ((const char *)&resdir->idEntries[n + 1] - (const char *)dir > size)
598 return FALSE;
599 cursor = &resdir->idEntries[n].ResInfo.cursor;
600 *width = cursor->wWidth;
601 *height = cursor->wHeight;
602 *bits = resdir->idEntries[n].wBitCount;
603 if (*height == *width * 2) *height /= 2;
604 return TRUE;
607 static const CURSORICONDIRENTRY *CURSORICON_FindBestIconRes( const CURSORICONDIR * dir, DWORD size,
608 int width, int height, int depth,
609 UINT loadflags )
611 int n;
613 n = CURSORICON_FindBestIcon( dir, size, CURSORICON_GetResIconEntry,
614 width, height, depth, loadflags );
615 if ( n < 0 )
616 return NULL;
617 return &dir->idEntries[n];
620 static const CURSORICONDIRENTRY *CURSORICON_FindBestCursorRes( const CURSORICONDIR *dir, DWORD size,
621 int width, int height, int depth,
622 UINT loadflags )
624 int n = CURSORICON_FindBestCursor( dir, size, CURSORICON_GetResCursorEntry,
625 width, height, depth, loadflags );
626 if ( n < 0 )
627 return NULL;
628 return &dir->idEntries[n];
631 static BOOL CURSORICON_GetFileEntry( LPCVOID dir, DWORD size, int n,
632 int *width, int *height, int *bits )
634 const CURSORICONFILEDIR *filedir = dir;
635 const CURSORICONFILEDIRENTRY *entry;
636 const BITMAPINFOHEADER *info;
638 if ( filedir->idCount <= n )
639 return FALSE;
640 if ((const char *)&filedir->idEntries[n + 1] - (const char *)dir > size)
641 return FALSE;
642 entry = &filedir->idEntries[n];
643 if (entry->dwDIBOffset > size - sizeof(info->biSize)) return FALSE;
644 info = (const BITMAPINFOHEADER *)((const char *)dir + entry->dwDIBOffset);
646 if (info->biSize == PNG_SIGN) return get_png_info(info, size, width, height, bits);
648 if (info->biSize != sizeof(BITMAPCOREHEADER))
650 if ((const char *)(info + 1) - (const char *)dir > size) return FALSE;
651 *bits = info->biBitCount;
653 else
655 const BITMAPCOREHEADER *coreinfo = (const BITMAPCOREHEADER *)((const char *)dir + entry->dwDIBOffset);
656 if ((const char *)(coreinfo + 1) - (const char *)dir > size) return FALSE;
657 *bits = coreinfo->bcBitCount;
659 *width = entry->bWidth;
660 *height = entry->bHeight;
661 return TRUE;
664 static const CURSORICONFILEDIRENTRY *CURSORICON_FindBestCursorFile( const CURSORICONFILEDIR *dir, DWORD size,
665 int width, int height, int depth,
666 UINT loadflags )
668 int n = CURSORICON_FindBestCursor( dir, size, CURSORICON_GetFileEntry,
669 width, height, depth, loadflags );
670 if ( n < 0 )
671 return NULL;
672 return &dir->idEntries[n];
675 static const CURSORICONFILEDIRENTRY *CURSORICON_FindBestIconFile( const CURSORICONFILEDIR *dir, DWORD size,
676 int width, int height, int depth,
677 UINT loadflags )
679 int n = CURSORICON_FindBestIcon( dir, size, CURSORICON_GetFileEntry,
680 width, height, depth, loadflags );
681 if ( n < 0 )
682 return NULL;
683 return &dir->idEntries[n];
686 /***********************************************************************
687 * bmi_has_alpha
689 static BOOL bmi_has_alpha( const BITMAPINFO *info, const void *bits )
691 int i;
692 BOOL has_alpha = FALSE;
693 const unsigned char *ptr = bits;
695 if (info->bmiHeader.biBitCount != 32) return FALSE;
696 for (i = 0; i < info->bmiHeader.biWidth * abs(info->bmiHeader.biHeight); i++, ptr += 4)
697 if ((has_alpha = (ptr[3] != 0))) break;
698 return has_alpha;
701 /***********************************************************************
702 * create_alpha_bitmap
704 * Create the alpha bitmap for a 32-bpp icon that has an alpha channel.
706 static HBITMAP create_alpha_bitmap( HBITMAP color, const BITMAPINFO *src_info, const void *color_bits )
708 HBITMAP alpha = 0;
709 BITMAPINFO *info = NULL;
710 BITMAP bm;
711 HDC hdc;
712 void *bits;
713 unsigned char *ptr;
714 int i;
716 if (!GetObjectW( color, sizeof(bm), &bm )) return 0;
717 if (bm.bmBitsPixel != 32) return 0;
719 if (!(hdc = CreateCompatibleDC( 0 ))) return 0;
720 if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) goto done;
721 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
722 info->bmiHeader.biWidth = bm.bmWidth;
723 info->bmiHeader.biHeight = -bm.bmHeight;
724 info->bmiHeader.biPlanes = 1;
725 info->bmiHeader.biBitCount = 32;
726 info->bmiHeader.biCompression = BI_RGB;
727 info->bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4;
728 info->bmiHeader.biXPelsPerMeter = 0;
729 info->bmiHeader.biYPelsPerMeter = 0;
730 info->bmiHeader.biClrUsed = 0;
731 info->bmiHeader.biClrImportant = 0;
732 if (!(alpha = CreateDIBSection( hdc, info, DIB_RGB_COLORS, &bits, NULL, 0 ))) goto done;
734 if (src_info)
736 SelectObject( hdc, alpha );
737 StretchDIBits( hdc, 0, 0, bm.bmWidth, bm.bmHeight,
738 0, 0, src_info->bmiHeader.biWidth, src_info->bmiHeader.biHeight,
739 color_bits, src_info, DIB_RGB_COLORS, SRCCOPY );
742 else
744 GetDIBits( hdc, color, 0, bm.bmHeight, bits, info, DIB_RGB_COLORS );
745 if (!bmi_has_alpha( info, bits ))
747 DeleteObject( alpha );
748 alpha = 0;
749 goto done;
753 /* pre-multiply by alpha */
754 for (i = 0, ptr = bits; i < bm.bmWidth * bm.bmHeight; i++, ptr += 4)
756 unsigned int alpha = ptr[3];
757 ptr[0] = (ptr[0] * alpha + 127) / 255;
758 ptr[1] = (ptr[1] * alpha + 127) / 255;
759 ptr[2] = (ptr[2] * alpha + 127) / 255;
762 done:
763 DeleteDC( hdc );
764 HeapFree( GetProcessHeap(), 0, info );
765 return alpha;
768 static BOOL create_icon_frame( const BITMAPINFO *bmi, DWORD maxsize, POINT hotspot, BOOL is_icon,
769 INT width, INT height, UINT flags, struct cursoricon_frame *frame )
771 DWORD size, color_size, mask_size, compr;
772 const void *color_bits, *mask_bits;
773 void *alpha_mask_bits = NULL;
774 LONG bmi_width, bmi_height;
775 BITMAPINFO *bmi_copy;
776 BOOL do_stretch;
777 HDC hdc = 0;
778 WORD bpp;
779 BOOL ret = FALSE;
781 memset( frame, 0, sizeof(*frame) );
783 /* Check bitmap header */
785 if (bmi->bmiHeader.biSize == PNG_SIGN)
787 BITMAPINFO *bmi_png;
789 if (!(bmi_png = load_png( (const char *)bmi, &maxsize ))) return FALSE;
790 ret = create_icon_frame( bmi_png, maxsize, hotspot, is_icon, width, height, flags, frame );
791 HeapFree( GetProcessHeap(), 0, bmi_png );
792 return ret;
795 if (maxsize < sizeof(BITMAPCOREHEADER))
797 WARN( "invalid size %lu\n", maxsize );
798 return FALSE;
800 if (maxsize < bmi->bmiHeader.biSize)
802 WARN( "invalid header size %lu\n", bmi->bmiHeader.biSize );
803 return FALSE;
805 if ( (bmi->bmiHeader.biSize != sizeof(BITMAPCOREHEADER)) &&
806 (bmi->bmiHeader.biSize != sizeof(BITMAPINFOHEADER) ||
807 (bmi->bmiHeader.biCompression != BI_RGB &&
808 bmi->bmiHeader.biCompression != BI_BITFIELDS)) )
810 WARN( "invalid bitmap header %lu\n", bmi->bmiHeader.biSize );
811 return FALSE;
814 size = bitmap_info_size( bmi, DIB_RGB_COLORS );
815 DIB_GetBitmapInfo(&bmi->bmiHeader, &bmi_width, &bmi_height, &bpp, &compr);
816 color_size = get_dib_image_size( bmi_width, bmi_height / 2,
817 bpp );
818 mask_size = get_dib_image_size( bmi_width, bmi_height / 2, 1 );
819 if (size > maxsize || color_size > maxsize - size)
821 WARN( "truncated file %lu < %lu+%lu+%lu\n", maxsize, size, color_size, mask_size );
822 return 0;
824 if (mask_size > maxsize - size - color_size) mask_size = 0; /* no mask */
826 if (flags & LR_DEFAULTSIZE)
828 if (!width) width = GetSystemMetrics( is_icon ? SM_CXICON : SM_CXCURSOR );
829 if (!height) height = GetSystemMetrics( is_icon ? SM_CYICON : SM_CYCURSOR );
831 else
833 if (!width) width = bmi_width;
834 if (!height) height = bmi_height/2;
836 do_stretch = (bmi_height/2 != height) || (bmi_width != width);
838 /* Scale the hotspot */
839 if (is_icon)
841 hotspot.x = width / 2;
842 hotspot.y = height / 2;
844 else if (do_stretch)
846 hotspot.x = (hotspot.x * width) / bmi_width;
847 hotspot.y = (hotspot.y * height) / (bmi_height / 2);
850 if (!(bmi_copy = HeapAlloc( GetProcessHeap(), 0, max( size, FIELD_OFFSET( BITMAPINFO, bmiColors[2] )))))
851 return 0;
852 if (!(hdc = CreateCompatibleDC( 0 ))) goto done;
854 memcpy( bmi_copy, bmi, size );
855 if (bmi_copy->bmiHeader.biSize != sizeof(BITMAPCOREHEADER))
856 bmi_copy->bmiHeader.biHeight /= 2;
857 else
858 ((BITMAPCOREINFO *)bmi_copy)->bmciHeader.bcHeight /= 2;
859 bmi_height /= 2;
861 color_bits = (const char*)bmi + size;
862 mask_bits = (const char*)color_bits + color_size;
864 if (is_dib_monochrome( bmi ))
866 if (!(frame->mask = CreateBitmap( width, height * 2, 1, 1, NULL ))) goto done;
868 /* copy color data into second half of mask bitmap */
869 SelectObject( hdc, frame->mask );
870 StretchDIBits( hdc, 0, height, width, height,
871 0, 0, bmi_width, bmi_height,
872 color_bits, bmi_copy, DIB_RGB_COLORS, SRCCOPY );
874 else
876 if (!(frame->mask = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
877 if (!(frame->color = create_color_bitmap( width, height ))) goto done;
878 SelectObject( hdc, frame->color );
879 StretchDIBits( hdc, 0, 0, width, height,
880 0, 0, bmi_width, bmi_height,
881 color_bits, bmi_copy, DIB_RGB_COLORS, SRCCOPY );
883 if (bmi_has_alpha( bmi_copy, color_bits ))
885 frame->alpha = create_alpha_bitmap( frame->color, bmi_copy, color_bits );
886 if (!mask_size) /* generate mask from alpha */
888 LONG x, y, dst_stride = ((bmi_width + 31) / 8) & ~3;
890 if ((alpha_mask_bits = heap_calloc( bmi_height, dst_stride )))
892 static const unsigned char masks[] = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 };
893 const DWORD *src = color_bits;
894 unsigned char *dst = alpha_mask_bits;
896 for (y = 0; y < bmi_height; y++, src += bmi_width, dst += dst_stride)
897 for (x = 0; x < bmi_width; x++)
898 if (src[x] >> 24 != 0xff) dst[x >> 3] |= masks[x & 7];
900 mask_bits = alpha_mask_bits;
901 mask_size = bmi_height * dst_stride;
906 /* convert info to monochrome to copy the mask */
907 if (bmi_copy->bmiHeader.biSize != sizeof(BITMAPCOREHEADER))
909 RGBQUAD *rgb = bmi_copy->bmiColors;
911 bmi_copy->bmiHeader.biBitCount = 1;
912 bmi_copy->bmiHeader.biClrUsed = bmi_copy->bmiHeader.biClrImportant = 2;
913 rgb[0].rgbBlue = rgb[0].rgbGreen = rgb[0].rgbRed = 0x00;
914 rgb[1].rgbBlue = rgb[1].rgbGreen = rgb[1].rgbRed = 0xff;
915 rgb[0].rgbReserved = rgb[1].rgbReserved = 0;
917 else
919 RGBTRIPLE *rgb = (RGBTRIPLE *)(((BITMAPCOREHEADER *)bmi_copy) + 1);
921 ((BITMAPCOREINFO *)bmi_copy)->bmciHeader.bcBitCount = 1;
922 rgb[0].rgbtBlue = rgb[0].rgbtGreen = rgb[0].rgbtRed = 0x00;
923 rgb[1].rgbtBlue = rgb[1].rgbtGreen = rgb[1].rgbtRed = 0xff;
927 if (mask_size)
929 SelectObject( hdc, frame->mask );
930 StretchDIBits( hdc, 0, 0, width, height,
931 0, 0, bmi_width, bmi_height,
932 mask_bits, bmi_copy, DIB_RGB_COLORS, SRCCOPY );
935 frame->width = width;
936 frame->height = height;
937 frame->hotspot = hotspot;
938 ret = TRUE;
940 done:
941 if (!ret) free_icon_frame( frame );
942 DeleteDC( hdc );
943 HeapFree( GetProcessHeap(), 0, bmi_copy );
944 HeapFree( GetProcessHeap(), 0, alpha_mask_bits );
945 return ret;
948 static HICON create_cursoricon_object( struct cursoricon_desc *desc, BOOL is_icon, HINSTANCE module,
949 const WCHAR *resname, HRSRC rsrc )
951 WCHAR buf[MAX_PATH];
952 UNICODE_STRING module_name = { 0, sizeof(buf), buf };
953 UNICODE_STRING res_str = { 0 };
954 HICON handle;
956 if (!(handle = NtUserCreateCursorIcon( is_icon ))) return 0;
958 if (module) LdrGetDllFullName( module, &module_name );
960 res_str.Buffer = (WCHAR *)resname;
961 if (!IS_INTRESOURCE(resname))
963 res_str.Length = lstrlenW( resname ) * sizeof(WCHAR);
964 res_str.MaximumLength = res_str.Length + sizeof(WCHAR);
966 desc->rsrc = rsrc; /* FIXME: we should probably avoid storing rsrc */
968 if (!NtUserSetCursorIconData( handle, &module_name, &res_str, desc ))
970 NtUserDestroyCursor( handle, 0 );
971 return 0;
974 return handle;
977 /***********************************************************************
978 * create_icon_from_bmi
980 * Create an icon from its BITMAPINFO.
982 static HICON create_icon_from_bmi( const BITMAPINFO *bmi, DWORD maxsize, HMODULE module, LPCWSTR resname,
983 HRSRC rsrc, POINT hotspot, BOOL bIcon, INT width, INT height,
984 UINT flags )
986 struct cursoricon_frame frame;
987 struct cursoricon_desc desc =
989 .flags = flags,
990 .frames = &frame,
992 HICON ret;
994 if (!create_icon_frame( bmi, maxsize, hotspot, bIcon, width, height, flags, &frame )) return 0;
996 ret = create_cursoricon_object( &desc, bIcon, module, resname, rsrc );
997 if (!ret) free_icon_frame( &frame );
998 return ret;
1002 /**********************************************************************
1003 * .ANI cursor support
1005 #define ANI_RIFF_ID RIFF_FOURCC('R', 'I', 'F', 'F')
1006 #define ANI_LIST_ID RIFF_FOURCC('L', 'I', 'S', 'T')
1007 #define ANI_ACON_ID RIFF_FOURCC('A', 'C', 'O', 'N')
1008 #define ANI_anih_ID RIFF_FOURCC('a', 'n', 'i', 'h')
1009 #define ANI_seq__ID RIFF_FOURCC('s', 'e', 'q', ' ')
1010 #define ANI_fram_ID RIFF_FOURCC('f', 'r', 'a', 'm')
1011 #define ANI_rate_ID RIFF_FOURCC('r', 'a', 't', 'e')
1013 #define ANI_FLAG_ICON 0x1
1014 #define ANI_FLAG_SEQUENCE 0x2
1016 typedef struct {
1017 DWORD header_size;
1018 DWORD num_frames;
1019 DWORD num_steps;
1020 DWORD width;
1021 DWORD height;
1022 DWORD bpp;
1023 DWORD num_planes;
1024 DWORD display_rate;
1025 DWORD flags;
1026 } ani_header;
1028 typedef struct {
1029 DWORD data_size;
1030 const unsigned char *data;
1031 } riff_chunk_t;
1033 static void dump_ani_header( const ani_header *header )
1035 TRACE(" header size: %ld\n", header->header_size);
1036 TRACE(" frames: %ld\n", header->num_frames);
1037 TRACE(" steps: %ld\n", header->num_steps);
1038 TRACE(" width: %ld\n", header->width);
1039 TRACE(" height: %ld\n", header->height);
1040 TRACE(" bpp: %ld\n", header->bpp);
1041 TRACE(" planes: %ld\n", header->num_planes);
1042 TRACE(" display rate: %ld\n", header->display_rate);
1043 TRACE(" flags: 0x%08lx\n", header->flags);
1048 * RIFF:
1049 * DWORD "RIFF"
1050 * DWORD size
1051 * DWORD riff_id
1052 * BYTE[] data
1054 * LIST:
1055 * DWORD "LIST"
1056 * DWORD size
1057 * DWORD list_id
1058 * BYTE[] data
1060 * CHUNK:
1061 * DWORD chunk_id
1062 * DWORD size
1063 * BYTE[] data
1065 static void riff_find_chunk( DWORD chunk_id, DWORD chunk_type, const riff_chunk_t *parent_chunk, riff_chunk_t *chunk )
1067 const unsigned char *ptr = parent_chunk->data;
1068 const unsigned char *end = parent_chunk->data + (parent_chunk->data_size - (2 * sizeof(DWORD)));
1070 if (chunk_type == ANI_LIST_ID || chunk_type == ANI_RIFF_ID) end -= sizeof(DWORD);
1072 while (ptr < end)
1074 if ((!chunk_type && *(const DWORD *)ptr == chunk_id )
1075 || (chunk_type && *(const DWORD *)ptr == chunk_type && *((const DWORD *)ptr + 2) == chunk_id ))
1077 ptr += sizeof(DWORD);
1078 chunk->data_size = (*(const DWORD *)ptr + 1) & ~1;
1079 ptr += sizeof(DWORD);
1080 if (chunk_type == ANI_LIST_ID || chunk_type == ANI_RIFF_ID) ptr += sizeof(DWORD);
1081 chunk->data = ptr;
1083 return;
1086 ptr += sizeof(DWORD);
1087 if (ptr >= end)
1088 break;
1089 ptr += (*(const DWORD *)ptr + 1) & ~1;
1090 ptr += sizeof(DWORD);
1096 * .ANI layout:
1098 * RIFF:'ACON' RIFF chunk
1099 * |- CHUNK:'anih' Header
1100 * |- CHUNK:'seq ' Sequence information (optional)
1101 * \- LIST:'fram' Frame list
1102 * |- CHUNK:icon Cursor frames
1103 * |- CHUNK:icon
1104 * |- ...
1105 * \- CHUNK:icon
1107 static HCURSOR CURSORICON_CreateIconFromANI( const BYTE *bits, DWORD bits_size, INT width, INT height,
1108 INT depth, BOOL is_icon, UINT loadflags )
1110 struct cursoricon_desc desc = { 0 };
1111 ani_header header;
1112 HCURSOR cursor;
1113 UINT i;
1114 BOOL error = FALSE;
1116 riff_chunk_t root_chunk = { bits_size, bits };
1117 riff_chunk_t ACON_chunk = {0};
1118 riff_chunk_t anih_chunk = {0};
1119 riff_chunk_t fram_chunk = {0};
1120 riff_chunk_t rate_chunk = {0};
1121 riff_chunk_t seq_chunk = {0};
1122 const unsigned char *icon_chunk;
1123 const unsigned char *icon_data;
1125 TRACE("bits %p, bits_size %ld\n", bits, bits_size);
1127 riff_find_chunk( ANI_ACON_ID, ANI_RIFF_ID, &root_chunk, &ACON_chunk );
1128 if (!ACON_chunk.data)
1130 ERR("Failed to get root chunk.\n");
1131 return 0;
1134 riff_find_chunk( ANI_anih_ID, 0, &ACON_chunk, &anih_chunk );
1135 if (!anih_chunk.data)
1137 ERR("Failed to get 'anih' chunk.\n");
1138 return 0;
1140 memcpy( &header, anih_chunk.data, sizeof(header) );
1141 dump_ani_header( &header );
1143 if (!(header.flags & ANI_FLAG_ICON))
1145 FIXME("Raw animated icon/cursor data is not currently supported.\n");
1146 return 0;
1149 if (header.flags & ANI_FLAG_SEQUENCE)
1151 riff_find_chunk( ANI_seq__ID, 0, &ACON_chunk, &seq_chunk );
1152 if (seq_chunk.data)
1154 desc.frame_seq = (DWORD *)seq_chunk.data;
1156 else
1158 FIXME("Sequence data expected but not found, assuming steps == frames.\n");
1159 header.num_steps = header.num_frames;
1163 riff_find_chunk( ANI_rate_ID, 0, &ACON_chunk, &rate_chunk );
1164 if (rate_chunk.data)
1165 desc.frame_rates = (DWORD *)rate_chunk.data;
1167 riff_find_chunk( ANI_fram_ID, ANI_LIST_ID, &ACON_chunk, &fram_chunk );
1168 if (!fram_chunk.data)
1170 ERR("Failed to get icon list.\n");
1171 return 0;
1174 /* The .ANI stores the display rate in jiffies (1/60s) */
1175 desc.delay = header.display_rate;
1176 desc.num_frames = header.num_frames;
1177 desc.num_steps = header.num_steps;
1178 if (!(desc.frames = HeapAlloc( GetProcessHeap(), 0, sizeof(*desc.frames) * desc.num_frames )))
1179 return 0;
1181 icon_chunk = fram_chunk.data;
1182 icon_data = fram_chunk.data + (2 * sizeof(DWORD));
1183 for (i = 0; i < desc.num_frames; i++)
1185 const DWORD chunk_size = *(const DWORD *)(icon_chunk + sizeof(DWORD));
1186 const CURSORICONFILEDIRENTRY *entry;
1187 INT frameWidth, frameHeight;
1188 const BITMAPINFO *bmi;
1189 POINT hotspot;
1191 entry = CURSORICON_FindBestIconFile((const CURSORICONFILEDIR *) icon_data,
1192 bits + bits_size - icon_data,
1193 width, height, depth, loadflags );
1195 hotspot.x = entry->xHotspot;
1196 hotspot.y = entry->yHotspot;
1197 if (!header.width || !header.height)
1199 frameWidth = entry->bWidth;
1200 frameHeight = entry->bHeight;
1202 else
1204 frameWidth = header.width;
1205 frameHeight = header.height;
1208 if (!(error = entry->dwDIBOffset >= bits + bits_size - icon_data))
1210 bmi = (const BITMAPINFO *) (icon_data + entry->dwDIBOffset);
1211 /* Grab a frame from the animation */
1212 error = !create_icon_frame( bmi, bits + bits_size - (const BYTE *)bmi, hotspot,
1213 is_icon, frameWidth, frameHeight, loadflags, &desc.frames[i] );
1216 if (error)
1218 if (i == 0)
1220 FIXME_(cursor)("Completely failed to create animated cursor!\n");
1221 HeapFree( GetProcessHeap(), 0, desc.frames );
1222 return 0;
1224 /* There was an error but we at least decoded the first frame, so just use that frame */
1225 FIXME_(cursor)("Error creating animated cursor, only using first frame!\n");
1226 while (i > 1) free_icon_frame( &desc.frames[--i] );
1227 desc.num_frames = desc.num_steps = 1;
1228 desc.frame_seq = NULL;
1229 desc.delay = 0;
1230 break;
1233 /* Advance to the next chunk */
1234 icon_chunk += chunk_size + (2 * sizeof(DWORD));
1235 icon_data = icon_chunk + (2 * sizeof(DWORD));
1238 cursor = create_cursoricon_object( &desc, is_icon, 0, 0, 0 );
1239 if (!cursor) for (i = 0; i < desc.num_frames; i++) free_icon_frame( &desc.frames[i] );
1240 HeapFree( GetProcessHeap(), 0, desc.frames );
1241 return cursor;
1245 /**********************************************************************
1246 * CreateIconFromResourceEx (USER32.@)
1248 * FIXME: Convert to mono when cFlag is LR_MONOCHROME.
1250 HICON WINAPI CreateIconFromResourceEx( LPBYTE bits, UINT cbSize,
1251 BOOL bIcon, DWORD dwVersion,
1252 INT width, INT height,
1253 UINT cFlag )
1255 POINT hotspot;
1256 const BITMAPINFO *bmi;
1258 TRACE_(cursor)("%p (%u bytes), ver %08lx, %ix%i %s %s\n",
1259 bits, cbSize, dwVersion, width, height,
1260 bIcon ? "icon" : "cursor", (cFlag & LR_MONOCHROME) ? "mono" : "" );
1262 if (!bits) return 0;
1264 if (dwVersion == 0x00020000)
1266 FIXME_(cursor)("\t2.xx resources are not supported\n");
1267 return 0;
1270 /* Check if the resource is an animated icon/cursor */
1271 if (!memcmp(bits, "RIFF", 4))
1272 return CURSORICON_CreateIconFromANI( bits, cbSize, width, height,
1273 0 /* default depth */, bIcon, cFlag );
1275 if (bIcon)
1277 hotspot.x = width / 2;
1278 hotspot.y = height / 2;
1279 bmi = (BITMAPINFO *)bits;
1281 else /* get the hotspot */
1283 const SHORT *pt = (const SHORT *)bits;
1284 hotspot.x = pt[0];
1285 hotspot.y = pt[1];
1286 bmi = (const BITMAPINFO *)(pt + 2);
1287 cbSize -= 2 * sizeof(*pt);
1290 return create_icon_from_bmi( bmi, cbSize, NULL, NULL, NULL, hotspot, bIcon, width, height, cFlag );
1294 /**********************************************************************
1295 * CreateIconFromResource (USER32.@)
1297 HICON WINAPI CreateIconFromResource( LPBYTE bits, UINT cbSize,
1298 BOOL bIcon, DWORD dwVersion)
1300 return CreateIconFromResourceEx( bits, cbSize, bIcon, dwVersion, 0, 0, LR_DEFAULTSIZE | LR_SHARED );
1304 static HICON CURSORICON_LoadFromFile( LPCWSTR filename,
1305 INT width, INT height, INT depth,
1306 BOOL fCursor, UINT loadflags)
1308 const CURSORICONFILEDIRENTRY *entry;
1309 const CURSORICONFILEDIR *dir;
1310 DWORD filesize = 0;
1311 HICON hIcon = 0;
1312 const BYTE *bits;
1313 POINT hotspot;
1315 TRACE("loading %s\n", debugstr_w( filename ));
1317 bits = map_fileW( filename, &filesize );
1318 if (!bits)
1319 return hIcon;
1321 /* Check for .ani. */
1322 if (memcmp( bits, "RIFF", 4 ) == 0)
1324 hIcon = CURSORICON_CreateIconFromANI( bits, filesize, width, height, depth, !fCursor, loadflags );
1325 goto end;
1328 dir = (const CURSORICONFILEDIR*) bits;
1329 if ( filesize < FIELD_OFFSET( CURSORICONFILEDIR, idEntries[dir->idCount] ))
1330 goto end;
1332 if ( fCursor )
1333 entry = CURSORICON_FindBestCursorFile( dir, filesize, width, height, depth, loadflags );
1334 else
1335 entry = CURSORICON_FindBestIconFile( dir, filesize, width, height, depth, loadflags );
1337 if ( !entry )
1338 goto end;
1340 /* check that we don't run off the end of the file */
1341 if ( entry->dwDIBOffset > filesize )
1342 goto end;
1343 if ( entry->dwDIBOffset + entry->dwDIBSize > filesize )
1344 goto end;
1346 hotspot.x = entry->xHotspot;
1347 hotspot.y = entry->yHotspot;
1348 hIcon = create_icon_from_bmi( (const BITMAPINFO *)&bits[entry->dwDIBOffset], filesize - entry->dwDIBOffset,
1349 NULL, NULL, NULL, hotspot, !fCursor, width, height, loadflags );
1350 end:
1351 TRACE("loaded %s -> %p\n", debugstr_w( filename ), hIcon );
1352 UnmapViewOfFile( bits );
1353 return hIcon;
1356 /**********************************************************************
1357 * CURSORICON_Load
1359 * Load a cursor or icon from resource or file.
1361 static HICON CURSORICON_Load(HINSTANCE hInstance, LPCWSTR name,
1362 INT width, INT height, INT depth,
1363 BOOL fCursor, UINT loadflags)
1365 HANDLE handle = 0;
1366 HICON hIcon = 0;
1367 HRSRC hRsrc;
1368 DWORD size;
1369 const CURSORICONDIR *dir;
1370 const CURSORICONDIRENTRY *dirEntry;
1371 const BYTE *bits;
1372 WORD wResId;
1373 POINT hotspot;
1375 TRACE("%p, %s, %dx%d, depth %d, fCursor %d, flags 0x%04x\n",
1376 hInstance, debugstr_w(name), width, height, depth, fCursor, loadflags);
1378 if ( loadflags & LR_LOADFROMFILE ) /* Load from file */
1379 return CURSORICON_LoadFromFile( name, width, height, depth, fCursor, loadflags );
1381 if (!hInstance) hInstance = user32_module; /* Load OEM cursor/icon */
1383 /* don't cache 16-bit instances (FIXME: should never get 16-bit instances in the first place) */
1384 if ((ULONG_PTR)hInstance >> 16 == 0) loadflags &= ~LR_SHARED;
1386 /* Get directory resource ID */
1388 if (!(hRsrc = FindResourceW( hInstance, name,
1389 (LPWSTR)(fCursor ? RT_GROUP_CURSOR : RT_GROUP_ICON) )))
1391 /* try animated resource */
1392 if (!(hRsrc = FindResourceW( hInstance, name,
1393 (LPWSTR)(fCursor ? RT_ANICURSOR : RT_ANIICON) ))) return 0;
1394 if (!(handle = LoadResource( hInstance, hRsrc ))) return 0;
1395 bits = LockResource( handle );
1396 return CURSORICON_CreateIconFromANI( bits, SizeofResource( hInstance, handle ),
1397 width, height, depth, !fCursor, loadflags );
1400 /* Find the best entry in the directory */
1402 if (!(handle = LoadResource( hInstance, hRsrc ))) return 0;
1403 if (!(dir = LockResource( handle ))) return 0;
1404 size = SizeofResource( hInstance, hRsrc );
1405 if (fCursor)
1406 dirEntry = CURSORICON_FindBestCursorRes( dir, size, width, height, depth, loadflags );
1407 else
1408 dirEntry = CURSORICON_FindBestIconRes( dir, size, width, height, depth, loadflags );
1409 if (!dirEntry) return 0;
1410 wResId = dirEntry->wResId;
1411 FreeResource( handle );
1413 /* Load the resource */
1415 if (!(hRsrc = FindResourceW(hInstance,MAKEINTRESOURCEW(wResId),
1416 (LPWSTR)(fCursor ? RT_CURSOR : RT_ICON) ))) return 0;
1418 /* If shared icon, check whether it was already loaded */
1419 if (loadflags & LR_SHARED)
1421 WCHAR module_buf[MAX_PATH];
1422 UNICODE_STRING module_str, res_str;
1424 res_str.Length = 0;
1425 res_str.Buffer = MAKEINTRESOURCEW(wResId);
1426 module_str.Buffer = module_buf;
1427 module_str.MaximumLength = sizeof(module_buf);
1428 if (!LdrGetDllFullName( hInstance, &module_str ) &&
1429 (hIcon = NtUserFindExistingCursorIcon( &module_str, &res_str, hRsrc )))
1430 return hIcon;
1433 if (!(handle = LoadResource( hInstance, hRsrc ))) return 0;
1434 size = SizeofResource( hInstance, hRsrc );
1435 bits = LockResource( handle );
1437 if (!fCursor)
1439 hotspot.x = width / 2;
1440 hotspot.y = height / 2;
1442 else /* get the hotspot */
1444 const SHORT *pt = (const SHORT *)bits;
1445 hotspot.x = pt[0];
1446 hotspot.y = pt[1];
1447 bits += 2 * sizeof(SHORT);
1448 size -= 2 * sizeof(SHORT);
1450 hIcon = create_icon_from_bmi( (const BITMAPINFO *)bits, size, hInstance, name, hRsrc,
1451 hotspot, !fCursor, width, height, loadflags );
1452 FreeResource( handle );
1453 return hIcon;
1457 static HBITMAP create_masked_bitmap( int width, int height, const void *and, const void *xor )
1459 HBITMAP and_bitmap, xor_bitmap, bitmap;
1460 HDC src_dc, dst_dc;
1462 and_bitmap = CreateBitmap( width, height, 1, 1, and );
1463 xor_bitmap = CreateBitmap( width, height, 1, 1, xor );
1464 bitmap = CreateBitmap( width, height * 2, 1, 1, NULL );
1465 src_dc = CreateCompatibleDC( 0 );
1466 dst_dc = CreateCompatibleDC( 0 );
1468 SelectObject( dst_dc, bitmap );
1469 SelectObject( src_dc, and_bitmap );
1470 BitBlt( dst_dc, 0, 0, width, height, src_dc, 0, 0, SRCCOPY );
1471 SelectObject( src_dc, xor_bitmap );
1472 BitBlt( dst_dc, 0, height, width, height, src_dc, 0, 0, SRCCOPY );
1474 DeleteObject( and_bitmap );
1475 DeleteObject( xor_bitmap );
1476 DeleteDC( src_dc );
1477 DeleteDC( dst_dc );
1478 return bitmap;
1482 /***********************************************************************
1483 * CreateCursor (USER32.@)
1485 HCURSOR WINAPI CreateCursor( HINSTANCE instance, int hotspot_x, int hotspot_y,
1486 int width, int height, const void *and, const void *xor )
1488 ICONINFO info;
1489 HCURSOR cursor;
1491 TRACE( "hotspot (%d,%d), size %dx%d\n", hotspot_x, hotspot_y, width, height );
1493 info.fIcon = FALSE;
1494 info.xHotspot = hotspot_x;
1495 info.yHotspot = hotspot_y;
1496 info.hbmColor = NULL;
1497 info.hbmMask = create_masked_bitmap( width, height, and, xor );
1498 cursor = CreateIconIndirect( &info );
1499 DeleteObject( info.hbmMask );
1500 return cursor;
1504 /***********************************************************************
1505 * CreateIcon (USER32.@)
1507 * Creates an icon based on the specified bitmaps. The bitmaps must be
1508 * provided in a device dependent format and will be resized to
1509 * (SM_CXICON,SM_CYICON) and depth converted to match the screen's color
1510 * depth. The provided bitmaps must be top-down bitmaps.
1511 * Although Windows does not support 15bpp(*) this API must support it
1512 * for Winelib applications.
1514 * (*) Windows does not support 15bpp but it supports the 555 RGB 16bpp
1515 * format!
1517 * RETURNS
1518 * Success: handle to an icon
1519 * Failure: NULL
1521 * FIXME: Do we need to resize the bitmaps?
1523 HICON WINAPI CreateIcon( HINSTANCE instance, int width, int height, BYTE planes,
1524 BYTE depth, const void *and, const void *xor )
1526 ICONINFO info;
1527 HICON icon;
1529 TRACE_(icon)( "%dx%d, planes %d, depth %d\n", width, height, planes, depth );
1531 info.fIcon = TRUE;
1532 info.xHotspot = width / 2;
1533 info.yHotspot = height / 2;
1534 if (depth == 1)
1536 info.hbmColor = NULL;
1537 info.hbmMask = create_masked_bitmap( width, height, and, xor );
1539 else
1541 info.hbmColor = CreateBitmap( width, height, planes, depth, xor );
1542 info.hbmMask = CreateBitmap( width, height, 1, 1, and );
1545 icon = CreateIconIndirect( &info );
1547 DeleteObject( info.hbmMask );
1548 DeleteObject( info.hbmColor );
1550 return icon;
1554 /***********************************************************************
1555 * CopyIcon (USER32.@)
1557 HICON WINAPI CopyIcon( HICON icon )
1559 ICONINFO info;
1560 HICON res;
1562 if (!GetIconInfo( icon, &info ))
1563 return NULL;
1565 res = CopyImage( icon, info.fIcon ? IMAGE_ICON : IMAGE_CURSOR, 0, 0, 0 );
1566 DeleteObject( info.hbmColor );
1567 DeleteObject( info.hbmMask );
1568 return res;
1572 /***********************************************************************
1573 * DestroyIcon (USER32.@)
1575 BOOL WINAPI DestroyIcon( HICON hIcon )
1577 return NtUserDestroyCursor( hIcon, 0 );
1581 /***********************************************************************
1582 * DestroyCursor (USER32.@)
1584 BOOL WINAPI DestroyCursor( HCURSOR hCursor )
1586 return DestroyIcon( hCursor );
1589 /***********************************************************************
1590 * DrawIcon (USER32.@)
1592 BOOL WINAPI DrawIcon( HDC hdc, INT x, INT y, HICON hIcon )
1594 return NtUserDrawIconEx( hdc, x, y, hIcon, 0, 0, 0, 0, DI_NORMAL | DI_COMPAT | DI_DEFAULTSIZE );
1597 /***********************************************************************
1598 * GetClipCursor (USER32.@)
1600 BOOL WINAPI DECLSPEC_HOTPATCH GetClipCursor( RECT *rect )
1602 return NtUserGetClipCursor( rect );
1606 /***********************************************************************
1607 * SetSystemCursor (USER32.@)
1609 BOOL WINAPI SetSystemCursor(HCURSOR hcur, DWORD id)
1611 FIXME("(%p,%08lx),stub!\n", hcur, id);
1612 return TRUE;
1616 /**********************************************************************
1617 * LookupIconIdFromDirectoryEx (USER32.@)
1619 INT WINAPI LookupIconIdFromDirectoryEx( LPBYTE xdir, BOOL bIcon,
1620 INT width, INT height, UINT cFlag )
1622 const CURSORICONDIR *dir = (const CURSORICONDIR*)xdir;
1623 UINT retVal = 0;
1624 if( dir && !dir->idReserved && (dir->idType & 3) )
1626 const CURSORICONDIRENTRY* entry;
1627 int depth = (cFlag & LR_MONOCHROME) ? 1 : get_display_bpp();
1629 if( bIcon )
1630 entry = CURSORICON_FindBestIconRes( dir, ~0u, width, height, depth, LR_DEFAULTSIZE );
1631 else
1632 entry = CURSORICON_FindBestCursorRes( dir, ~0u, width, height, depth, LR_DEFAULTSIZE );
1634 if( entry ) retVal = entry->wResId;
1636 else WARN_(cursor)("invalid resource directory\n");
1637 return retVal;
1640 /**********************************************************************
1641 * LookupIconIdFromDirectory (USER32.@)
1643 INT WINAPI LookupIconIdFromDirectory( LPBYTE dir, BOOL bIcon )
1645 return LookupIconIdFromDirectoryEx( dir, bIcon, 0, 0, bIcon ? 0 : LR_MONOCHROME );
1648 /***********************************************************************
1649 * LoadCursorW (USER32.@)
1651 HCURSOR WINAPI LoadCursorW(HINSTANCE hInstance, LPCWSTR name)
1653 TRACE("%p, %s\n", hInstance, debugstr_w(name));
1655 return LoadImageW( hInstance, name, IMAGE_CURSOR, 0, 0,
1656 LR_SHARED | LR_DEFAULTSIZE );
1659 /***********************************************************************
1660 * LoadCursorA (USER32.@)
1662 HCURSOR WINAPI LoadCursorA(HINSTANCE hInstance, LPCSTR name)
1664 TRACE("%p, %s\n", hInstance, debugstr_a(name));
1666 return LoadImageA( hInstance, name, IMAGE_CURSOR, 0, 0,
1667 LR_SHARED | LR_DEFAULTSIZE );
1670 /***********************************************************************
1671 * LoadCursorFromFileW (USER32.@)
1673 HCURSOR WINAPI LoadCursorFromFileW (LPCWSTR name)
1675 TRACE("%s\n", debugstr_w(name));
1677 return LoadImageW( 0, name, IMAGE_CURSOR, 0, 0,
1678 LR_LOADFROMFILE | LR_DEFAULTSIZE );
1681 /***********************************************************************
1682 * LoadCursorFromFileA (USER32.@)
1684 HCURSOR WINAPI LoadCursorFromFileA (LPCSTR name)
1686 TRACE("%s\n", debugstr_a(name));
1688 return LoadImageA( 0, name, IMAGE_CURSOR, 0, 0,
1689 LR_LOADFROMFILE | LR_DEFAULTSIZE );
1692 /***********************************************************************
1693 * LoadIconW (USER32.@)
1695 HICON WINAPI LoadIconW(HINSTANCE hInstance, LPCWSTR name)
1697 TRACE("%p, %s\n", hInstance, debugstr_w(name));
1699 return LoadImageW( hInstance, name, IMAGE_ICON, 0, 0,
1700 LR_SHARED | LR_DEFAULTSIZE );
1703 /***********************************************************************
1704 * LoadIconA (USER32.@)
1706 HICON WINAPI LoadIconA(HINSTANCE hInstance, LPCSTR name)
1708 TRACE("%p, %s\n", hInstance, debugstr_a(name));
1710 return LoadImageA( hInstance, name, IMAGE_ICON, 0, 0,
1711 LR_SHARED | LR_DEFAULTSIZE );
1714 /**********************************************************************
1715 * GetCursorFrameInfo (USER32.@)
1717 * NOTES
1718 * So far no use has been found for the second parameter, it is currently presumed
1719 * that this parameter is reserved for future use.
1721 * PARAMS
1722 * hCursor [I] Handle to cursor for which to retrieve information
1723 * reserved [I] No purpose has been found for this parameter (may be NULL)
1724 * istep [I] The step of the cursor for which to retrieve information
1725 * rate_jiffies [O] Pointer to DWORD that receives the frame-specific delay (cannot be NULL)
1726 * num_steps [O] Pointer to DWORD that receives the number of steps in the cursor (cannot be NULL)
1728 * RETURNS
1729 * Success: Handle to a frame of the cursor (specified by istep)
1730 * Failure: NULL cursor (0)
1732 HCURSOR WINAPI GetCursorFrameInfo( HCURSOR handle, DWORD reserved, DWORD istep,
1733 DWORD *rate_jiffies, DWORD *num_steps )
1735 return NtUserGetCursorFrameInfo( handle, istep, rate_jiffies, num_steps );
1738 /**********************************************************************
1739 * GetIconInfo (USER32.@)
1741 BOOL WINAPI GetIconInfo( HICON icon, ICONINFO *info )
1743 return NtUserGetIconInfo( icon, info, NULL, NULL, NULL, 0 );
1746 /**********************************************************************
1747 * GetIconInfoExA (USER32.@)
1749 BOOL WINAPI GetIconInfoExA( HICON icon, ICONINFOEXA *info )
1751 ICONINFOEXW infoW;
1753 if (info->cbSize != sizeof(*info))
1755 SetLastError( ERROR_INVALID_PARAMETER );
1756 return FALSE;
1758 infoW.cbSize = sizeof(infoW);
1759 if (!GetIconInfoExW( icon, &infoW )) return FALSE;
1760 info->fIcon = infoW.fIcon;
1761 info->xHotspot = infoW.xHotspot;
1762 info->yHotspot = infoW.yHotspot;
1763 info->hbmColor = infoW.hbmColor;
1764 info->hbmMask = infoW.hbmMask;
1765 info->wResID = infoW.wResID;
1766 WideCharToMultiByte( CP_ACP, 0, infoW.szModName, -1, info->szModName, MAX_PATH, NULL, NULL );
1767 WideCharToMultiByte( CP_ACP, 0, infoW.szResName, -1, info->szResName, MAX_PATH, NULL, NULL );
1768 return TRUE;
1771 /**********************************************************************
1772 * GetIconInfoExW (USER32.@)
1774 BOOL WINAPI GetIconInfoExW( HICON handle, ICONINFOEXW *ret )
1776 UNICODE_STRING module, res_name;
1777 ICONINFO info;
1779 if (ret->cbSize != sizeof(*ret))
1781 SetLastError( ERROR_INVALID_PARAMETER );
1782 return FALSE;
1785 module.Buffer = ret->szModName;
1786 module.MaximumLength = sizeof(ret->szModName) - sizeof(WCHAR);
1787 res_name.Buffer = ret->szResName;
1788 res_name.MaximumLength = sizeof(ret->szResName) - sizeof(WCHAR);
1789 if (!NtUserGetIconInfo( handle, &info, &module, &res_name, NULL, 0 )) return FALSE;
1790 ret->fIcon = info.fIcon;
1791 ret->xHotspot = info.xHotspot;
1792 ret->yHotspot = info.yHotspot;
1793 ret->hbmColor = info.hbmColor;
1794 ret->hbmMask = info.hbmMask;
1795 ret->wResID = res_name.Length ? 0 : LOWORD( res_name.Buffer );
1796 ret->szModName[module.Length] = 0;
1797 ret->szResName[res_name.Length] = 0;
1798 return TRUE;
1801 /* copy an icon bitmap, even when it can't be selected into a DC */
1802 /* helper for CreateIconIndirect */
1803 static void stretch_blt_icon( HDC hdc_dst, int dst_x, int dst_y, int dst_width, int dst_height,
1804 HBITMAP src, int width, int height )
1806 HDC hdc = CreateCompatibleDC( 0 );
1808 if (!SelectObject( hdc, src )) /* do it the hard way */
1810 BITMAPINFO *info;
1811 void *bits;
1813 if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) return;
1814 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1815 info->bmiHeader.biWidth = width;
1816 info->bmiHeader.biHeight = height;
1817 info->bmiHeader.biPlanes = GetDeviceCaps( hdc_dst, PLANES );
1818 info->bmiHeader.biBitCount = GetDeviceCaps( hdc_dst, BITSPIXEL );
1819 info->bmiHeader.biCompression = BI_RGB;
1820 info->bmiHeader.biSizeImage = get_dib_image_size( width, height, info->bmiHeader.biBitCount );
1821 info->bmiHeader.biXPelsPerMeter = 0;
1822 info->bmiHeader.biYPelsPerMeter = 0;
1823 info->bmiHeader.biClrUsed = 0;
1824 info->bmiHeader.biClrImportant = 0;
1825 bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage );
1826 if (bits && GetDIBits( hdc, src, 0, height, bits, info, DIB_RGB_COLORS ))
1827 StretchDIBits( hdc_dst, dst_x, dst_y, dst_width, dst_height,
1828 0, 0, width, height, bits, info, DIB_RGB_COLORS, SRCCOPY );
1830 HeapFree( GetProcessHeap(), 0, bits );
1831 HeapFree( GetProcessHeap(), 0, info );
1833 else StretchBlt( hdc_dst, dst_x, dst_y, dst_width, dst_height, hdc, 0, 0, width, height, SRCCOPY );
1835 DeleteDC( hdc );
1838 /**********************************************************************
1839 * CreateIconIndirect (USER32.@)
1841 HICON WINAPI CreateIconIndirect(PICONINFO iconinfo)
1843 struct cursoricon_frame frame = { 0 };
1844 struct cursoricon_desc desc = { .frames = &frame };
1845 BITMAP bmpXor, bmpAnd;
1846 HICON ret;
1847 HDC hdc;
1849 TRACE("color %p, mask %p, hotspot %lux%lu, fIcon %d\n",
1850 iconinfo->hbmColor, iconinfo->hbmMask,
1851 iconinfo->xHotspot, iconinfo->yHotspot, iconinfo->fIcon);
1853 if (!iconinfo->hbmMask) return 0;
1855 GetObjectW( iconinfo->hbmMask, sizeof(bmpAnd), &bmpAnd );
1856 TRACE("mask: width %d, height %d, width bytes %d, planes %u, bpp %u\n",
1857 bmpAnd.bmWidth, bmpAnd.bmHeight, bmpAnd.bmWidthBytes,
1858 bmpAnd.bmPlanes, bmpAnd.bmBitsPixel);
1860 if (iconinfo->hbmColor)
1862 GetObjectW( iconinfo->hbmColor, sizeof(bmpXor), &bmpXor );
1863 TRACE("color: width %d, height %d, width bytes %d, planes %u, bpp %u\n",
1864 bmpXor.bmWidth, bmpXor.bmHeight, bmpXor.bmWidthBytes,
1865 bmpXor.bmPlanes, bmpXor.bmBitsPixel);
1867 frame.width = bmpXor.bmWidth;
1868 frame.height = bmpXor.bmHeight;
1869 frame.color = create_color_bitmap( frame.width, frame.height );
1871 else
1873 frame.width = bmpAnd.bmWidth;
1874 frame.height = bmpAnd.bmHeight;
1876 frame.mask = CreateBitmap( frame.width, frame.height, 1, 1, NULL );
1878 hdc = CreateCompatibleDC( 0 );
1879 SelectObject( hdc, frame.mask );
1880 stretch_blt_icon( hdc, 0, 0, frame.width, frame.height, iconinfo->hbmMask,
1881 bmpAnd.bmWidth, bmpAnd.bmHeight );
1883 if (frame.color)
1885 SelectObject( hdc, frame.color );
1886 stretch_blt_icon( hdc, 0, 0, frame.width, frame.height, iconinfo->hbmColor,
1887 frame.width, frame.height );
1889 else frame.height /= 2;
1891 DeleteDC( hdc );
1893 frame.alpha = create_alpha_bitmap( iconinfo->hbmColor, NULL, NULL );
1895 if (iconinfo->fIcon)
1897 frame.hotspot.x = frame.width / 2;
1898 frame.hotspot.y = frame.height / 2;
1900 else
1902 frame.hotspot.x = iconinfo->xHotspot;
1903 frame.hotspot.y = iconinfo->yHotspot;
1906 ret = create_cursoricon_object( &desc, iconinfo->fIcon, 0, 0, 0 );
1907 if (!ret) free_icon_frame( &frame );
1908 return ret;
1911 /***********************************************************************
1912 * DIB_FixColorsToLoadflags
1914 * Change color table entries when LR_LOADTRANSPARENT or LR_LOADMAP3DCOLORS
1915 * are in loadflags
1917 static void DIB_FixColorsToLoadflags(BITMAPINFO * bmi, UINT loadflags, BYTE pix)
1919 int colors;
1920 COLORREF c_W, c_S, c_F, c_L, c_C;
1921 int incr,i;
1922 RGBQUAD *ptr;
1923 int bitmap_type;
1924 LONG width;
1925 LONG height;
1926 WORD bpp;
1927 DWORD compr;
1929 if (((bitmap_type = DIB_GetBitmapInfo((BITMAPINFOHEADER*) bmi, &width, &height, &bpp, &compr)) == -1))
1931 WARN_(resource)("Invalid bitmap\n");
1932 return;
1935 if (bpp > 8) return;
1937 if (bitmap_type == 0) /* BITMAPCOREHEADER */
1939 incr = 3;
1940 colors = 1 << bpp;
1942 else
1944 incr = 4;
1945 colors = bmi->bmiHeader.biClrUsed;
1946 if (colors > 256) colors = 256;
1947 if (!colors && (bpp <= 8)) colors = 1 << bpp;
1950 c_W = GetSysColor(COLOR_WINDOW);
1951 c_S = GetSysColor(COLOR_3DSHADOW);
1952 c_F = GetSysColor(COLOR_3DFACE);
1953 c_L = GetSysColor(COLOR_3DLIGHT);
1955 if (loadflags & LR_LOADTRANSPARENT) {
1956 switch (bpp) {
1957 case 1: pix = pix >> 7; break;
1958 case 4: pix = pix >> 4; break;
1959 case 8: break;
1960 default:
1961 WARN_(resource)("(%d): Unsupported depth\n", bpp);
1962 return;
1964 if (pix >= colors) {
1965 WARN_(resource)("pixel has color index greater than biClrUsed!\n");
1966 return;
1968 if (loadflags & LR_LOADMAP3DCOLORS) c_W = c_F;
1969 ptr = (RGBQUAD*)((char*)bmi->bmiColors+pix*incr);
1970 ptr->rgbBlue = GetBValue(c_W);
1971 ptr->rgbGreen = GetGValue(c_W);
1972 ptr->rgbRed = GetRValue(c_W);
1974 if (loadflags & LR_LOADMAP3DCOLORS)
1975 for (i=0; i<colors; i++) {
1976 ptr = (RGBQUAD*)((char*)bmi->bmiColors+i*incr);
1977 c_C = RGB(ptr->rgbRed, ptr->rgbGreen, ptr->rgbBlue);
1978 if (c_C == RGB(128, 128, 128)) {
1979 ptr->rgbRed = GetRValue(c_S);
1980 ptr->rgbGreen = GetGValue(c_S);
1981 ptr->rgbBlue = GetBValue(c_S);
1982 } else if (c_C == RGB(192, 192, 192)) {
1983 ptr->rgbRed = GetRValue(c_F);
1984 ptr->rgbGreen = GetGValue(c_F);
1985 ptr->rgbBlue = GetBValue(c_F);
1986 } else if (c_C == RGB(223, 223, 223)) {
1987 ptr->rgbRed = GetRValue(c_L);
1988 ptr->rgbGreen = GetGValue(c_L);
1989 ptr->rgbBlue = GetBValue(c_L);
1995 /**********************************************************************
1996 * BITMAP_Load
1998 static HBITMAP BITMAP_Load( HINSTANCE instance, LPCWSTR name,
1999 INT desiredx, INT desiredy, UINT loadflags )
2001 HBITMAP hbitmap = 0, orig_bm;
2002 HRSRC hRsrc;
2003 HGLOBAL handle;
2004 const char *ptr = NULL;
2005 BITMAPINFO *info, *fix_info = NULL, *scaled_info = NULL;
2006 int size;
2007 BYTE pix;
2008 char *bits;
2009 LONG width, height, new_width, new_height;
2010 WORD bpp_dummy;
2011 DWORD compr_dummy, offbits = 0;
2012 INT bm_type;
2013 HDC screen_mem_dc = NULL;
2015 if (!(loadflags & LR_LOADFROMFILE))
2017 if (!instance)
2019 /* OEM bitmap: try to load the resource from user32.dll */
2020 instance = user32_module;
2023 if (!(hRsrc = FindResourceW( instance, name, (LPWSTR)RT_BITMAP ))) return 0;
2024 if (!(handle = LoadResource( instance, hRsrc ))) return 0;
2026 if ((info = LockResource( handle )) == NULL) return 0;
2028 else
2030 BITMAPFILEHEADER * bmfh;
2032 if (!(ptr = map_fileW( name, NULL ))) return 0;
2033 info = (BITMAPINFO *)(ptr + sizeof(BITMAPFILEHEADER));
2034 bmfh = (BITMAPFILEHEADER *)ptr;
2035 if (bmfh->bfType != 0x4d42 /* 'BM' */)
2037 WARN("Invalid/unsupported bitmap format!\n");
2038 goto end;
2040 if (bmfh->bfOffBits) offbits = bmfh->bfOffBits - sizeof(BITMAPFILEHEADER);
2043 bm_type = DIB_GetBitmapInfo( &info->bmiHeader, &width, &height,
2044 &bpp_dummy, &compr_dummy);
2045 if (bm_type == -1)
2047 WARN("Invalid bitmap format!\n");
2048 goto end;
2051 size = bitmap_info_size(info, DIB_RGB_COLORS);
2052 fix_info = HeapAlloc(GetProcessHeap(), 0, size);
2053 scaled_info = HeapAlloc(GetProcessHeap(), 0, size);
2055 if (!fix_info || !scaled_info) goto end;
2056 memcpy(fix_info, info, size);
2058 pix = *((LPBYTE)info + size);
2059 DIB_FixColorsToLoadflags(fix_info, loadflags, pix);
2061 memcpy(scaled_info, fix_info, size);
2063 if(desiredx != 0)
2064 new_width = desiredx;
2065 else
2066 new_width = width;
2068 if(desiredy != 0)
2069 new_height = height > 0 ? desiredy : -desiredy;
2070 else
2071 new_height = height;
2073 if(bm_type == 0)
2075 BITMAPCOREHEADER *core = (BITMAPCOREHEADER *)&scaled_info->bmiHeader;
2076 core->bcWidth = new_width;
2077 core->bcHeight = new_height;
2079 else
2081 /* Some sanity checks for BITMAPINFO (not applicable to BITMAPCOREINFO) */
2082 if (info->bmiHeader.biHeight > 65535 || info->bmiHeader.biWidth > 65535) {
2083 WARN("Broken BitmapInfoHeader!\n");
2084 goto end;
2087 scaled_info->bmiHeader.biWidth = new_width;
2088 scaled_info->bmiHeader.biHeight = new_height;
2091 if (new_height < 0) new_height = -new_height;
2093 if (!(screen_mem_dc = CreateCompatibleDC( 0 ))) goto end;
2095 bits = (char *)info + (offbits ? offbits : size);
2097 if (loadflags & LR_CREATEDIBSECTION)
2099 scaled_info->bmiHeader.biCompression = 0; /* DIBSection can't be compressed */
2100 hbitmap = CreateDIBSection(0, scaled_info, DIB_RGB_COLORS, NULL, 0, 0);
2102 else
2104 if (is_dib_monochrome(fix_info))
2105 hbitmap = CreateBitmap(new_width, new_height, 1, 1, NULL);
2106 else
2107 hbitmap = create_color_bitmap(new_width, new_height);
2110 orig_bm = SelectObject(screen_mem_dc, hbitmap);
2111 if (info->bmiHeader.biBitCount > 1)
2112 SetStretchBltMode(screen_mem_dc, HALFTONE);
2113 StretchDIBits(screen_mem_dc, 0, 0, new_width, new_height, 0, 0, width, height, bits, fix_info, DIB_RGB_COLORS, SRCCOPY);
2114 SelectObject(screen_mem_dc, orig_bm);
2116 end:
2117 if (screen_mem_dc) DeleteDC(screen_mem_dc);
2118 HeapFree(GetProcessHeap(), 0, scaled_info);
2119 HeapFree(GetProcessHeap(), 0, fix_info);
2120 if (loadflags & LR_LOADFROMFILE) UnmapViewOfFile( ptr );
2122 return hbitmap;
2125 /**********************************************************************
2126 * LoadImageA (USER32.@)
2128 * See LoadImageW.
2130 HANDLE WINAPI LoadImageA( HINSTANCE hinst, LPCSTR name, UINT type,
2131 INT desiredx, INT desiredy, UINT loadflags)
2133 HANDLE res;
2134 LPWSTR u_name;
2136 if (IS_INTRESOURCE(name))
2137 return LoadImageW(hinst, (LPCWSTR)name, type, desiredx, desiredy, loadflags);
2139 __TRY {
2140 DWORD len = MultiByteToWideChar( CP_ACP, 0, name, -1, NULL, 0 );
2141 u_name = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
2142 MultiByteToWideChar( CP_ACP, 0, name, -1, u_name, len );
2144 __EXCEPT_PAGE_FAULT {
2145 SetLastError( ERROR_INVALID_PARAMETER );
2146 return 0;
2148 __ENDTRY
2149 res = LoadImageW(hinst, u_name, type, desiredx, desiredy, loadflags);
2150 HeapFree(GetProcessHeap(), 0, u_name);
2151 return res;
2155 /******************************************************************************
2156 * LoadImageW (USER32.@) Loads an icon, cursor, or bitmap
2158 * PARAMS
2159 * hinst [I] Handle of instance that contains image
2160 * name [I] Name of image
2161 * type [I] Type of image
2162 * desiredx [I] Desired width
2163 * desiredy [I] Desired height
2164 * loadflags [I] Load flags
2166 * RETURNS
2167 * Success: Handle to newly loaded image
2168 * Failure: NULL
2170 * FIXME: Implementation lacks some features, see LR_ defines in winuser.h
2172 HANDLE WINAPI LoadImageW( HINSTANCE hinst, LPCWSTR name, UINT type,
2173 INT desiredx, INT desiredy, UINT loadflags )
2175 int depth;
2176 WCHAR path[MAX_PATH];
2178 TRACE_(resource)("(%p,%s,%d,%d,%d,0x%08x)\n",
2179 hinst,debugstr_w(name),type,desiredx,desiredy,loadflags);
2181 if (loadflags & LR_LOADFROMFILE)
2183 loadflags &= ~LR_SHARED;
2184 /* relative paths are not only relative to the current working directory */
2185 if (SearchPathW(NULL, name, NULL, ARRAY_SIZE(path), path, NULL)) name = path;
2187 switch (type) {
2188 case IMAGE_BITMAP:
2189 return BITMAP_Load( hinst, name, desiredx, desiredy, loadflags );
2191 case IMAGE_ICON:
2192 case IMAGE_CURSOR:
2193 depth = 1;
2194 if (!(loadflags & LR_MONOCHROME)) depth = get_display_bpp();
2195 return CURSORICON_Load(hinst, name, desiredx, desiredy, depth, (type == IMAGE_CURSOR), loadflags);
2197 return 0;
2201 /* StretchBlt from src to dest; helper for CopyImage(). */
2202 static void stretch_bitmap( HBITMAP dst, HBITMAP src, int dst_width, int dst_height, int src_width, int src_height )
2204 HDC src_dc = CreateCompatibleDC( 0 ), dst_dc = CreateCompatibleDC( 0 );
2206 SelectObject( src_dc, src );
2207 SelectObject( dst_dc, dst );
2208 StretchBlt( dst_dc, 0, 0, dst_width, dst_height, src_dc, 0, 0, src_width, src_height, SRCCOPY );
2210 DeleteDC( src_dc );
2211 DeleteDC( dst_dc );
2215 /******************************************************************************
2216 * CopyImage (USER32.@) Creates new image and copies attributes to it
2218 * PARAMS
2219 * hnd [I] Handle to image to copy
2220 * type [I] Type of image to copy
2221 * desiredx [I] Desired width of new image
2222 * desiredy [I] Desired height of new image
2223 * flags [I] Copy flags
2225 * RETURNS
2226 * Success: Handle to newly created image
2227 * Failure: NULL
2229 * BUGS
2230 * Only Windows NT 4.0 supports the LR_COPYRETURNORG flag for bitmaps,
2231 * all other versions (95/2000/XP have been tested) ignore it.
2233 * NOTES
2234 * If LR_CREATEDIBSECTION is absent, the copy will be monochrome for
2235 * a monochrome source bitmap or if LR_MONOCHROME is present, otherwise
2236 * the copy will have the same depth as the screen.
2237 * The content of the image will only be copied if the bit depth of the
2238 * original image is compatible with the bit depth of the screen, or
2239 * if the source is a DIB section.
2240 * The LR_MONOCHROME flag is ignored if LR_CREATEDIBSECTION is present.
2242 HANDLE WINAPI CopyImage( HANDLE hnd, UINT type, INT desiredx,
2243 INT desiredy, UINT flags )
2245 TRACE("hnd=%p, type=%u, desiredx=%d, desiredy=%d, flags=%x\n",
2246 hnd, type, desiredx, desiredy, flags);
2248 switch (type)
2250 case IMAGE_BITMAP:
2252 HBITMAP res = NULL;
2253 DIBSECTION ds;
2254 int objSize;
2255 BITMAPINFO * bi;
2257 objSize = GetObjectW( hnd, sizeof(ds), &ds );
2258 if (!objSize) return 0;
2259 if ((desiredx < 0) || (desiredy < 0)) return 0;
2261 if (flags & LR_COPYFROMRESOURCE)
2263 FIXME("The flag LR_COPYFROMRESOURCE is not implemented for bitmaps\n");
2266 if (desiredx == 0) desiredx = ds.dsBm.bmWidth;
2267 if (desiredy == 0) desiredy = ds.dsBm.bmHeight;
2269 /* Allocate memory for a BITMAPINFOHEADER structure and a
2270 color table. The maximum number of colors in a color table
2271 is 256 which corresponds to a bitmap with depth 8.
2272 Bitmaps with higher depths don't have color tables. */
2273 bi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
2274 if (!bi) return 0;
2276 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
2277 bi->bmiHeader.biPlanes = ds.dsBm.bmPlanes;
2278 bi->bmiHeader.biBitCount = ds.dsBm.bmBitsPixel;
2279 bi->bmiHeader.biCompression = BI_RGB;
2281 if (flags & LR_CREATEDIBSECTION)
2283 /* Create a DIB section. LR_MONOCHROME is ignored */
2284 void * bits;
2285 HDC dc = CreateCompatibleDC(NULL);
2287 if (objSize == sizeof(DIBSECTION))
2289 /* The source bitmap is a DIB.
2290 Get its attributes to create an exact copy */
2291 memcpy(bi, &ds.dsBmih, sizeof(BITMAPINFOHEADER));
2294 bi->bmiHeader.biWidth = desiredx;
2295 bi->bmiHeader.biHeight = desiredy;
2297 /* Get the color table or the color masks */
2298 GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
2300 res = CreateDIBSection(dc, bi, DIB_RGB_COLORS, &bits, NULL, 0);
2301 DeleteDC(dc);
2303 else
2305 /* Create a device-dependent bitmap */
2307 BOOL monochrome = (flags & LR_MONOCHROME);
2309 if (objSize == sizeof(DIBSECTION))
2311 /* The source bitmap is a DIB section.
2312 Get its attributes */
2313 HDC dc = CreateCompatibleDC(NULL);
2314 bi->bmiHeader.biWidth = ds.dsBm.bmWidth;
2315 bi->bmiHeader.biHeight = ds.dsBm.bmHeight;
2316 GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
2317 DeleteDC(dc);
2319 if (!monochrome && ds.dsBm.bmBitsPixel == 1)
2321 /* Look if the colors of the DIB are black and white */
2323 monochrome =
2324 (bi->bmiColors[0].rgbRed == 0xff
2325 && bi->bmiColors[0].rgbGreen == 0xff
2326 && bi->bmiColors[0].rgbBlue == 0xff
2327 && bi->bmiColors[0].rgbReserved == 0
2328 && bi->bmiColors[1].rgbRed == 0
2329 && bi->bmiColors[1].rgbGreen == 0
2330 && bi->bmiColors[1].rgbBlue == 0
2331 && bi->bmiColors[1].rgbReserved == 0)
2333 (bi->bmiColors[0].rgbRed == 0
2334 && bi->bmiColors[0].rgbGreen == 0
2335 && bi->bmiColors[0].rgbBlue == 0
2336 && bi->bmiColors[0].rgbReserved == 0
2337 && bi->bmiColors[1].rgbRed == 0xff
2338 && bi->bmiColors[1].rgbGreen == 0xff
2339 && bi->bmiColors[1].rgbBlue == 0xff
2340 && bi->bmiColors[1].rgbReserved == 0);
2343 else if (!monochrome)
2345 monochrome = ds.dsBm.bmBitsPixel == 1;
2348 if (monochrome)
2349 res = CreateBitmap(desiredx, desiredy, 1, 1, NULL);
2350 else
2351 res = create_color_bitmap(desiredx, desiredy);
2354 if (res)
2356 /* Only copy the bitmap if it's a DIB section or if it's
2357 compatible to the screen */
2358 if (objSize == sizeof(DIBSECTION) ||
2359 ds.dsBm.bmBitsPixel == 1 ||
2360 ds.dsBm.bmBitsPixel == get_display_bpp())
2362 /* The source bitmap may already be selected in a device context,
2363 use GetDIBits/StretchDIBits and not StretchBlt */
2365 HDC dc;
2366 void * bits;
2368 dc = CreateCompatibleDC(NULL);
2369 if (ds.dsBm.bmBitsPixel > 1)
2370 SetStretchBltMode(dc, HALFTONE);
2372 bi->bmiHeader.biWidth = ds.dsBm.bmWidth;
2373 bi->bmiHeader.biHeight = ds.dsBm.bmHeight;
2374 bi->bmiHeader.biSizeImage = 0;
2375 bi->bmiHeader.biClrUsed = 0;
2376 bi->bmiHeader.biClrImportant = 0;
2378 /* Fill in biSizeImage */
2379 GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
2380 bits = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bi->bmiHeader.biSizeImage);
2382 if (bits)
2384 HBITMAP oldBmp;
2386 /* Get the image bits of the source bitmap */
2387 GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, bits, bi, DIB_RGB_COLORS);
2389 /* Copy it to the destination bitmap */
2390 oldBmp = SelectObject(dc, res);
2391 StretchDIBits(dc, 0, 0, desiredx, desiredy,
2392 0, 0, ds.dsBm.bmWidth, ds.dsBm.bmHeight,
2393 bits, bi, DIB_RGB_COLORS, SRCCOPY);
2394 SelectObject(dc, oldBmp);
2396 HeapFree(GetProcessHeap(), 0, bits);
2399 DeleteDC(dc);
2402 if (flags & LR_COPYDELETEORG)
2404 DeleteObject(hnd);
2407 HeapFree(GetProcessHeap(), 0, bi);
2408 return res;
2410 case IMAGE_ICON:
2411 case IMAGE_CURSOR:
2413 ICONINFOEXW icon_info;
2414 int depth = (flags & LR_MONOCHROME) ? 1 : get_display_bpp();
2415 HICON resource_icon = 0;
2416 LONG width, height;
2417 ICONINFO info;
2418 HANDLE module;
2419 HICON res;
2421 icon_info.cbSize = sizeof(icon_info);
2422 if (!GetIconInfoExW( hnd, &icon_info )) return 0;
2424 if (icon_info.szModName[0] && (flags & LR_COPYFROMRESOURCE) &&
2425 (module = GetModuleHandleW( icon_info.szModName )))
2427 const WCHAR *res = icon_info.szResName[0] ? icon_info.szResName
2428 : MAKEINTRESOURCEW( icon_info.wResID );
2429 resource_icon = CURSORICON_Load( module, res, desiredx, desiredy, depth,
2430 !icon_info.fIcon, flags );
2431 DeleteObject( icon_info.hbmColor );
2432 DeleteObject( icon_info.hbmMask );
2433 NtUserGetIconSize( resource_icon, 0, &width, &height );
2434 if (!GetIconInfoExW( resource_icon, &icon_info )) return 0;
2436 else NtUserGetIconSize( hnd, 0, &width, &height );
2437 height /= 2;
2439 if (flags & LR_DEFAULTSIZE)
2441 if (!desiredx) desiredx = GetSystemMetrics( type == IMAGE_ICON ? SM_CXICON : SM_CXCURSOR );
2442 if (!desiredy) desiredy = GetSystemMetrics( type == IMAGE_ICON ? SM_CYICON : SM_CYCURSOR );
2444 else
2446 if (!desiredx) desiredx = width;
2447 if (!desiredy) desiredy = height;
2450 info.fIcon = icon_info.fIcon;
2451 info.xHotspot = icon_info.xHotspot;
2452 info.yHotspot = icon_info.yHotspot;
2454 if (desiredx == width && desiredy == height)
2456 info.hbmColor = icon_info.hbmColor;
2457 info.hbmMask = icon_info.hbmMask;
2458 res = CreateIconIndirect( &info );
2460 else
2462 if (icon_info.hbmColor)
2464 if (!(info.hbmColor = create_color_bitmap( desiredx, desiredy )))
2466 DeleteObject( icon_info.hbmColor );
2467 DeleteObject( icon_info.hbmMask );
2468 if (resource_icon) DestroyIcon( resource_icon );
2469 return 0;
2471 stretch_bitmap( info.hbmColor, icon_info.hbmColor, desiredx, desiredy,
2472 width, height );
2474 if (!(info.hbmMask = CreateBitmap( desiredx, desiredy, 1, 1, NULL )))
2476 DeleteObject( icon_info.hbmColor );
2477 DeleteObject( icon_info.hbmMask );
2478 DeleteObject( info.hbmColor );
2479 if (resource_icon) DestroyIcon( resource_icon );
2480 return 0;
2482 stretch_bitmap( info.hbmMask, icon_info.hbmMask, desiredx, desiredy,
2483 width, height );
2485 else
2487 info.hbmColor = NULL;
2489 if (!(info.hbmMask = CreateBitmap( desiredx, desiredy * 2, 1, 1, NULL )))
2491 DeleteObject( icon_info.hbmColor );
2492 DeleteObject( icon_info.hbmMask );
2493 if (resource_icon) DestroyIcon( resource_icon );
2494 return 0;
2496 stretch_bitmap( info.hbmMask, icon_info.hbmMask, desiredx, desiredy * 2,
2497 width, height * 2 );
2500 res = CreateIconIndirect( &info );
2502 DeleteObject( info.hbmColor );
2503 DeleteObject( info.hbmMask );
2506 DeleteObject( icon_info.hbmColor );
2507 DeleteObject( icon_info.hbmMask );
2509 if (res && (flags & LR_COPYDELETEORG)) DestroyIcon( hnd );
2510 if (resource_icon) DestroyIcon( resource_icon );
2511 return res;
2514 return 0;
2518 /******************************************************************************
2519 * LoadBitmapW (USER32.@) Loads bitmap from the executable file
2521 * RETURNS
2522 * Success: Handle to specified bitmap
2523 * Failure: NULL
2525 HBITMAP WINAPI LoadBitmapW(
2526 HINSTANCE instance, /* [in] Handle to application instance */
2527 LPCWSTR name) /* [in] Address of bitmap resource name */
2529 return LoadImageW( instance, name, IMAGE_BITMAP, 0, 0, 0 );
2532 /**********************************************************************
2533 * LoadBitmapA (USER32.@)
2535 * See LoadBitmapW.
2537 HBITMAP WINAPI LoadBitmapA( HINSTANCE instance, LPCSTR name )
2539 return LoadImageA( instance, name, IMAGE_BITMAP, 0, 0, 0 );