1 /*
2  * PROJECT:         ReactOS user32.dll
3  * COPYRIGHT:       GPL - See COPYING in the top level directory
4  * FILE:            win32ss/user/user32/windows/cursoricon.c
5  * PURPOSE:         cursor and icons implementation
6  * PROGRAMMER:      Jérôme Gardou (jerome.gardou@reactos.org)
7  */
8 
9 #include <user32.h>
10 
11 WINE_DEFAULT_DEBUG_CHANNEL(cursor);
12 WINE_DECLARE_DEBUG_CHANNEL(icon);
13 //WINE_DECLARE_DEBUG_CHANNEL(resource);
14 
15 /* We only use Wide string functions */
16 #undef MAKEINTRESOURCE
17 #define MAKEINTRESOURCE MAKEINTRESOURCEW
18 
19 /************* USER32 INTERNAL FUNCTIONS **********/
20 
LoadSystemCursors(VOID)21 VOID LoadSystemCursors(VOID)
22 {
23    if (!gpsi->hIconSmWindows)
24    {
25        ERR("Loading System Cursors\n");
26        NtUserSetSystemCursor(LoadImageW( 0, IDC_ARROW,       IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_NORMAL);
27        NtUserSetSystemCursor(LoadImageW( 0, IDC_IBEAM,       IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_IBEAM);
28        NtUserSetSystemCursor(LoadImageW( 0, IDC_WAIT,        IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_WAIT);
29        NtUserSetSystemCursor(LoadImageW( 0, IDC_CROSS,       IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_CROSS);
30        NtUserSetSystemCursor(LoadImageW( 0, IDC_UPARROW,     IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_UP);
31        NtUserSetSystemCursor(LoadImageW( 0, IDC_ICON,        IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_ICON);
32        NtUserSetSystemCursor(LoadImageW( 0, IDC_SIZE,        IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_SIZE);
33        NtUserSetSystemCursor(LoadImageW( 0, IDC_SIZENWSE,    IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_SIZENWSE);
34        NtUserSetSystemCursor(LoadImageW( 0, IDC_SIZENESW,    IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_SIZENESW);
35        NtUserSetSystemCursor(LoadImageW( 0, IDC_SIZEWE,      IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_SIZEWE);
36        NtUserSetSystemCursor(LoadImageW( 0, IDC_SIZENS,      IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_SIZENS);
37        NtUserSetSystemCursor(LoadImageW( 0, IDC_SIZEALL,     IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_SIZEALL);
38        NtUserSetSystemCursor(LoadImageW( 0, IDC_NO,          IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_NO);
39        NtUserSetSystemCursor(LoadImageW( 0, IDC_HAND,        IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_HAND);
40        NtUserSetSystemCursor(LoadImageW( 0, IDC_APPSTARTING, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_APPSTARTING);
41        NtUserSetSystemCursor(LoadImageW( 0, IDC_HELP,        IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE ), OCR_HELP);
42    }
43 }
44 
45 /* This callback routine is called directly after switching to gui mode */
46 NTSTATUS
47 WINAPI
User32SetupDefaultCursors(PVOID Arguments,ULONG ArgumentLength)48 User32SetupDefaultCursors(PVOID Arguments,
49                           ULONG ArgumentLength)
50 {
51     BOOL *DefaultCursor = (BOOL*)Arguments;
52     HCURSOR hCursor;
53 
54     /* Load system cursors first */
55     LoadSystemCursors();
56 
57     if(*DefaultCursor)
58     {
59         /* set default cursor */
60         hCursor = LoadCursorW(0, IDC_ARROW);
61         SetCursor(hCursor);
62     }
63     else
64     {
65         /* FIXME load system cursor scheme */
66         SetCursor(0);
67         hCursor = LoadCursorW(0, IDC_ARROW);
68         SetCursor(hCursor);
69     }
70 
71     return(ZwCallbackReturn(&hCursor, sizeof(HCURSOR), STATUS_SUCCESS));
72 }
73 
get_icon_size(HICON hIcon,SIZE * size)74 BOOL get_icon_size(HICON hIcon, SIZE *size)
75 {
76     return NtUserGetIconSize(hIcon, 0, &size->cx, &size->cy);
77 }
78 
CursorIconToCursor(HICON hIcon,BOOL SemiTransparent)79 HCURSOR CursorIconToCursor(HICON hIcon, BOOL SemiTransparent)
80 {
81     UNIMPLEMENTED;
82     return NULL;
83 }
84 
85 /************* IMPLEMENTATION HELPERS ******************/
86 
87 static const WCHAR DISPLAYW[] = L"DISPLAY";
88 
map_fileW(LPCWSTR name,LPDWORD filesize)89 static void *map_fileW( LPCWSTR name, LPDWORD filesize )
90 {
91     HANDLE hFile, hMapping;
92     LPVOID ptr = NULL;
93 
94     hFile = CreateFileW( name, GENERIC_READ, FILE_SHARE_READ, NULL,
95                          OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0 );
96     if (hFile != INVALID_HANDLE_VALUE)
97     {
98         hMapping = CreateFileMappingW( hFile, NULL, PAGE_READONLY, 0, 0, NULL );
99         if (hMapping)
100         {
101             ptr = MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 );
102             CloseHandle( hMapping );
103             if (filesize)
104                 *filesize = GetFileSize( hFile, NULL );
105         }
106         CloseHandle( hFile );
107     }
108     return ptr;
109 }
110 
get_dib_image_size(int width,int height,int depth)111 static int get_dib_image_size( int width, int height, int depth )
112 {
113     return (((width * depth + 31) / 8) & ~3) * abs( height );
114 }
115 
is_dib_monochrome(const BITMAPINFO * info)116 static BOOL is_dib_monochrome( const BITMAPINFO* info )
117 {
118     if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
119     {
120         const RGBTRIPLE *rgb = ((const BITMAPCOREINFO*)info)->bmciColors;
121 
122         if (((const BITMAPCOREINFO*)info)->bmciHeader.bcBitCount != 1) return FALSE;
123 
124         /* Check if the first color is black */
125         if ((rgb->rgbtRed == 0) && (rgb->rgbtGreen == 0) && (rgb->rgbtBlue == 0))
126         {
127             rgb++;
128 
129             /* Check if the second color is white */
130             return ((rgb->rgbtRed == 0xff) && (rgb->rgbtGreen == 0xff)
131                  && (rgb->rgbtBlue == 0xff));
132         }
133         else return FALSE;
134     }
135     else  /* assume BITMAPINFOHEADER */
136     {
137         const RGBQUAD *rgb = info->bmiColors;
138 
139         if (info->bmiHeader.biBitCount != 1) return FALSE;
140 
141         /* Check if the first color is black */
142         if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) &&
143             (rgb->rgbBlue == 0) && (rgb->rgbReserved == 0))
144         {
145             rgb++;
146 
147             /* Check if the second color is white */
148             return ((rgb->rgbRed == 0xff) && (rgb->rgbGreen == 0xff)
149                  && (rgb->rgbBlue == 0xff) && (rgb->rgbReserved == 0));
150         }
151         else return FALSE;
152     }
153 }
154 
155 /* Return the size of the bitmap info structure including color table and
156  * the bytes required for 3 DWORDS if this is a BI_BITFIELDS bmp. */
bitmap_info_size(const BITMAPINFO * info,WORD coloruse)157 static int bitmap_info_size( const BITMAPINFO * info, WORD coloruse )
158 {
159     unsigned int colors, size, masks = 0;
160 
161     if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
162     {
163         const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *)info;
164         colors = (core->bcBitCount <= 8) ? 1 << core->bcBitCount : 0;
165         return sizeof(BITMAPCOREHEADER) + colors *
166              ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBTRIPLE) : sizeof(WORD));
167     }
168     else  /* assume BITMAPINFOHEADER */
169     {
170         colors = info->bmiHeader.biClrUsed;
171         if (colors > 256) /* buffer overflow otherwise */
172                 colors = 256;
173         if (!colors && (info->bmiHeader.biBitCount <= 8))
174             colors = 1 << info->bmiHeader.biBitCount;
175         /* Account for BI_BITFIELDS in BITMAPINFOHEADER(v1-v3) bmp's. The
176          * 'max' selection using biSize below will exclude v4 & v5's. */
177         if (info->bmiHeader.biCompression == BI_BITFIELDS) masks = 3;
178         size = max( info->bmiHeader.biSize, sizeof(BITMAPINFOHEADER) + masks * sizeof(DWORD) );
179         /* Test for BI_BITFIELDS format and either 16 or 32 bpp.
180          * If so, account for the 3 DWORD masks (RGB Order).
181          * BITMAPCOREHEADER tested above has no 16 or 32 bpp types.
182          * See table "All of the possible pixel formats in a DIB"
183          * at https://en.wikipedia.org/wiki/BMP_file_format. */
184         if (info->bmiHeader.biSize >= sizeof(BITMAPV4HEADER) &&
185             info->bmiHeader.biCompression == BI_BITFIELDS &&
186             (info->bmiHeader.biBitCount == 16 || info->bmiHeader.biBitCount == 32))
187         {
188             size += 3 * sizeof(DWORD);  // BI_BITFIELDS
189         }
190         return size + colors * ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBQUAD) : sizeof(WORD));
191     }
192 }
193 
DIB_GetBitmapInfo(const BITMAPINFOHEADER * header,LONG * width,LONG * height,WORD * bpp,DWORD * compr)194 static int DIB_GetBitmapInfo( const BITMAPINFOHEADER *header, LONG *width,
195                               LONG *height, WORD *bpp, DWORD *compr )
196 {
197     #define CR 13
198     #define LF 10
199     #define EOFM 26 // DOS End Of File Marker
200     #define HighBitDetect 0x89 // Byte with high bit set to test if not 7-bit
201     /* wine's definition */
202     static const BYTE png_sig_pattern[] = { HighBitDetect, 'P', 'N', 'G', CR, LF, EOFM, LF };
203     if (header->biSize == sizeof(BITMAPCOREHEADER))
204     {
205         const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *)header;
206         *width  = core->bcWidth;
207         *height = core->bcHeight;
208         *bpp    = core->bcBitCount;
209         *compr  = 0;
210         return 0;
211     }
212     else if (header->biSize == sizeof(BITMAPINFOHEADER) ||
213              header->biSize == sizeof(BITMAPV4HEADER) ||
214              header->biSize == sizeof(BITMAPV5HEADER))
215     {
216         *width  = header->biWidth;
217         *height = header->biHeight;
218         *bpp    = header->biBitCount;
219         *compr  = header->biCompression;
220         return 1;
221     }
222     if (memcmp(&header->biSize, png_sig_pattern, sizeof(png_sig_pattern)) == 0)
223     {
224         ERR("Cannot yet display PNG icons\n");
225         /* for PNG format details see https://en.wikipedia.org/wiki/PNG */
226     }
227     else
228     {
229         ERR("Unknown/wrong size for header of 0x%x\n", header->biSize );
230     }
231     return -1;
232 }
233 
234 /* copy an icon bitmap, even when it can't be selected into a DC */
235 /* helper for CreateIconIndirect */
stretch_blt_icon(HDC hdc_dst,int dst_width,int dst_height,HBITMAP src)236 static void stretch_blt_icon(HDC hdc_dst, int dst_width, int dst_height, HBITMAP src)
237 {
238     HDC hdc = CreateCompatibleDC( 0 );
239     BITMAP bm;
240     HBITMAP hbmpPrev;
241 
242     GetObjectW(src, sizeof(bm), &bm);
243 
244     hbmpPrev = SelectObject(hdc, src);
245 
246     if (!hbmpPrev)  /* do it the hard way */
247     {
248         BITMAPINFO *info;
249         void *bits;
250 
251         if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) return;
252         info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
253         info->bmiHeader.biWidth = bm.bmWidth;
254         info->bmiHeader.biHeight = bm.bmHeight;
255         info->bmiHeader.biPlanes = GetDeviceCaps( hdc_dst, PLANES );
256         info->bmiHeader.biBitCount = GetDeviceCaps( hdc_dst, BITSPIXEL );
257         info->bmiHeader.biCompression = BI_RGB;
258         info->bmiHeader.biSizeImage = get_dib_image_size( bm.bmWidth, bm.bmHeight, info->bmiHeader.biBitCount );
259         info->bmiHeader.biXPelsPerMeter = 0;
260         info->bmiHeader.biYPelsPerMeter = 0;
261         info->bmiHeader.biClrUsed = 0;
262         info->bmiHeader.biClrImportant = 0;
263         bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage );
264         if (bits && GetDIBits( hdc, src, 0, bm.bmHeight, bits, info, DIB_RGB_COLORS ))
265             StretchDIBits( hdc_dst, 0, 0, dst_width, dst_height,
266                            0, 0, bm.bmWidth, bm.bmHeight, bits, info, DIB_RGB_COLORS, SRCCOPY );
267 
268         HeapFree( GetProcessHeap(), 0, bits );
269         HeapFree( GetProcessHeap(), 0, info );
270     }
271     else
272     {
273         StretchBlt( hdc_dst, 0, 0, dst_width, dst_height, hdc, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY );
274         SelectObject(hdc, hbmpPrev);
275     }
276 
277     DeleteDC( hdc );
278 }
279 
280 /***********************************************************************
281  *          bmi_has_alpha
282  */
bmi_has_alpha(const BITMAPINFO * info,const void * bits)283 static BOOL bmi_has_alpha( const BITMAPINFO *info, const void *bits )
284 {
285     int i;
286     BOOL has_alpha = FALSE;
287     const unsigned char *ptr = bits;
288 
289     if (info->bmiHeader.biBitCount != 32) return FALSE;
290     for (i = 0; i < info->bmiHeader.biWidth * abs(info->bmiHeader.biHeight); i++, ptr += 4)
291         if ((has_alpha = (ptr[3] != 0))) break;
292     return has_alpha;
293 }
294 
295 /***********************************************************************
296  *          create_alpha_bitmap
297  *
298  * Create the alpha bitmap for a 32-bpp icon that has an alpha channel.
299  */
300 static
301 HBITMAP
create_alpha_bitmap(_In_opt_ HBITMAP color,_In_opt_ BITMAPINFO * src_info,_In_opt_ const void * color_bits,_In_ LONG width,_In_ LONG height)302 create_alpha_bitmap(
303     _In_opt_  HBITMAP color,
304     _In_opt_  BITMAPINFO *src_info,
305     _In_opt_  const void *color_bits,
306     _In_ LONG width,
307     _In_ LONG height)
308 {
309     HBITMAP alpha = NULL, hbmpOld;
310     HDC hdc = NULL, hdcScreen;
311     unsigned char *ptr;
312     void *bits = NULL;
313     ULONG size;
314 
315     hdcScreen = CreateDCW(DISPLAYW, NULL, NULL, NULL);
316     if (!hdcScreen)
317         return NULL;
318     hdc = CreateCompatibleDC(hdcScreen);
319     if (!hdc)
320     {
321         DeleteDC(hdcScreen);
322         return NULL;
323     }
324 
325     if (color)
326     {
327         BITMAP bm;
328         BITMAPINFO *info = NULL;
329 
330         TRACE("Creating alpha bitmap from existing bitmap.\n");
331 
332         if (!GetObjectW( color, sizeof(bm), &bm ))
333             goto done;
334         if (bm.bmBitsPixel != 32)
335             goto done;
336 
337         size = get_dib_image_size(bm.bmWidth, bm.bmHeight, 32);
338 
339         info = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(BITMAPINFO, bmiColors[256]));
340         if(!info)
341             goto done;
342         info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
343         info->bmiHeader.biWidth = bm.bmWidth;
344         info->bmiHeader.biHeight = -bm.bmHeight;
345         info->bmiHeader.biPlanes = 1;
346         info->bmiHeader.biBitCount = 32;
347         info->bmiHeader.biCompression = BI_RGB;
348         info->bmiHeader.biSizeImage = size;
349         info->bmiHeader.biXPelsPerMeter = 0;
350         info->bmiHeader.biYPelsPerMeter = 0;
351         info->bmiHeader.biClrUsed = 0;
352         info->bmiHeader.biClrImportant = 0;
353 
354         bits = HeapAlloc(GetProcessHeap(), 0, size);
355         if(!bits)
356         {
357             HeapFree(GetProcessHeap(), 0, info);
358             goto done;
359         }
360         if(!GetDIBits( hdc, color, 0, bm.bmHeight, bits, info, DIB_RGB_COLORS ))
361         {
362             HeapFree(GetProcessHeap(), 0, info);
363             goto done;
364         }
365         if (!bmi_has_alpha( info, bits ))
366         {
367             HeapFree(GetProcessHeap(), 0, info);
368             goto done;
369         }
370 
371         /* pre-multiply by alpha */
372         for (ptr = bits; ptr < ((BYTE*)bits + size); ptr += 4)
373         {
374             unsigned int alpha = ptr[3];
375             ptr[0] = (ptr[0] * alpha) / 255;
376             ptr[1] = (ptr[1] * alpha) / 255;
377             ptr[2] = (ptr[2] * alpha) / 255;
378         }
379 
380         /* Directly create a 32-bits DDB (thanks to undocumented CreateDIBitmap flag). */
381         alpha = CreateDIBitmap(hdc, NULL, CBM_INIT | 2, bits, info, DIB_RGB_COLORS);
382 
383         HeapFree(GetProcessHeap(), 0, info);
384     }
385     else
386     {
387         WORD bpp;
388         DWORD compr;
389         LONG orig_width, orig_height;
390 
391         TRACE("Creating alpha bitmap from bitmap info.\n");
392 
393         if(!bmi_has_alpha(src_info, color_bits))
394             goto done;
395 
396         if(!DIB_GetBitmapInfo(&src_info->bmiHeader, &orig_width, &orig_height, &bpp, &compr))
397             goto done;
398         if(bpp != 32)
399             goto done;
400 
401         size = get_dib_image_size(orig_width, orig_height, bpp);
402         bits = HeapAlloc(GetProcessHeap(), 0, size);
403         if(!bits)
404             goto done;
405         CopyMemory(bits, color_bits, size);
406         /* pre-multiply by alpha */
407         for (ptr = bits; ptr < ((BYTE*)bits + size); ptr += 4)
408         {
409             unsigned int alpha = ptr[3];
410             ptr[0] = (ptr[0] * alpha) / 255;
411             ptr[1] = (ptr[1] * alpha) / 255;
412             ptr[2] = (ptr[2] * alpha) / 255;
413         }
414 
415         /* Create the bitmap. Set the bitmap info to have the right width and height */
416         if(src_info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
417         {
418             ((BITMAPCOREHEADER*)&src_info->bmiHeader)->bcWidth = width;
419             ((BITMAPCOREHEADER*)&src_info->bmiHeader)->bcHeight = height;
420         }
421         else
422         {
423             src_info->bmiHeader.biWidth = width;
424             src_info->bmiHeader.biHeight = height;
425         }
426         /* Directly create a 32-bits DDB (thanks to undocumented CreateDIBitmap flag). */
427         alpha = CreateDIBitmap(hdcScreen, NULL, 2, NULL, src_info, DIB_RGB_COLORS);
428         /* Restore values */
429         if(src_info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
430         {
431             ((BITMAPCOREHEADER*)&src_info->bmiHeader)->bcWidth = orig_width;
432             ((BITMAPCOREHEADER*)&src_info->bmiHeader)->bcHeight = orig_height;
433         }
434         else
435         {
436             src_info->bmiHeader.biWidth = orig_width;
437             src_info->bmiHeader.biHeight = orig_height;
438         }
439         if(!alpha)
440             goto done;
441         hbmpOld = SelectObject(hdc, alpha);
442         if(!hbmpOld)
443         {
444             DeleteObject(alpha);
445             alpha = NULL;
446             goto done;
447         }
448         if(!StretchDIBits( hdc, 0, 0, width, height,
449                    0, 0, orig_width, orig_height,
450                    bits, src_info, DIB_RGB_COLORS, SRCCOPY ))
451         {
452             SelectObject(hdc, hbmpOld);
453             hbmpOld = NULL;
454             DeleteObject(alpha);
455             alpha = NULL;
456         }
457         else
458         {
459             SelectObject(hdc, hbmpOld);
460         }
461     }
462 
463 done:
464     DeleteDC(hdcScreen);
465     DeleteDC( hdc );
466     if(bits) HeapFree(GetProcessHeap(), 0, bits);
467 
468     TRACE("Returning 0x%08x.\n", alpha);
469     return alpha;
470 }
471 
472 #include "pshpack1.h"
473 
474 typedef struct {
475     BYTE bWidth;
476     BYTE bHeight;
477     BYTE bColorCount;
478     BYTE bReserved;
479     WORD xHotspot;
480     WORD yHotspot;
481     DWORD dwDIBSize;
482     DWORD dwDIBOffset;
483 } CURSORICONFILEDIRENTRY;
484 
485 typedef struct
486 {
487     WORD                idReserved;
488     WORD                idType;
489     WORD                idCount;
490     CURSORICONFILEDIRENTRY  idEntries[1];
491 } CURSORICONFILEDIR;
492 
493 #include "poppack.h"
494 
495 const CURSORICONFILEDIRENTRY*
get_best_icon_file_entry(_In_ const CURSORICONFILEDIR * dir,_In_ DWORD dwFileSize,_In_ int cxDesired,_In_ int cyDesired,_In_ BOOL bIcon,_In_ DWORD fuLoad)496 get_best_icon_file_entry(
497     _In_ const CURSORICONFILEDIR* dir,
498     _In_ DWORD dwFileSize,
499     _In_ int cxDesired,
500     _In_ int cyDesired,
501     _In_ BOOL bIcon,
502     _In_ DWORD fuLoad
503 )
504 {
505     CURSORICONDIR* fakeDir;
506     CURSORICONDIRENTRY* fakeEntry;
507     WORD i;
508     const CURSORICONFILEDIRENTRY* entry;
509 
510     /* Check our file is what it claims to be */
511     if ( dwFileSize < sizeof(*dir) )
512         return NULL;
513 
514     if (dwFileSize < FIELD_OFFSET(CURSORICONFILEDIR, idEntries[dir->idCount]))
515         return NULL;
516 
517     /*
518      * Cute little hack:
519      * We allocate a buffer, fake it as if it was a pointer to a resource in a module,
520      * pass it to LookupIconIdFromDirectoryEx and get back the index we have to use
521      */
522     fakeDir = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(CURSORICONDIR, idEntries[dir->idCount]));
523     if(!fakeDir)
524     {
525         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
526         return NULL;
527     }
528     fakeDir->idReserved = 0;
529     fakeDir->idType = dir->idType;
530     fakeDir->idCount = dir->idCount;
531     for(i = 0; i<dir->idCount; i++)
532     {
533         fakeEntry = &fakeDir->idEntries[i];
534         entry = &dir->idEntries[i];
535         /* Take this as an occasion to perform a size check */
536         if ((entry->dwDIBOffset > dwFileSize)
537                 || ((entry->dwDIBOffset + entry->dwDIBSize) > dwFileSize))
538         {
539             ERR("Corrupted icon file?.\n");
540             HeapFree(GetProcessHeap(), 0, fakeDir);
541             return NULL;
542         }
543         /* File icon/cursors are not like resource ones */
544         if(bIcon)
545         {
546             fakeEntry->ResInfo.icon.bWidth = entry->bWidth;
547             fakeEntry->ResInfo.icon.bHeight = entry->bHeight;
548             fakeEntry->ResInfo.icon.bColorCount = 0;
549             fakeEntry->ResInfo.icon.bReserved = 0;
550         }
551         else
552         {
553             fakeEntry->ResInfo.cursor.wWidth = entry->bWidth;
554             fakeEntry->ResInfo.cursor.wHeight = entry->bHeight;
555         }
556         /* Let's assume there's always one plane */
557         fakeEntry->wPlanes = 1;
558         /* We must get the bitcount from the BITMAPINFOHEADER itself */
559         if (((BITMAPINFOHEADER *)((char *)dir + entry->dwDIBOffset))->biSize == sizeof(BITMAPCOREHEADER))
560             fakeEntry->wBitCount = ((BITMAPCOREHEADER *)((char *)dir + entry->dwDIBOffset))->bcBitCount;
561         else
562             fakeEntry->wBitCount = ((BITMAPINFOHEADER *)((char *)dir + entry->dwDIBOffset))->biBitCount;
563         fakeEntry->dwBytesInRes = entry->dwDIBSize;
564         fakeEntry->wResId = i + 1;
565     }
566 
567     /* Now call LookupIconIdFromResourceEx */
568     i = LookupIconIdFromDirectoryEx((PBYTE)fakeDir, bIcon, cxDesired, cyDesired, fuLoad & LR_MONOCHROME);
569     /* We don't need this anymore */
570     HeapFree(GetProcessHeap(), 0, fakeDir);
571     if(i == 0)
572     {
573         WARN("Unable to get a fit entry index.\n");
574         return NULL;
575     }
576 
577     /* We found it */
578     return &dir->idEntries[i-1];
579 }
580 
581 DWORD
get_best_icon_file_offset(_In_ const LPBYTE dir,_In_ DWORD dwFileSize,_In_ int cxDesired,_In_ int cyDesired,_In_ BOOL bIcon,_In_ DWORD fuLoad,_Out_ POINT * ptHotSpot)582 get_best_icon_file_offset(
583     _In_ const LPBYTE dir,
584     _In_ DWORD dwFileSize,
585     _In_ int cxDesired,
586     _In_ int cyDesired,
587     _In_ BOOL bIcon,
588     _In_ DWORD fuLoad,
589     _Out_ POINT *ptHotSpot
590 )
591 {
592     const CURSORICONFILEDIRENTRY *entry;
593 
594     entry = get_best_icon_file_entry((CURSORICONFILEDIR *) dir, dwFileSize, cxDesired, cyDesired, bIcon, fuLoad);
595 
596     if(ptHotSpot)
597     {
598         ptHotSpot->x = entry->xHotspot;
599         ptHotSpot->y = entry->yHotspot;
600     }
601 
602     if(entry)
603         return entry->dwDIBOffset;
604 
605     return 0;
606 }
607 
608 
609 
610 /************* IMPLEMENTATION CORE ****************/
611 
CURSORICON_GetCursorDataFromBMI(_Inout_ CURSORDATA * pdata,_In_ const BITMAPINFO * pbmi)612 static BOOL CURSORICON_GetCursorDataFromBMI(
613     _Inout_ CURSORDATA* pdata,
614     _In_    const BITMAPINFO *pbmi
615 )
616 {
617     UINT ubmiSize = bitmap_info_size(pbmi, DIB_RGB_COLORS);
618     BOOL monochrome = is_dib_monochrome(pbmi);
619     LONG width, height;
620     WORD bpp;
621     DWORD compr;
622     int ibmpType;
623     HDC hdc, hdcScreen;
624     BITMAPINFO* pbmiCopy;
625     HBITMAP hbmpOld = NULL;
626     BOOL bResult = FALSE;
627     const VOID *pvColor, *pvMask;
628 
629     ibmpType = DIB_GetBitmapInfo(&pbmi->bmiHeader, &width, &height, &bpp, &compr);
630     /* Invalid data */
631     if(ibmpType < 0)
632         return FALSE;
633 
634     /* No compression for icons */
635     if(compr != BI_RGB)
636         return FALSE;
637 
638     /* If no dimensions were set, use the one from the icon */
639     if(!pdata->cx) pdata->cx = width;
640     if(!pdata->cy) pdata->cy = height < 0 ? -height/2 : height/2;
641 
642     /* Fix the hotspot coords */
643     if(pdata->rt == (USHORT)((ULONG_PTR)RT_CURSOR))
644     {
645         if(pdata->cx != width)
646             pdata->xHotspot = (pdata->xHotspot * pdata->cx) / width;
647         if(pdata->cy != height/2)
648             pdata->yHotspot = (pdata->yHotspot * pdata->cy * 2) / height;
649     }
650     else
651     {
652         pdata->xHotspot = pdata->cx/2;
653         pdata->yHotspot = pdata->cy/2;
654     }
655 
656     hdcScreen = CreateDCW(DISPLAYW, NULL, NULL, NULL);
657     if(!hdcScreen)
658         return FALSE;
659     hdc = CreateCompatibleDC(hdcScreen);
660     if(!hdc)
661     {
662         DeleteDC(hdcScreen);
663         return FALSE;
664     }
665 
666     pbmiCopy = HeapAlloc(GetProcessHeap(), 0, max(ubmiSize, FIELD_OFFSET(BITMAPINFO, bmiColors[3])));
667     if(!pbmiCopy)
668         goto done;
669     RtlCopyMemory(pbmiCopy, pbmi, ubmiSize);
670 
671     /* In an icon/cursor, the BITMAPINFO holds twice the height */
672     if(pbmiCopy->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
673         ((BITMAPCOREHEADER*)&pbmiCopy->bmiHeader)->bcHeight /= 2;
674     else
675         pbmiCopy->bmiHeader.biHeight /= 2;
676     height /= 2;
677 
678     pvColor = (const char*)pbmi + ubmiSize;
679     pvMask = (const char*)pvColor +
680         get_dib_image_size(width, height, bpp );
681 
682     /* Set XOR bits */
683     if(monochrome)
684     {
685         /* Create the 1bpp bitmap which will contain everything */
686         pdata->hbmColor = NULL;
687         pdata->hbmMask = CreateBitmap(pdata->cx, pdata->cy * 2, 1, 1, NULL);
688         if(!pdata->hbmMask)
689             goto done;
690         hbmpOld = SelectObject(hdc, pdata->hbmMask);
691         if(!hbmpOld)
692             goto done;
693 
694         if(!StretchDIBits(hdc, 0, pdata->cy, pdata->cx, pdata->cy,
695                           0, 0, width, height,
696                           pvColor, pbmiCopy, DIB_RGB_COLORS, SRCCOPY))
697             goto done;
698         pdata->bpp = 1;
699     }
700     else
701     {
702         /* Create the bitmap. It has to be compatible with the screen surface */
703         pdata->hbmColor = CreateCompatibleBitmap(hdcScreen, pdata->cx, pdata->cy);
704         if(!pdata->hbmColor)
705             goto done;
706         /* Create the 1bpp mask bitmap */
707         pdata->hbmMask = CreateBitmap(pdata->cx, pdata->cy, 1, 1, NULL);
708         if(!pdata->hbmMask)
709             goto done;
710         hbmpOld = SelectObject(hdc, pdata->hbmColor);
711         if(!hbmpOld)
712             goto done;
713         if(!StretchDIBits(hdc, 0, 0, pdata->cx, pdata->cy,
714                   0, 0, width, height,
715                   pvColor, pbmiCopy, DIB_RGB_COLORS, SRCCOPY))
716             goto done;
717         pdata->bpp = GetDeviceCaps(hdcScreen, BITSPIXEL);
718         pdata->hbmAlpha = create_alpha_bitmap(NULL, pbmiCopy, pvColor, pdata->cx, pdata->cy);
719 
720         /* Now convert the info to monochrome for the mask bits */
721         if (pbmiCopy->bmiHeader.biSize != sizeof(BITMAPCOREHEADER))
722         {
723             RGBQUAD *rgb = pbmiCopy->bmiColors;
724 
725             pbmiCopy->bmiHeader.biClrUsed = pbmiCopy->bmiHeader.biClrImportant = 2;
726             rgb[0].rgbBlue = rgb[0].rgbGreen = rgb[0].rgbRed = 0x00;
727             rgb[1].rgbBlue = rgb[1].rgbGreen = rgb[1].rgbRed = 0xff;
728             rgb[0].rgbReserved = rgb[1].rgbReserved = 0;
729             pbmiCopy->bmiHeader.biBitCount = 1;
730         }
731         else
732         {
733             RGBTRIPLE *rgb = (RGBTRIPLE *)(((BITMAPCOREHEADER *)pbmiCopy) + 1);
734 
735             rgb[0].rgbtBlue = rgb[0].rgbtGreen = rgb[0].rgbtRed = 0x00;
736             rgb[1].rgbtBlue = rgb[1].rgbtGreen = rgb[1].rgbtRed = 0xff;
737             ((BITMAPCOREHEADER*)&pbmiCopy->bmiHeader)->bcBitCount = 1;
738         }
739     }
740     /* Set the mask bits */
741     if(!SelectObject(hdc, pdata->hbmMask))
742         goto done;
743     bResult = StretchDIBits(hdc, 0, 0, pdata->cx, pdata->cy,
744                   0, 0, width, height,
745                   pvMask, pbmiCopy, DIB_RGB_COLORS, SRCCOPY) != 0;
746 
747 done:
748     DeleteDC(hdcScreen);
749     if(hbmpOld) SelectObject(hdc, hbmpOld);
750     DeleteDC(hdc);
751     if(pbmiCopy) HeapFree(GetProcessHeap(), 0, pbmiCopy);
752     /* Clean up in case of failure */
753     if(!bResult)
754     {
755         if(pdata->hbmMask) DeleteObject(pdata->hbmMask);
756         if(pdata->hbmColor) DeleteObject(pdata->hbmColor);
757         if(pdata->hbmAlpha) DeleteObject(pdata->hbmAlpha);
758     }
759     return bResult;
760 }
761 
CURSORICON_GetCursorDataFromIconInfo(_Out_ CURSORDATA * pCursorData,_In_ ICONINFO * pIconInfo)762 static BOOL CURSORICON_GetCursorDataFromIconInfo(
763   _Out_ CURSORDATA* pCursorData,
764   _In_  ICONINFO* pIconInfo
765 )
766 {
767     BITMAP bm;
768 
769     ZeroMemory(pCursorData, sizeof(*pCursorData));
770     if(pIconInfo->hbmColor)
771     {
772         /* We must convert the color bitmap to screen format */
773         HDC hdcScreen, hdcMem;
774         HBITMAP hbmpPrev;
775 
776         /* The mask dictates its dimensions */
777         if (!GetObject(pIconInfo->hbmMask, sizeof(bm), &bm))
778             return FALSE;
779         hdcScreen = CreateDCW(DISPLAYW, NULL, NULL, NULL);
780         if(!hdcScreen)
781             return FALSE;
782         hdcMem = CreateCompatibleDC(hdcScreen);
783         if(!hdcMem)
784         {
785             DeleteDC(hdcScreen);
786             return FALSE;
787         }
788         pCursorData->hbmColor = CreateCompatibleBitmap(hdcScreen, bm.bmWidth, bm.bmHeight);
789         DeleteDC(hdcScreen);
790         if (!pCursorData->hbmColor)
791         {
792             DeleteDC(hdcMem);
793             return FALSE;
794         }
795         hbmpPrev = SelectObject(hdcMem, pCursorData->hbmColor);
796         if (!hbmpPrev)
797         {
798             DeleteDC(hdcMem);
799             DeleteObject(pCursorData->hbmColor);
800             return FALSE;
801         }
802         stretch_blt_icon( hdcMem, bm.bmWidth, bm.bmHeight, pIconInfo->hbmColor);
803         SelectObject(hdcMem, hbmpPrev);
804         DeleteDC(hdcMem);
805     }
806     pCursorData->hbmMask = CopyImage(pIconInfo->hbmMask, IMAGE_BITMAP, 0, 0, LR_MONOCHROME);
807     if(!pCursorData->hbmMask)
808         return FALSE;
809 
810     /* Now, fill some information */
811     pCursorData->rt = (USHORT)((ULONG_PTR)(pIconInfo->fIcon ? RT_ICON : RT_CURSOR));
812     if(pCursorData->hbmColor)
813     {
814         GetObject(pCursorData->hbmColor, sizeof(bm), &bm);
815         pCursorData->bpp = bm.bmBitsPixel;
816         pCursorData->cx = bm.bmWidth;
817         pCursorData->cy = bm.bmHeight;
818         if(pCursorData->bpp == 32)
819             pCursorData->hbmAlpha = create_alpha_bitmap(pCursorData->hbmColor, NULL, NULL, 0, 0);
820     }
821     else
822     {
823         GetObject(pCursorData->hbmMask, sizeof(bm), &bm);
824         pCursorData->bpp = 1;
825         pCursorData->cx = bm.bmWidth;
826         pCursorData->cy = bm.bmHeight/2;
827     }
828 
829     if(pIconInfo->fIcon)
830     {
831         pCursorData->xHotspot = pCursorData->cx/2;
832         pCursorData->yHotspot = pCursorData->cy/2;
833     }
834     else
835     {
836         pCursorData->xHotspot = pIconInfo->xHotspot;
837         pCursorData->yHotspot = pIconInfo->yHotspot;
838     }
839 
840     return TRUE;
841 }
842 
843 
844 #define RIFF_FOURCC( c0, c1, c2, c3 ) \
845         ( (DWORD)(BYTE)(c0) | ( (DWORD)(BYTE)(c1) << 8 ) | \
846         ( (DWORD)(BYTE)(c2) << 16 ) | ( (DWORD)(BYTE)(c3) << 24 ) )
847 
848 #define ANI_RIFF_ID RIFF_FOURCC('R', 'I', 'F', 'F')
849 #define ANI_LIST_ID RIFF_FOURCC('L', 'I', 'S', 'T')
850 #define ANI_ACON_ID RIFF_FOURCC('A', 'C', 'O', 'N')
851 #define ANI_anih_ID RIFF_FOURCC('a', 'n', 'i', 'h')
852 #define ANI_seq__ID RIFF_FOURCC('s', 'e', 'q', ' ')
853 #define ANI_fram_ID RIFF_FOURCC('f', 'r', 'a', 'm')
854 #define ANI_rate_ID RIFF_FOURCC('r', 'a', 't', 'e')
855 
856 #define ANI_FLAG_ICON       0x1
857 #define ANI_FLAG_SEQUENCE   0x2
858 
859 #include <pshpack1.h>
860 typedef struct {
861     DWORD header_size;
862     DWORD num_frames;
863     DWORD num_steps;
864     DWORD width;
865     DWORD height;
866     DWORD bpp;
867     DWORD num_planes;
868     DWORD display_rate;
869     DWORD flags;
870 } ani_header;
871 
872 typedef struct {
873     DWORD           data_size;
874     const unsigned char   *data;
875 } riff_chunk_t;
876 #include <poppack.h>
877 
dump_ani_header(const ani_header * header)878 static void dump_ani_header( const ani_header *header )
879 {
880     TRACE("     header size: %d\n", header->header_size);
881     TRACE("          frames: %d\n", header->num_frames);
882     TRACE("           steps: %d\n", header->num_steps);
883     TRACE("           width: %d\n", header->width);
884     TRACE("          height: %d\n", header->height);
885     TRACE("             bpp: %d\n", header->bpp);
886     TRACE("          planes: %d\n", header->num_planes);
887     TRACE("    display rate: %d\n", header->display_rate);
888     TRACE("           flags: 0x%08x\n", header->flags);
889 }
890 
891 /* Find an animated cursor chunk, given its type and ID */
riff_find_chunk(DWORD chunk_id,DWORD chunk_type,const riff_chunk_t * parent_chunk,riff_chunk_t * chunk)892 static void riff_find_chunk( DWORD chunk_id, DWORD chunk_type, const riff_chunk_t *parent_chunk, riff_chunk_t *chunk )
893 {
894     const unsigned char *ptr = parent_chunk->data;
895     const unsigned char *end = parent_chunk->data + (parent_chunk->data_size - (2 * sizeof(DWORD)));
896 
897     if (chunk_type == ANI_LIST_ID || chunk_type == ANI_RIFF_ID) end -= sizeof(DWORD);
898 
899     while (ptr < end)
900     {
901         if ((!chunk_type && *(const DWORD *)ptr == chunk_id )
902                 || (chunk_type && *(const DWORD *)ptr == chunk_type && *((const DWORD *)ptr + 2) == chunk_id ))
903         {
904             ptr += sizeof(DWORD);
905             chunk->data_size = (*(const DWORD *)ptr + 1) & ~1;
906             ptr += sizeof(DWORD);
907             if (chunk_type == ANI_LIST_ID || chunk_type == ANI_RIFF_ID) ptr += sizeof(DWORD);
908             chunk->data = ptr;
909 
910             return;
911         }
912 
913         ptr += sizeof(DWORD);
914         ptr += (*(const DWORD *)ptr + 1) & ~1;
915         ptr += sizeof(DWORD);
916     }
917 }
918 
CURSORICON_GetCursorDataFromANI(_Inout_ CURSORDATA * pCurData,_In_ const BYTE * pData,_In_ DWORD dwDataSize,_In_ DWORD fuLoad)919 static BOOL CURSORICON_GetCursorDataFromANI(
920     _Inout_ CURSORDATA* pCurData,
921     _In_    const BYTE *pData,
922     _In_    DWORD dwDataSize,
923     _In_    DWORD fuLoad
924 )
925 {
926     UINT i;
927     const ani_header *pHeader;
928     riff_chunk_t root_chunk = { dwDataSize, pData };
929     riff_chunk_t ACON_chunk = {0};
930     riff_chunk_t anih_chunk = {0};
931     riff_chunk_t fram_chunk = {0};
932     riff_chunk_t rate_chunk = {0};
933     riff_chunk_t seq_chunk = {0};
934     const unsigned char *icon_chunk;
935     const unsigned char *icon_data;
936 
937     /* Find the root chunk */
938     riff_find_chunk( ANI_ACON_ID, ANI_RIFF_ID, &root_chunk, &ACON_chunk );
939     if (!ACON_chunk.data)
940     {
941         ERR("Failed to get root chunk.\n");
942         return FALSE;
943     }
944 
945     /* Find the header chunk */
946     riff_find_chunk( ANI_anih_ID, 0, &ACON_chunk, &anih_chunk );
947     if (!ACON_chunk.data)
948     {
949         ERR("Failed to get header chunk.\n");
950         return FALSE;
951     }
952     pHeader = (ani_header*)anih_chunk.data;
953     dump_ani_header(pHeader);
954 
955     /* Set up the master data */
956     pCurData->CURSORF_flags |= CURSORF_ACON;
957     pCurData->cpcur = pHeader->num_frames;
958     pCurData->cicur = pHeader->num_steps;
959     pCurData->iicur = pHeader->display_rate;
960 
961     /* Get the sequences */
962     if (pHeader->flags & ANI_FLAG_SEQUENCE)
963     {
964         riff_find_chunk( ANI_seq__ID, 0, &ACON_chunk, &seq_chunk );
965         if (!seq_chunk.data)
966         {
967             ERR("No sequence data although the flag is set!\n");
968             return FALSE;
969         }
970     }
971 
972     /* Get the frame rates */
973     riff_find_chunk( ANI_rate_ID, 0, &ACON_chunk, &rate_chunk );
974     if (rate_chunk.data)
975         pCurData->ajifRate = (INT*)rate_chunk.data;
976 
977     /* Get the frames chunk */
978     riff_find_chunk( ANI_fram_ID, ANI_LIST_ID, &ACON_chunk, &fram_chunk );
979     if (!fram_chunk.data)
980     {
981         ERR("Failed to get icon list.\n");
982         return 0;
983     }
984     icon_chunk = fram_chunk.data;
985     icon_data = fram_chunk.data + (2 * sizeof(DWORD));
986 
987     if(pHeader->num_frames > 1)
988     {
989         /* Allocate frame descriptors, step indices and rates */
990         pCurData->aspcur = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
991             pHeader->num_frames * sizeof(CURSORDATA) + pHeader->num_steps * (sizeof(DWORD) + sizeof(INT)));
992         if(!pCurData->aspcur)
993         {
994             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
995             return FALSE;
996         }
997         pCurData->aicur = (DWORD*)(pCurData->aspcur + pHeader->num_frames);
998         pCurData->ajifRate = (INT*)(pCurData->aicur + pHeader->num_steps);
999     }
1000 
1001     for(i=0; i < pHeader->num_frames; i++)
1002     {
1003         CURSORDATA* pFrameData;
1004         const DWORD chunk_size = *(const DWORD *)(icon_chunk + sizeof(DWORD));
1005         const BITMAPINFO* pbmi;
1006 
1007         if(pHeader->num_frames > 1)
1008             pFrameData = &pCurData->aspcur[i];
1009         else
1010             pFrameData = pCurData;
1011 
1012         pFrameData->rt = pCurData->rt;
1013 
1014         if (pHeader->flags & ANI_FLAG_ICON)
1015         {
1016             /* The chunks describe an icon file */
1017             const CURSORICONFILEDIRENTRY* pDirEntry = get_best_icon_file_entry(
1018                 (const CURSORICONFILEDIR *) icon_data,
1019                 chunk_size,
1020                 pCurData->cx,
1021                 pCurData->cy,
1022                 TRUE,
1023                 fuLoad);
1024             if(!pDirEntry)
1025             {
1026                 ERR("Unable to find the right file entry for frame %d.\n", i);
1027                 goto error;
1028             }
1029             pFrameData->xHotspot = pDirEntry->xHotspot;
1030             pFrameData->yHotspot = pDirEntry->yHotspot;
1031             if(!pHeader->width || !pHeader->height)
1032             {
1033                 pFrameData->cx = pDirEntry->bWidth;
1034                 pFrameData->cy = pDirEntry->bHeight;
1035             }
1036             else
1037             {
1038                 pFrameData->cx = pHeader->width;
1039                 pFrameData->cy = pHeader->height;
1040             }
1041             pbmi = (const BITMAPINFO *) (icon_data + pDirEntry->dwDIBOffset);
1042         }
1043         else
1044         {
1045             /* The chunks just describe bitmaps */
1046             pbmi = (const BITMAPINFO *)icon_data;
1047             pFrameData->xHotspot = pFrameData->yHotspot = 0;
1048         }
1049 
1050         /* Do the real work */
1051         CURSORICON_GetCursorDataFromBMI(pFrameData, pbmi);
1052 
1053         if(pHeader->num_frames > 1)
1054             pFrameData->CURSORF_flags |= CURSORF_ACONFRAME;
1055         else
1056             pFrameData->CURSORF_flags &= ~CURSORF_ACON;
1057 
1058 
1059         /* Next frame */
1060         icon_chunk += chunk_size + (2 * sizeof(DWORD));
1061         icon_data = icon_chunk + (2 * sizeof(DWORD));
1062     }
1063 
1064     if(pHeader->num_frames <= 1)
1065         return TRUE;
1066 
1067     if(rate_chunk.data)
1068         CopyMemory(pCurData->ajifRate, rate_chunk.data, pHeader->num_steps * sizeof(INT));
1069     else
1070     {
1071         for(i=0; i < pHeader->num_steps; i++)
1072             pCurData->ajifRate[i] = pHeader->display_rate;
1073     }
1074 
1075     if (pHeader->flags & ANI_FLAG_SEQUENCE)
1076     {
1077         CopyMemory(pCurData->aicur, seq_chunk.data, pHeader->num_steps * sizeof(DWORD));
1078     }
1079     else
1080     {
1081         for(i=0; i < pHeader->num_steps; i++)
1082             pCurData->aicur[i] = i;
1083     }
1084 
1085     return TRUE;
1086 
1087 error:
1088     HeapFree(GetProcessHeap(), 0, pCurData->aspcur);
1089     ZeroMemory(pCurData, sizeof(CURSORDATA));
1090     return FALSE;
1091 }
1092 
1093 
1094 static
1095 HBITMAP
BITMAP_LoadImageW(_In_opt_ HINSTANCE hinst,_In_ LPCWSTR lpszName,_In_ int cxDesired,_In_ int cyDesired,_In_ UINT fuLoad)1096 BITMAP_LoadImageW(
1097   _In_opt_  HINSTANCE hinst,
1098   _In_      LPCWSTR lpszName,
1099   _In_      int cxDesired,
1100   _In_      int cyDesired,
1101   _In_      UINT fuLoad
1102 )
1103 {
1104     const BITMAPINFO* pbmi;
1105     BITMAPINFO* pbmiScaled = NULL;
1106     BITMAPINFO* pbmiCopy = NULL;
1107     const VOID* pvMapping = NULL;
1108     DWORD dwOffset = 0;
1109     HGLOBAL hgRsrc = NULL;
1110     int iBMISize;
1111     PVOID pvBits;
1112     HDC hdcScreen = NULL;
1113     HDC hdc = NULL;
1114     HBITMAP hbmpOld, hbmpRet = NULL;
1115     LONG width, height;
1116     WORD bpp;
1117     DWORD compr, ResSize = 0;
1118 
1119     /* Map the bitmap info */
1120     if(fuLoad & LR_LOADFROMFILE)
1121     {
1122         const BITMAPFILEHEADER* pbmfh;
1123 
1124         pvMapping = map_fileW(lpszName, NULL);
1125         if(!pvMapping)
1126             return NULL;
1127         pbmfh = pvMapping;
1128         if (pbmfh->bfType != 0x4d42 /* 'BM' */)
1129         {
1130             WARN("Invalid/unsupported bitmap format!\n");
1131             goto end;
1132         }
1133         pbmi = (const BITMAPINFO*)(pbmfh + 1);
1134 
1135         /* Get the image bits */
1136         if(pbmfh->bfOffBits)
1137             dwOffset = pbmfh->bfOffBits - sizeof(BITMAPFILEHEADER);
1138     }
1139     else
1140     {
1141         HRSRC hrsrc;
1142 
1143         /* Caller wants an OEM bitmap */
1144         if(!hinst)
1145             hinst = User32Instance;
1146         hrsrc = FindResourceW(hinst, lpszName, RT_BITMAP);
1147         if(!hrsrc)
1148             return NULL;
1149         hgRsrc = LoadResource(hinst, hrsrc);
1150         if(!hgRsrc)
1151             return NULL;
1152         pbmi = LockResource(hgRsrc);
1153         if(!pbmi)
1154             return NULL;
1155         ResSize = SizeofResource(hinst, hrsrc);
1156     }
1157 
1158     /* Fix up values */
1159     if(DIB_GetBitmapInfo(&pbmi->bmiHeader, &width, &height, &bpp, &compr) == -1)
1160         goto end;
1161     if((width > 65535) || (height > 65535))
1162         goto end;
1163     if(cxDesired == 0)
1164         cxDesired = width;
1165     if(cyDesired == 0)
1166         cyDesired = height;
1167     else if(height < 0)
1168         cyDesired = -cyDesired;
1169 
1170     iBMISize = bitmap_info_size(pbmi, DIB_RGB_COLORS);
1171 
1172     /* Get a pointer to the image data */
1173     pvBits = (char*)pbmi + (dwOffset ? dwOffset : iBMISize);
1174 
1175     /* Create a copy of the info describing the bitmap in the file */
1176     pbmiCopy = HeapAlloc(GetProcessHeap(), 0, iBMISize);
1177     if(!pbmiCopy)
1178         goto end;
1179     CopyMemory(pbmiCopy, pbmi, iBMISize);
1180 
1181     TRACE("Size Image %d, Size Header %d, ResSize %d\n",
1182         pbmiCopy->bmiHeader.biSizeImage, pbmiCopy->bmiHeader.biSize, ResSize);
1183 
1184     /* HACK: If this is a binutils' windres.exe compiled 16 or 32 bpp bitmap
1185      * using BI_BITFIELDS, then a bug causes it to fail to include
1186      * the bytes for the bitfields. So, we have to substract out the
1187      * size of the bitfields previously included from bitmap_info_size. */
1188     if (compr == BI_BITFIELDS && (bpp == 16 || bpp == 32) &&
1189         pbmiCopy->bmiHeader.biSizeImage + pbmiCopy->bmiHeader.biSize == ResSize)
1190     {
1191         /* GCC pointer to the image data has 12 less bytes than MSVC */
1192         pvBits = (char*)pvBits - 12;
1193         WARN("Found GCC Resource Compiled 16-bpp or 32-bpp error\n");
1194     }
1195 
1196     /* Fix it up, if needed */
1197     if(fuLoad & (LR_LOADTRANSPARENT | LR_LOADMAP3DCOLORS))
1198     {
1199         WORD bpp, incr, numColors;
1200         char* pbmiColors;
1201         RGBTRIPLE* ptr;
1202         COLORREF crWindow, cr3DShadow, cr3DFace, cr3DLight;
1203         BYTE pixel = *((BYTE*)pvBits);
1204         UINT i;
1205 
1206         if(pbmiCopy->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
1207         {
1208             bpp = ((BITMAPCOREHEADER*)&pbmiCopy->bmiHeader)->bcBitCount;
1209             numColors = 1 << bpp;
1210             /* BITMAPCOREINFO holds RGBTRIPLEs */
1211             incr = 3;
1212         }
1213         else
1214         {
1215             bpp = pbmiCopy->bmiHeader.biBitCount;
1216             /* BITMAPINFOHEADER holds RGBQUADs */
1217             incr = 4;
1218             numColors = pbmiCopy->bmiHeader.biClrUsed;
1219             if(numColors > 256) numColors = 256;
1220             if (!numColors && (bpp <= 8)) numColors = 1 << bpp;
1221         }
1222 
1223         if(bpp > 8)
1224             goto create_bitmap;
1225 
1226         pbmiColors = (char*)pbmiCopy + pbmiCopy->bmiHeader.biSize;
1227 
1228         /* Get the relevant colors */
1229         crWindow = GetSysColor(COLOR_WINDOW);
1230         cr3DShadow = GetSysColor(COLOR_3DSHADOW);
1231         cr3DFace = GetSysColor(COLOR_3DFACE);
1232         cr3DLight = GetSysColor(COLOR_3DLIGHT);
1233 
1234         /* Fix the transparent palette entry */
1235         if(fuLoad & LR_LOADTRANSPARENT)
1236         {
1237             switch(bpp)
1238             {
1239                 case 1: pixel >>= 7; break;
1240                 case 4: pixel >>= 4; break;
1241                 case 8: break;
1242                 default:
1243                     FIXME("Unhandled bit depth %d.\n", bpp);
1244                     goto create_bitmap;
1245             }
1246 
1247             if(pixel >= numColors)
1248             {
1249                 ERR("Wrong pixel passed in.\n");
1250                 goto create_bitmap;
1251             }
1252 
1253             /* If both flags are set, we must use COLOR_3DFACE */
1254             if(fuLoad & LR_LOADMAP3DCOLORS) crWindow = cr3DFace;
1255 
1256             /* Define the color */
1257             ptr = (RGBTRIPLE*)(pbmiColors + pixel*incr);
1258             ptr->rgbtBlue = GetBValue(crWindow);
1259             ptr->rgbtGreen = GetGValue(crWindow);
1260             ptr->rgbtRed = GetRValue(crWindow);
1261             goto create_bitmap;
1262         }
1263 
1264         /* If we are here, then LR_LOADMAP3DCOLORS is set without LR_TRANSPARENT */
1265         for(i = 0; i<numColors; i++)
1266         {
1267             ptr = (RGBTRIPLE*)(pbmiColors + i*incr);
1268             if((ptr->rgbtBlue == ptr->rgbtRed) && (ptr->rgbtBlue == ptr->rgbtGreen))
1269             {
1270                 if(ptr->rgbtBlue == 128)
1271                 {
1272                     ptr->rgbtBlue = GetBValue(cr3DShadow);
1273                     ptr->rgbtGreen = GetGValue(cr3DShadow);
1274                     ptr->rgbtRed = GetRValue(cr3DShadow);
1275                 }
1276                 if(ptr->rgbtBlue == 192)
1277                 {
1278                     ptr->rgbtBlue = GetBValue(cr3DFace);
1279                     ptr->rgbtGreen = GetGValue(cr3DFace);
1280                     ptr->rgbtRed = GetRValue(cr3DFace);
1281                 }
1282                 if(ptr->rgbtBlue == 223)
1283                 {
1284                     ptr->rgbtBlue = GetBValue(cr3DLight);
1285                     ptr->rgbtGreen = GetGValue(cr3DLight);
1286                     ptr->rgbtRed = GetRValue(cr3DLight);
1287                 }
1288             }
1289         }
1290     }
1291 
1292 create_bitmap:
1293     if(fuLoad & LR_CREATEDIBSECTION)
1294     {
1295         /* Allocate the BMI describing the new bitmap */
1296         pbmiScaled = HeapAlloc(GetProcessHeap(), 0, iBMISize);
1297         if(!pbmiScaled)
1298             goto end;
1299         CopyMemory(pbmiScaled, pbmiCopy, iBMISize);
1300 
1301         /* Fix it up */
1302         if(pbmiScaled->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
1303         {
1304             BITMAPCOREHEADER* pbmch = (BITMAPCOREHEADER*)&pbmiScaled->bmiHeader;
1305             pbmch->bcWidth = cxDesired;
1306             pbmch->bcHeight = cyDesired;
1307         }
1308         else
1309         {
1310             pbmiScaled->bmiHeader.biWidth = cxDesired;
1311             pbmiScaled->bmiHeader.biHeight = cyDesired;
1312             /* No compression for DIB sections */
1313             pbmiScaled->bmiHeader.biCompression = BI_RGB;
1314         }
1315     }
1316 
1317     /* Top-down image */
1318     if(cyDesired < 0) cyDesired = -cyDesired;
1319 
1320     /* We need a device context */
1321     hdcScreen = CreateDCW(DISPLAYW, NULL, NULL, NULL);
1322     if(!hdcScreen)
1323         goto end;
1324     hdc = CreateCompatibleDC(hdcScreen);
1325     if(!hdc)
1326         goto end;
1327 
1328     /* Now create the bitmap */
1329     if(fuLoad & LR_CREATEDIBSECTION)
1330         hbmpRet = CreateDIBSection(hdc, pbmiScaled, DIB_RGB_COLORS, NULL, 0, 0);
1331     else
1332     {
1333         if(is_dib_monochrome(pbmiCopy) || (fuLoad & LR_MONOCHROME))
1334             hbmpRet = CreateBitmap(cxDesired, cyDesired, 1, 1, NULL);
1335         else
1336             hbmpRet = CreateCompatibleBitmap(hdcScreen, cxDesired, cyDesired);
1337     }
1338 
1339     if(!hbmpRet)
1340         goto end;
1341 
1342     hbmpOld = SelectObject(hdc, hbmpRet);
1343     if(!hbmpOld)
1344         goto end;
1345     if(!StretchDIBits(hdc, 0, 0, cxDesired, cyDesired,
1346                            0, 0, width, height,
1347                            pvBits, pbmiCopy, DIB_RGB_COLORS, SRCCOPY))
1348     {
1349         ERR("StretchDIBits failed!.\n");
1350         SelectObject(hdc, hbmpOld);
1351         DeleteObject(hbmpRet);
1352         hbmpRet = NULL;
1353         goto end;
1354     }
1355 
1356     SelectObject(hdc, hbmpOld);
1357 
1358 end:
1359     if(hdcScreen)
1360         DeleteDC(hdcScreen);
1361     if(hdc)
1362         DeleteDC(hdc);
1363     if(pbmiScaled)
1364         HeapFree(GetProcessHeap(), 0, pbmiScaled);
1365     if(pbmiCopy)
1366         HeapFree(GetProcessHeap(), 0, pbmiCopy);
1367     if (pvMapping)
1368         UnmapViewOfFile( pvMapping );
1369     if(hgRsrc)
1370         FreeResource(hgRsrc);
1371 
1372     return hbmpRet;
1373 }
1374 
1375 
1376 static
1377 HANDLE
CURSORICON_LoadFromFileW(_In_ LPCWSTR lpszName,_In_ int cxDesired,_In_ int cyDesired,_In_ UINT fuLoad,_In_ BOOL bIcon)1378 CURSORICON_LoadFromFileW(
1379   _In_      LPCWSTR lpszName,
1380   _In_      int cxDesired,
1381   _In_      int cyDesired,
1382   _In_      UINT fuLoad,
1383   _In_      BOOL bIcon
1384 )
1385 {
1386     const CURSORICONFILEDIRENTRY *entry;
1387     const CURSORICONFILEDIR *dir;
1388     DWORD filesize = 0;
1389     LPBYTE bits;
1390     HANDLE hCurIcon = NULL;
1391     CURSORDATA cursorData;
1392 
1393     TRACE("loading %s\n", debugstr_w( lpszName ));
1394 
1395     bits = map_fileW( lpszName, &filesize );
1396     if (!bits)
1397         return NULL;
1398 
1399     /* Check for .ani. */
1400     if (memcmp( bits, "RIFF", 4 ) == 0)
1401     {
1402         UNIMPLEMENTED;
1403         goto end;
1404     }
1405 
1406     dir = (CURSORICONFILEDIR*) bits;
1407     entry = get_best_icon_file_entry(dir, filesize, cxDesired, cyDesired, bIcon, fuLoad);
1408     if(!entry)
1409         goto end;
1410 
1411     /* Fix dimensions */
1412     if(!cxDesired) cxDesired = entry->bWidth;
1413     if(!cyDesired) cyDesired = entry->bHeight;
1414     /* A bit of preparation */
1415     ZeroMemory(&cursorData, sizeof(cursorData));
1416     if(!bIcon)
1417     {
1418         cursorData.xHotspot = entry->xHotspot;
1419         cursorData.yHotspot = entry->yHotspot;
1420     }
1421     cursorData.rt = (USHORT)((ULONG_PTR)(bIcon ? RT_ICON : RT_CURSOR));
1422 
1423     /* Do the dance */
1424     if(!CURSORICON_GetCursorDataFromBMI(&cursorData, (BITMAPINFO*)(&bits[entry->dwDIBOffset])))
1425         {
1426             ERR("Failing File is \n    '%S'.\n", lpszName);
1427             goto end;
1428         }
1429 
1430     hCurIcon = NtUserxCreateEmptyCurObject(FALSE);
1431     if(!hCurIcon)
1432         goto end;
1433 
1434     /* Tell win32k */
1435     if(!NtUserSetCursorIconData(hCurIcon, NULL, NULL, &cursorData))
1436     {
1437         NtUserDestroyCursor(hCurIcon, TRUE);
1438         goto end_error;
1439     }
1440 
1441 end:
1442     UnmapViewOfFile(bits);
1443     return hCurIcon;
1444 
1445     /* Clean up */
1446 end_error:
1447     DeleteObject(cursorData.hbmMask);
1448     if(cursorData.hbmColor) DeleteObject(cursorData.hbmColor);
1449     if(cursorData.hbmAlpha) DeleteObject(cursorData.hbmAlpha);
1450     UnmapViewOfFile(bits);
1451 
1452     return NULL;
1453 }
1454 
1455 static
1456 HANDLE
CURSORICON_LoadImageW(_In_opt_ HINSTANCE hinst,_In_ LPCWSTR lpszName,_In_ int cxDesired,_In_ int cyDesired,_In_ UINT fuLoad,_In_ BOOL bIcon)1457 CURSORICON_LoadImageW(
1458   _In_opt_  HINSTANCE hinst,
1459   _In_      LPCWSTR lpszName,
1460   _In_      int cxDesired,
1461   _In_      int cyDesired,
1462   _In_      UINT fuLoad,
1463   _In_      BOOL bIcon
1464 )
1465 {
1466     HRSRC hrsrc;
1467     HANDLE handle, hCurIcon = NULL;
1468     CURSORICONDIR* dir;
1469     WORD wResId;
1470     LPBYTE bits;
1471     CURSORDATA cursorData;
1472     BOOL bStatus;
1473     UNICODE_STRING ustrRsrc;
1474     UNICODE_STRING ustrModule = {0, 0, NULL};
1475 
1476     /* Fix width/height */
1477     if(fuLoad & LR_DEFAULTSIZE)
1478     {
1479         if(!cxDesired) cxDesired = GetSystemMetrics(bIcon ? SM_CXICON : SM_CXCURSOR);
1480         if(!cyDesired) cyDesired = GetSystemMetrics(bIcon ? SM_CYICON : SM_CYCURSOR);
1481     }
1482 
1483     if(fuLoad & LR_LOADFROMFILE)
1484     {
1485         return CURSORICON_LoadFromFileW(lpszName, cxDesired, cyDesired, fuLoad, bIcon);
1486     }
1487 
1488     /* Check if caller wants OEM icons */
1489     if(!hinst)
1490         hinst = User32Instance;
1491 
1492     if(lpszName)
1493     {
1494         /* Prepare the resource name string */
1495         if(IS_INTRESOURCE(lpszName))
1496         {
1497             ustrRsrc.Buffer = (LPWSTR)lpszName;
1498             ustrRsrc.Length = 0;
1499             ustrRsrc.MaximumLength = 0;
1500         }
1501         else
1502             RtlInitUnicodeString(&ustrRsrc, lpszName);
1503     }
1504 
1505     if(LDR_IS_RESOURCE(hinst))
1506     {
1507         /* We don't have a real module for GetModuleFileName, construct a fake name instead.
1508          * GetIconInfoEx reveals the name used by Windows. */
1509         LPCWSTR fakeNameFmt = sizeof(void*) > 4 ? L"\x01%016IX" : L"\x01%08IX";
1510         ustrModule.MaximumLength = 18 * sizeof(WCHAR);
1511         ustrModule.Buffer = HeapAlloc(GetProcessHeap(), 0, ustrModule.MaximumLength);
1512         if (!ustrModule.Buffer)
1513         {
1514             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1515             return NULL;
1516         }
1517         ustrModule.Length = wsprintfW(ustrModule.Buffer, fakeNameFmt, hinst) * sizeof(WCHAR);
1518     }
1519     else if(hinst)
1520     {
1521         DWORD size = MAX_PATH;
1522         /* Get the module name string */
1523         while (TRUE)
1524         {
1525             DWORD ret;
1526             ustrModule.Buffer = HeapAlloc(GetProcessHeap(), 0, size*sizeof(WCHAR));
1527             if (!ustrModule.Buffer)
1528             {
1529                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1530                 return NULL;
1531             }
1532             ret = GetModuleFileNameW(hinst, ustrModule.Buffer, size);
1533             if(ret == 0)
1534             {
1535                 HeapFree(GetProcessHeap(), 0, ustrModule.Buffer);
1536                 return NULL;
1537             }
1538 
1539             /* This API is completely broken... */
1540             if (ret == size)
1541             {
1542                 HeapFree(GetProcessHeap(), 0, ustrModule.Buffer);
1543                 size *= 2;
1544                 continue;
1545             }
1546 
1547             ustrModule.Buffer[ret] = UNICODE_NULL;
1548             ustrModule.Length = ret * sizeof(WCHAR);
1549             ustrModule.MaximumLength = size * sizeof(WCHAR);
1550             break;
1551         }
1552     }
1553 
1554     if(fuLoad & LR_SHARED)
1555     {
1556         FINDEXISTINGCURICONPARAM param;
1557 
1558         TRACE("Checking for an LR_SHARED cursor/icon.\n");
1559         /* Ask win32k */
1560         param.bIcon = bIcon;
1561         param.cx = cxDesired;
1562         param.cy = cyDesired;
1563         hCurIcon = NtUserFindExistingCursorIcon(&ustrModule, &ustrRsrc, &param);
1564         if(hCurIcon)
1565         {
1566             /* Woohoo, got it! */
1567             TRACE("MATCH! %p\n",hCurIcon);
1568             HeapFree(GetProcessHeap(), 0, ustrModule.Buffer);
1569             return hCurIcon;
1570         }
1571     }
1572 
1573     /* Find resource ID */
1574     hrsrc = FindResourceW(
1575         hinst,
1576         lpszName,
1577         bIcon ? RT_GROUP_ICON : RT_GROUP_CURSOR);
1578 
1579     /* We let FindResource, LoadResource, etc. call SetLastError */
1580     if(!hrsrc)
1581         goto done;
1582 
1583     handle = LoadResource(hinst, hrsrc);
1584     if(!handle)
1585         goto done;
1586 
1587     dir = LockResource(handle);
1588     if(!dir)
1589         goto done;
1590 
1591     wResId = LookupIconIdFromDirectoryEx((PBYTE)dir, bIcon, cxDesired, cyDesired, fuLoad);
1592     FreeResource(handle);
1593 
1594     /* Get the relevant resource pointer */
1595     hrsrc = FindResourceW(
1596         hinst,
1597         MAKEINTRESOURCEW(wResId),
1598         bIcon ? RT_ICON : RT_CURSOR);
1599     if(!hrsrc)
1600         goto done;
1601 
1602     handle = LoadResource(hinst, hrsrc);
1603     if(!handle)
1604         goto done;
1605 
1606     bits = LockResource(handle);
1607     if(!bits)
1608     {
1609         FreeResource(handle);
1610         goto done;
1611     }
1612 
1613     ZeroMemory(&cursorData, sizeof(cursorData));
1614 
1615     /* This is from resource */
1616     cursorData.CURSORF_flags = CURSORF_FROMRESOURCE;
1617 
1618     if(dir->idType == 2)
1619     {
1620         /* idType == 2 for cursor resources */
1621         SHORT* ptr = (SHORT*)bits;
1622         cursorData.xHotspot = ptr[0];
1623         cursorData.yHotspot = ptr[1];
1624         bits += 2*sizeof(SHORT);
1625     }
1626     cursorData.cx = cxDesired;
1627     cursorData.cy = cyDesired;
1628     cursorData.rt = (USHORT)((ULONG_PTR)(bIcon ? RT_ICON : RT_CURSOR));
1629 
1630     /* Get the bitmaps */
1631     bStatus = CURSORICON_GetCursorDataFromBMI(
1632         &cursorData,
1633         (BITMAPINFO*)bits);
1634 
1635     FreeResource( handle );
1636 
1637     if(!bStatus)
1638         goto done;
1639 
1640     /* Create the handle */
1641     hCurIcon = NtUserxCreateEmptyCurObject(FALSE);
1642     if(!hCurIcon)
1643     {
1644         goto end_error;
1645     }
1646 
1647     if(fuLoad & LR_SHARED)
1648     {
1649         cursorData.CURSORF_flags |= CURSORF_LRSHARED;
1650     }
1651 
1652     /* Tell win32k */
1653     bStatus = NtUserSetCursorIconData(hCurIcon, hinst ? &ustrModule : NULL, lpszName ? &ustrRsrc : NULL, &cursorData);
1654 
1655     if(!bStatus)
1656     {
1657         NtUserDestroyCursor(hCurIcon, TRUE);
1658         goto end_error;
1659     }
1660 
1661 done:
1662     if(ustrModule.Buffer)
1663         HeapFree(GetProcessHeap(), 0, ustrModule.Buffer);
1664     return hCurIcon;
1665 
1666 end_error:
1667     if(ustrModule.Buffer)
1668         HeapFree(GetProcessHeap(), 0, ustrModule.Buffer);
1669     DeleteObject(cursorData.hbmMask);
1670     if(cursorData.hbmColor) DeleteObject(cursorData.hbmColor);
1671     if(cursorData.hbmAlpha) DeleteObject(cursorData.hbmAlpha);
1672 
1673     return NULL;
1674 }
1675 
1676 static
1677 HBITMAP
BITMAP_CopyImage(_In_ HBITMAP hnd,_In_ int desiredx,_In_ int desiredy,_In_ UINT flags)1678 BITMAP_CopyImage(
1679   _In_  HBITMAP hnd,
1680   _In_  int desiredx,
1681   _In_  int desiredy,
1682   _In_  UINT flags
1683 )
1684 {
1685     HBITMAP res = NULL;
1686     DIBSECTION ds;
1687     int objSize;
1688     BITMAPINFO * bi;
1689 
1690     objSize = GetObjectW( hnd, sizeof(ds), &ds );
1691     if (!objSize) return 0;
1692     if ((desiredx < 0) || (desiredy < 0)) return 0;
1693 
1694     if (flags & LR_COPYFROMRESOURCE)
1695     {
1696         FIXME("The flag LR_COPYFROMRESOURCE is not implemented for bitmaps\n");
1697     }
1698 
1699     if (flags & LR_COPYRETURNORG)
1700     {
1701         FIXME("The flag LR_COPYRETURNORG is not implemented for bitmaps\n");
1702     }
1703 
1704     if (desiredx == 0) desiredx = ds.dsBm.bmWidth;
1705     if (desiredy == 0) desiredy = ds.dsBm.bmHeight;
1706 
1707     /* Allocate memory for a BITMAPINFOHEADER structure and a
1708        color table. The maximum number of colors in a color table
1709        is 256 which corresponds to a bitmap with depth 8.
1710        Bitmaps with higher depths don't have color tables. */
1711     bi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
1712     if (!bi) return 0;
1713 
1714     bi->bmiHeader.biSize        = sizeof(bi->bmiHeader);
1715     bi->bmiHeader.biPlanes      = ds.dsBm.bmPlanes;
1716     bi->bmiHeader.biBitCount    = ds.dsBm.bmBitsPixel;
1717     bi->bmiHeader.biCompression = BI_RGB;
1718 
1719     if (flags & LR_CREATEDIBSECTION)
1720     {
1721         /* Create a DIB section. LR_MONOCHROME is ignored */
1722         void * bits;
1723         HDC dc = CreateCompatibleDC(NULL);
1724 
1725         if (objSize == sizeof(DIBSECTION))
1726         {
1727             /* The source bitmap is a DIB.
1728                Get its attributes to create an exact copy */
1729             memcpy(bi, &ds.dsBmih, sizeof(BITMAPINFOHEADER));
1730         }
1731 
1732         bi->bmiHeader.biWidth  = desiredx;
1733         bi->bmiHeader.biHeight = desiredy;
1734 
1735         /* Get the color table or the color masks */
1736         GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
1737 
1738         res = CreateDIBSection(dc, bi, DIB_RGB_COLORS, &bits, NULL, 0);
1739         DeleteDC(dc);
1740     }
1741     else
1742     {
1743         /* Create a device-dependent bitmap */
1744 
1745         BOOL monochrome = (flags & LR_MONOCHROME);
1746 
1747         if (objSize == sizeof(DIBSECTION))
1748         {
1749             /* The source bitmap is a DIB section.
1750                Get its attributes */
1751             HDC dc = CreateCompatibleDC(NULL);
1752             bi->bmiHeader.biWidth  = ds.dsBm.bmWidth;
1753             bi->bmiHeader.biHeight = ds.dsBm.bmHeight;
1754             GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
1755             DeleteDC(dc);
1756 
1757             if (!monochrome && ds.dsBm.bmBitsPixel == 1)
1758             {
1759                 /* Look if the colors of the DIB are black and white */
1760 
1761                 monochrome =
1762                       (bi->bmiColors[0].rgbRed == 0xff
1763                     && bi->bmiColors[0].rgbGreen == 0xff
1764                     && bi->bmiColors[0].rgbBlue == 0xff
1765                     && bi->bmiColors[0].rgbReserved == 0
1766                     && bi->bmiColors[1].rgbRed == 0
1767                     && bi->bmiColors[1].rgbGreen == 0
1768                     && bi->bmiColors[1].rgbBlue == 0
1769                     && bi->bmiColors[1].rgbReserved == 0)
1770                     ||
1771                       (bi->bmiColors[0].rgbRed == 0
1772                     && bi->bmiColors[0].rgbGreen == 0
1773                     && bi->bmiColors[0].rgbBlue == 0
1774                     && bi->bmiColors[0].rgbReserved == 0
1775                     && bi->bmiColors[1].rgbRed == 0xff
1776                     && bi->bmiColors[1].rgbGreen == 0xff
1777                     && bi->bmiColors[1].rgbBlue == 0xff
1778                     && bi->bmiColors[1].rgbReserved == 0);
1779             }
1780         }
1781         else if (!monochrome)
1782         {
1783             monochrome = ds.dsBm.bmBitsPixel == 1;
1784         }
1785 
1786         if (monochrome)
1787         {
1788             res = CreateBitmap(desiredx, desiredy, 1, 1, NULL);
1789         }
1790         else
1791         {
1792             HDC screenDC = GetDC(NULL);
1793             res = CreateCompatibleBitmap(screenDC, desiredx, desiredy);
1794             ReleaseDC(NULL, screenDC);
1795         }
1796     }
1797 
1798     if (res)
1799     {
1800         /* Only copy the bitmap if it's a DIB section or if it's
1801            compatible to the screen */
1802         BOOL copyContents;
1803 
1804         if (objSize == sizeof(DIBSECTION))
1805         {
1806             copyContents = TRUE;
1807         }
1808         else
1809         {
1810             HDC screenDC = GetDC(NULL);
1811             int screen_depth = GetDeviceCaps(screenDC, BITSPIXEL);
1812             ReleaseDC(NULL, screenDC);
1813 
1814             copyContents = (ds.dsBm.bmBitsPixel == 1 || ds.dsBm.bmBitsPixel == screen_depth);
1815         }
1816 
1817         if (copyContents)
1818         {
1819             /* The source bitmap may already be selected in a device context,
1820                use GetDIBits/StretchDIBits and not StretchBlt  */
1821 
1822             HDC dc;
1823             void * bits;
1824 
1825             dc = CreateCompatibleDC(NULL);
1826 
1827             bi->bmiHeader.biWidth = ds.dsBm.bmWidth;
1828             bi->bmiHeader.biHeight = ds.dsBm.bmHeight;
1829             bi->bmiHeader.biSizeImage = 0;
1830             bi->bmiHeader.biClrUsed = 0;
1831             bi->bmiHeader.biClrImportant = 0;
1832 
1833             /* Fill in biSizeImage */
1834             GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
1835             bits = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bi->bmiHeader.biSizeImage);
1836 
1837             if (bits)
1838             {
1839                 HBITMAP oldBmp;
1840 
1841                 /* Get the image bits of the source bitmap */
1842                 GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, bits, bi, DIB_RGB_COLORS);
1843 
1844                 /* Copy it to the destination bitmap */
1845                 oldBmp = SelectObject(dc, res);
1846                 StretchDIBits(dc, 0, 0, desiredx, desiredy,
1847                               0, 0, ds.dsBm.bmWidth, ds.dsBm.bmHeight,
1848                               bits, bi, DIB_RGB_COLORS, SRCCOPY);
1849                 SelectObject(dc, oldBmp);
1850 
1851                 HeapFree(GetProcessHeap(), 0, bits);
1852             }
1853 
1854             DeleteDC(dc);
1855         }
1856 
1857         if (flags & LR_COPYDELETEORG)
1858         {
1859             DeleteObject(hnd);
1860         }
1861     }
1862     HeapFree(GetProcessHeap(), 0, bi);
1863     return res;
1864 }
1865 
1866 static
1867 HICON
CURSORICON_CopyImage(_In_ HICON hicon,_In_ BOOL bIcon,_In_ int cxDesired,_In_ int cyDesired,_In_ UINT fuFlags)1868 CURSORICON_CopyImage(
1869   _In_  HICON hicon,
1870   _In_  BOOL  bIcon,
1871   _In_  int cxDesired,
1872   _In_  int cyDesired,
1873   _In_  UINT fuFlags
1874 )
1875 {
1876     HICON ret = NULL;
1877     ICONINFO ii;
1878     CURSORDATA CursorData;
1879 
1880     if (fuFlags & LR_COPYFROMRESOURCE)
1881     {
1882         /* Get the icon module/resource names */
1883         UNICODE_STRING ustrModule;
1884         UNICODE_STRING ustrRsrc;
1885         HMODULE hModule;
1886 
1887         ustrModule.MaximumLength = 0;
1888         ustrRsrc.MaximumLength = 0;
1889 
1890         /* Get the buffer size */
1891         if (!NtUserGetIconInfo(hicon, NULL, &ustrModule, &ustrRsrc, NULL, FALSE))
1892         {
1893             return NULL;
1894         }
1895 
1896         ustrModule.Buffer = HeapAlloc(GetProcessHeap(), 0, ustrModule.MaximumLength);
1897         if (!ustrModule.Buffer)
1898         {
1899             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1900             return NULL;
1901         }
1902 
1903         if (ustrRsrc.MaximumLength)
1904         {
1905             ustrRsrc.Buffer = HeapAlloc(GetProcessHeap(), 0, ustrRsrc.MaximumLength);
1906             if (!ustrRsrc.Buffer)
1907             {
1908                 HeapFree(GetProcessHeap(), 0, ustrModule.Buffer);
1909                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1910                 return NULL;
1911             }
1912         }
1913 
1914         if (!NtUserGetIconInfo(hicon, NULL, &ustrModule, &ustrRsrc, NULL, FALSE))
1915         {
1916             HeapFree(GetProcessHeap(), 0, ustrModule.Buffer);
1917             if (!IS_INTRESOURCE(ustrRsrc.Buffer))
1918                 HeapFree(GetProcessHeap(), 0, ustrRsrc.Buffer);
1919             return NULL;
1920         }
1921 
1922         /* NULL-terminate our strings */
1923         ustrModule.Buffer[ustrModule.Length/sizeof(WCHAR)] = UNICODE_NULL;
1924         if (!IS_INTRESOURCE(ustrRsrc.Buffer))
1925             ustrRsrc.Buffer[ustrRsrc.Length/sizeof(WCHAR)] = UNICODE_NULL;
1926 
1927         TRACE("Got module %wZ, resource %p (%S).\n", &ustrModule,
1928             ustrRsrc.Buffer, IS_INTRESOURCE(ustrRsrc.Buffer) ? L"" : ustrRsrc.Buffer);
1929 
1930         /* Get the module handle or load the module */
1931         hModule = LoadLibraryExW(ustrModule.Buffer, NULL, /* NT6+: LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE);
1932         if (!hModule)
1933         {
1934             DWORD err = GetLastError();
1935             ERR("Unable to load/use module '%wZ' in process %lu, error %lu.\n", &ustrModule, GetCurrentProcessId(), err);
1936             SetLastError(ERROR_INVALID_PARAMETER);
1937             goto leave;
1938         }
1939 
1940         /* Call the relevant function */
1941         ret = CURSORICON_LoadImageW(
1942             hModule,
1943             ustrRsrc.Buffer,
1944             cxDesired,
1945             cyDesired,
1946             fuFlags & (LR_DEFAULTSIZE | LR_SHARED),
1947             bIcon);
1948 
1949         FreeLibrary(hModule);
1950 
1951         /* If we're here, that means that the passed icon is shared. Don't destroy it, even if LR_COPYDELETEORG is specified */
1952     leave:
1953         HeapFree(GetProcessHeap(), 0, ustrModule.Buffer);
1954         if (!IS_INTRESOURCE(ustrRsrc.Buffer))
1955             HeapFree(GetProcessHeap(), 0, ustrRsrc.Buffer);
1956 
1957         TRACE("Returning 0x%08x.\n", ret);
1958 
1959         return ret;
1960     }
1961 
1962     /* This is a regular copy */
1963     if (fuFlags & ~(LR_COPYDELETEORG | LR_SHARED))
1964         FIXME("Unimplemented flags: 0x%08x\n", fuFlags);
1965 
1966     if (!GetIconInfo(hicon, &ii))
1967     {
1968         ERR("GetIconInfo failed.\n");
1969         return NULL;
1970     }
1971 
1972     /* This is CreateIconIndirect with the LR_SHARED coat added */
1973     if  (!CURSORICON_GetCursorDataFromIconInfo(&CursorData, &ii))
1974         goto Leave;
1975 
1976     if (fuFlags & LR_SHARED)
1977         CursorData.CURSORF_flags |= CURSORF_LRSHARED;
1978 
1979     ret = NtUserxCreateEmptyCurObject(FALSE);
1980     if (!ret)
1981         goto Leave;
1982 
1983     if (!NtUserSetCursorIconData(ret, NULL, NULL, &CursorData))
1984     {
1985         NtUserDestroyCursor(ret, TRUE);
1986         goto Leave;
1987     }
1988 
1989 Leave:
1990     DeleteObject(ii.hbmMask);
1991     if (ii.hbmColor) DeleteObject(ii.hbmColor);
1992 
1993     if (ret && (fuFlags & LR_COPYDELETEORG))
1994         DestroyIcon(hicon);
1995 
1996     return ret;
1997 }
1998 
1999 NTSTATUS WINAPI
User32CallCopyImageFromKernel(PVOID Arguments,ULONG ArgumentLength)2000 User32CallCopyImageFromKernel(PVOID Arguments, ULONG ArgumentLength)
2001 {
2002   PCOPYIMAGE_CALLBACK_ARGUMENTS Common;
2003   HANDLE Result;
2004   Common = (PCOPYIMAGE_CALLBACK_ARGUMENTS) Arguments;
2005 
2006   Result = CopyImage(Common->hImage,
2007                      Common->uType,
2008                      Common->cxDesired,
2009                      Common->cyDesired,
2010                      Common->fuFlags);
2011 
2012   return ZwCallbackReturn(&Result, sizeof(HANDLE), STATUS_SUCCESS);
2013 }
2014 
2015 
2016 /************* PUBLIC FUNCTIONS *******************/
2017 
2018 #define COPYIMAGE_VALID_FLAGS ( \
2019     LR_SHARED | LR_COPYFROMRESOURCE | LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS | 0x800 | \
2020     LR_VGACOLOR | LR_LOADREALSIZE | LR_DEFAULTSIZE | LR_LOADTRANSPARENT | LR_LOADFROMFILE | \
2021     LR_COPYDELETEORG | LR_COPYRETURNORG | LR_COLOR | LR_MONOCHROME \
2022 )
2023 
CopyImage(_In_ HANDLE hImage,_In_ UINT uType,_In_ int cxDesired,_In_ int cyDesired,_In_ UINT fuFlags)2024 HANDLE WINAPI CopyImage(
2025   _In_  HANDLE hImage,
2026   _In_  UINT uType,
2027   _In_  int cxDesired,
2028   _In_  int cyDesired,
2029   _In_  UINT fuFlags
2030 )
2031 {
2032     TRACE("hImage=%p, uType=%u, cxDesired=%d, cyDesired=%d, fuFlags=%x\n",
2033         hImage, uType, cxDesired, cyDesired, fuFlags);
2034 
2035     if (fuFlags & ~COPYIMAGE_VALID_FLAGS)
2036     {
2037         SetLastError(ERROR_INVALID_PARAMETER);
2038         return NULL;
2039     }
2040 
2041     switch(uType)
2042     {
2043         case IMAGE_BITMAP:
2044             return BITMAP_CopyImage(hImage, cxDesired, cyDesired, fuFlags);
2045         case IMAGE_CURSOR:
2046         case IMAGE_ICON:
2047         /* HACK: Copying bitmaps with LR_COPYFROMRESOURCE flag fails. CORE-17902.
2048          * This is a way to return the original bit map if we need
2049          * the icons to show up. We need a simpler test. */
2050         {
2051             HANDLE handle = CURSORICON_CopyImage(hImage, uType == IMAGE_ICON, cxDesired, cyDesired, fuFlags);
2052             if (!handle && (fuFlags & (LR_COPYFROMRESOURCE|LR_COPYRETURNORG)))
2053                 handle = CURSORICON_CopyImage(hImage, uType == IMAGE_ICON, cxDesired, cyDesired, (fuFlags & ~LR_COPYFROMRESOURCE));
2054             return handle;
2055         }
2056         default:
2057             SetLastError(ERROR_INVALID_PARAMETER);
2058             break;
2059     }
2060     return NULL;
2061 }
2062 
CopyIcon(_In_ HICON hIcon)2063 HICON WINAPI CopyIcon(
2064   _In_  HICON hIcon
2065 )
2066 {
2067     return CURSORICON_CopyImage(hIcon, FALSE, 0, 0, 0);
2068 }
2069 
DrawIcon(_In_ HDC hDC,_In_ int X,_In_ int Y,_In_ HICON hIcon)2070 BOOL WINAPI DrawIcon(
2071   _In_  HDC hDC,
2072   _In_  int X,
2073   _In_  int Y,
2074   _In_  HICON hIcon
2075 )
2076 {
2077     return DrawIconEx(hDC, X, Y, hIcon, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT | DI_DEFAULTSIZE);
2078 }
2079 
DrawIconEx(_In_ HDC hdc,_In_ int xLeft,_In_ int yTop,_In_ HICON hIcon,_In_ int cxWidth,_In_ int cyWidth,_In_ UINT istepIfAniCur,_In_opt_ HBRUSH hbrFlickerFreeDraw,_In_ UINT diFlags)2080 BOOL WINAPI DrawIconEx(
2081   _In_      HDC hdc,
2082   _In_      int xLeft,
2083   _In_      int yTop,
2084   _In_      HICON hIcon,
2085   _In_      int cxWidth,
2086   _In_      int cyWidth,
2087   _In_      UINT istepIfAniCur,
2088   _In_opt_  HBRUSH hbrFlickerFreeDraw,
2089   _In_      UINT diFlags
2090 )
2091 {
2092     return NtUserDrawIconEx(hdc, xLeft, yTop, hIcon, cxWidth, cyWidth,
2093                             istepIfAniCur, hbrFlickerFreeDraw, diFlags,
2094                             0, 0);
2095 }
2096 
GetIconInfo(_In_ HICON hIcon,_Out_ PICONINFO piconinfo)2097 BOOL WINAPI GetIconInfo(
2098   _In_   HICON hIcon,
2099   _Out_  PICONINFO piconinfo
2100 )
2101 {
2102     return NtUserGetIconInfo(hIcon, piconinfo, NULL, NULL, NULL, FALSE);
2103 }
2104 
DestroyIcon(_In_ HICON hIcon)2105 BOOL WINAPI DestroyIcon(
2106   _In_  HICON hIcon
2107 )
2108 {
2109     return NtUserDestroyCursor(hIcon, FALSE);
2110 }
2111 
LoadIconA(_In_opt_ HINSTANCE hInstance,_In_ LPCSTR lpIconName)2112 HICON WINAPI LoadIconA(
2113   _In_opt_  HINSTANCE hInstance,
2114   _In_      LPCSTR lpIconName
2115 )
2116 {
2117     TRACE("%p, %s\n", hInstance, debugstr_a(lpIconName));
2118 
2119     return LoadImageA(hInstance,
2120         lpIconName,
2121         IMAGE_ICON,
2122         0,
2123         0,
2124         LR_SHARED | LR_DEFAULTSIZE );
2125 }
2126 
LoadIconW(_In_opt_ HINSTANCE hInstance,_In_ LPCWSTR lpIconName)2127 HICON WINAPI LoadIconW(
2128   _In_opt_  HINSTANCE hInstance,
2129   _In_      LPCWSTR lpIconName
2130 )
2131 {
2132     TRACE("%p, %s\n", hInstance, debugstr_w(lpIconName));
2133 
2134     return LoadImageW(hInstance,
2135         lpIconName,
2136         IMAGE_ICON,
2137         0,
2138         0,
2139         LR_SHARED | LR_DEFAULTSIZE );
2140 }
2141 
LoadCursorA(_In_opt_ HINSTANCE hInstance,_In_ LPCSTR lpCursorName)2142 HCURSOR WINAPI LoadCursorA(
2143   _In_opt_  HINSTANCE hInstance,
2144   _In_      LPCSTR    lpCursorName
2145 )
2146 {
2147     TRACE("%p, %s\n", hInstance, debugstr_a(lpCursorName));
2148 
2149     return LoadImageA(hInstance,
2150         lpCursorName,
2151         IMAGE_CURSOR,
2152         0,
2153         0,
2154         LR_SHARED | LR_DEFAULTSIZE );
2155 }
2156 
LoadCursorW(_In_opt_ HINSTANCE hInstance,_In_ LPCWSTR lpCursorName)2157 HCURSOR WINAPI LoadCursorW(
2158   _In_opt_  HINSTANCE hInstance,
2159   _In_      LPCWSTR   lpCursorName
2160 )
2161 {
2162     TRACE("%p, %s\n", hInstance, debugstr_w(lpCursorName));
2163 
2164     return LoadImageW(hInstance,
2165         lpCursorName,
2166         IMAGE_CURSOR,
2167         0,
2168         0,
2169         LR_SHARED | LR_DEFAULTSIZE );
2170 }
2171 
LoadCursorFromFileA(_In_ LPCSTR lpFileName)2172 HCURSOR WINAPI LoadCursorFromFileA(
2173   _In_  LPCSTR lpFileName
2174 )
2175 {
2176     TRACE("%s\n", debugstr_a(lpFileName));
2177 
2178     return LoadImageA(NULL,
2179         lpFileName,
2180         IMAGE_CURSOR,
2181         0,
2182         0,
2183         LR_LOADFROMFILE | LR_DEFAULTSIZE );
2184 }
2185 
LoadCursorFromFileW(_In_ LPCWSTR lpFileName)2186 HCURSOR WINAPI LoadCursorFromFileW(
2187   _In_  LPCWSTR lpFileName
2188 )
2189 {
2190     TRACE("%s\n", debugstr_w(lpFileName));
2191 
2192     return LoadImageW(NULL,
2193         lpFileName,
2194         IMAGE_CURSOR,
2195         0,
2196         0,
2197         LR_LOADFROMFILE | LR_DEFAULTSIZE );
2198 }
2199 
LoadBitmapA(_In_opt_ HINSTANCE hInstance,_In_ LPCSTR lpBitmapName)2200 HBITMAP WINAPI LoadBitmapA(
2201   _In_opt_  HINSTANCE hInstance,
2202   _In_      LPCSTR lpBitmapName
2203 )
2204 {
2205     TRACE("%p, %s\n", hInstance, debugstr_a(lpBitmapName));
2206 
2207     return LoadImageA(hInstance,
2208         lpBitmapName,
2209         IMAGE_BITMAP,
2210         0,
2211         0,
2212         0);
2213 }
2214 
LoadBitmapW(_In_opt_ HINSTANCE hInstance,_In_ LPCWSTR lpBitmapName)2215 HBITMAP WINAPI LoadBitmapW(
2216   _In_opt_  HINSTANCE hInstance,
2217   _In_      LPCWSTR lpBitmapName
2218 )
2219 {
2220     TRACE("%p, %s\n", hInstance, debugstr_w(lpBitmapName));
2221 
2222     return LoadImageW(hInstance,
2223         lpBitmapName,
2224         IMAGE_BITMAP,
2225         0,
2226         0,
2227         0);
2228 }
2229 
LoadImageA(_In_opt_ HINSTANCE hinst,_In_ LPCSTR lpszName,_In_ UINT uType,_In_ int cxDesired,_In_ int cyDesired,_In_ UINT fuLoad)2230 HANDLE WINAPI LoadImageA(
2231   _In_opt_  HINSTANCE hinst,
2232   _In_      LPCSTR lpszName,
2233   _In_      UINT uType,
2234   _In_      int cxDesired,
2235   _In_      int cyDesired,
2236   _In_      UINT fuLoad
2237 )
2238 {
2239     HANDLE res;
2240     LPWSTR u_name;
2241     DWORD len;
2242 
2243     if (IS_INTRESOURCE(lpszName))
2244         return LoadImageW(hinst, (LPCWSTR)lpszName, uType, cxDesired, cyDesired, fuLoad);
2245 
2246     len = MultiByteToWideChar( CP_ACP, 0, lpszName, -1, NULL, 0 );
2247     u_name = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
2248     MultiByteToWideChar( CP_ACP, 0, lpszName, -1, u_name, len );
2249 
2250     res = LoadImageW(hinst, u_name, uType, cxDesired, cyDesired, fuLoad);
2251     HeapFree(GetProcessHeap(), 0, u_name);
2252     return res;
2253 }
2254 
LoadImageW(_In_opt_ HINSTANCE hinst,_In_ LPCWSTR lpszName,_In_ UINT uType,_In_ int cxDesired,_In_ int cyDesired,_In_ UINT fuLoad)2255 HANDLE WINAPI LoadImageW(
2256   _In_opt_  HINSTANCE hinst,
2257   _In_      LPCWSTR lpszName,
2258   _In_      UINT uType,
2259   _In_      int cxDesired,
2260   _In_      int cyDesired,
2261   _In_      UINT fuLoad
2262 )
2263 {
2264     TRACE("hinst 0x%p, name %s, uType 0x%08x, cxDesired %d, cyDesired %d, fuLoad 0x%08x.\n",
2265         hinst, debugstr_w(lpszName), uType, cxDesired, cyDesired, fuLoad);
2266     /* Redirect to each implementation */
2267     switch(uType)
2268     {
2269         case IMAGE_BITMAP:
2270             return BITMAP_LoadImageW(hinst, lpszName, cxDesired, cyDesired, fuLoad);
2271         case IMAGE_CURSOR:
2272         case IMAGE_ICON:
2273             return CURSORICON_LoadImageW(hinst, lpszName, cxDesired, cyDesired, fuLoad, uType == IMAGE_ICON);
2274         default:
2275             SetLastError(ERROR_INVALID_PARAMETER);
2276             break;
2277     }
2278     return NULL;
2279 }
2280 
LookupIconIdFromDirectory(_In_ PBYTE presbits,_In_ BOOL fIcon)2281 int WINAPI LookupIconIdFromDirectory(
2282   _In_  PBYTE presbits,
2283   _In_  BOOL fIcon
2284 )
2285 {
2286     return LookupIconIdFromDirectoryEx( presbits, fIcon,
2287            fIcon ? GetSystemMetrics(SM_CXICON) : GetSystemMetrics(SM_CXCURSOR),
2288            fIcon ? GetSystemMetrics(SM_CYICON) : GetSystemMetrics(SM_CYCURSOR), fIcon ? 0 : LR_MONOCHROME );
2289 }
2290 
LookupIconIdFromDirectoryEx(_In_ PBYTE presbits,_In_ BOOL fIcon,_In_ int cxDesired,_In_ int cyDesired,_In_ UINT Flags)2291 int WINAPI LookupIconIdFromDirectoryEx(
2292   _In_  PBYTE presbits,
2293   _In_  BOOL fIcon,
2294   _In_  int cxDesired,
2295   _In_  int cyDesired,
2296   _In_  UINT Flags
2297 )
2298 {
2299     WORD bppDesired;
2300     CURSORICONDIR* dir = (CURSORICONDIR*)presbits;
2301     CURSORICONDIRENTRY* entry;
2302     int i, numMatch = 0, iIndex = -1;
2303     WORD width, height, BitCount = 0;
2304     BOOL notPaletted = FALSE;
2305     ULONG bestScore = 0xFFFFFFFF, score;
2306 
2307     TRACE("%p, %x, %i, %i, %x.\n", presbits, fIcon, cxDesired, cyDesired, Flags);
2308 
2309     if(!(dir && !dir->idReserved && (dir->idType & 3)))
2310     {
2311         WARN("Invalid resource.\n");
2312         return 0;
2313     }
2314 
2315     if(Flags & LR_MONOCHROME)
2316         bppDesired = 1;
2317     else
2318     {
2319         HDC icScreen;
2320         icScreen = CreateICW(DISPLAYW, NULL, NULL, NULL);
2321         if(!icScreen)
2322             return FALSE;
2323 
2324         bppDesired = GetDeviceCaps(icScreen, BITSPIXEL);
2325         DeleteDC(icScreen);
2326     }
2327 
2328     if(!cxDesired)
2329         cxDesired = Flags & LR_DEFAULTSIZE ? GetSystemMetrics(fIcon ? SM_CXICON : SM_CXCURSOR) : 256;
2330     if(!cyDesired)
2331         cyDesired = Flags & LR_DEFAULTSIZE ? GetSystemMetrics(fIcon ? SM_CYICON : SM_CYCURSOR) : 256;
2332 
2333     /* Find the best match for the desired size */
2334     for(i = 0; i < dir->idCount; i++)
2335     {
2336         entry = &dir->idEntries[i];
2337         width = fIcon ? entry->ResInfo.icon.bWidth : entry->ResInfo.cursor.wWidth;
2338         /* Height is twice as big in cursor resources */
2339         height = fIcon ? entry->ResInfo.icon.bHeight : entry->ResInfo.cursor.wHeight/2;
2340         /* 0 represents 256 */
2341         if(!width) width = 256;
2342         if(!height) height = 256;
2343         /* Calculate the "score" (lower is better) */
2344         score = 2*(abs(width - cxDesired) + abs(height - cyDesired));
2345         if( score > bestScore)
2346             continue;
2347         /* Bigger than requested lowers the score */
2348         if(width > cxDesired)
2349             score -= width - cxDesired;
2350         if(height > cyDesired)
2351             score -= height - cyDesired;
2352         if(score > bestScore)
2353             continue;
2354         if(score == bestScore)
2355         {
2356             if(entry->wBitCount > BitCount)
2357                 BitCount = entry->wBitCount;
2358             numMatch++;
2359             continue;
2360         }
2361         iIndex = i;
2362         numMatch = 1;
2363         bestScore = score;
2364         BitCount = entry->wBitCount;
2365     }
2366 
2367     if(numMatch == 1)
2368     {
2369         /* Only one entry fits the asked dimensions */
2370         return dir->idEntries[iIndex].wResId;
2371     }
2372 
2373     /* Avoid paletted icons on non-paletted device */
2374     if (bppDesired > 8 && BitCount > 8)
2375         notPaletted = TRUE;
2376 
2377     BitCount = 0;
2378     iIndex = -1;
2379     /* Now find the entry with the best depth */
2380     for(i = 0; i < dir->idCount; i++)
2381     {
2382         entry = &dir->idEntries[i];
2383         width = fIcon ? entry->ResInfo.icon.bWidth : entry->ResInfo.cursor.wWidth;
2384         height = fIcon ? entry->ResInfo.icon.bHeight : entry->ResInfo.cursor.wHeight/2;
2385         /* 0 represents 256 */
2386         if(!width) width = 256;
2387         if(!height) height = 256;
2388         /* Check if this is the best match we had */
2389         score = 2*(abs(width - cxDesired) + abs(height - cyDesired));
2390         if(width > cxDesired)
2391             score -= width - cxDesired;
2392         if(height > cyDesired)
2393             score -= height - cyDesired;
2394         if(score != bestScore)
2395             continue;
2396         /* Exact match? */
2397         if(entry->wBitCount == bppDesired)
2398             return entry->wResId;
2399         /* We take the highest possible but smaller  than the display depth */
2400         if((entry->wBitCount > BitCount) && (entry->wBitCount < bppDesired))
2401         {
2402             /* Avoid paletted icons on non paletted devices */
2403             if ((entry->wBitCount <= 8) && notPaletted)
2404                 continue;
2405             iIndex = i;
2406             BitCount = entry->wBitCount;
2407         }
2408     }
2409 
2410     if(iIndex >= 0)
2411         return dir->idEntries[iIndex].wResId;
2412 
2413     /* No inferior or equal depth available. Get the smallest bigger one */
2414     BitCount = 0xFFFF;
2415     iIndex = -1;
2416     for(i = 0; i < dir->idCount; i++)
2417     {
2418         entry = &dir->idEntries[i];
2419         width = fIcon ? entry->ResInfo.icon.bWidth : entry->ResInfo.cursor.wWidth;
2420         height = fIcon ? entry->ResInfo.icon.bHeight : entry->ResInfo.cursor.wHeight/2;
2421         /* 0 represents 256 */
2422         if(!width) width = 256;
2423         if(!height) height = 256;
2424         /* Check if this is the best match we had */
2425         score = 2*(abs(width - cxDesired) + abs(height - cyDesired));
2426         if(width > cxDesired)
2427             score -= width - cxDesired;
2428         if(height > cyDesired)
2429             score -= height - cyDesired;
2430         if(score != bestScore)
2431             continue;
2432         /* Check the bit depth */
2433         if(entry->wBitCount < BitCount)
2434         {
2435             if((entry->wBitCount <= 8) && notPaletted)
2436                 continue;
2437             iIndex = i;
2438             BitCount = entry->wBitCount;
2439         }
2440     }
2441     if (iIndex >= 0)
2442         return dir->idEntries[iIndex].wResId;
2443 
2444     return 0;
2445 }
2446 
CreateIcon(_In_opt_ HINSTANCE hInstance,_In_ int nWidth,_In_ int nHeight,_In_ BYTE cPlanes,_In_ BYTE cBitsPixel,_In_ const BYTE * lpbANDbits,_In_ const BYTE * lpbXORbits)2447 HICON WINAPI CreateIcon(
2448   _In_opt_  HINSTANCE hInstance,
2449   _In_      int nWidth,
2450   _In_      int nHeight,
2451   _In_      BYTE cPlanes,
2452   _In_      BYTE cBitsPixel,
2453   _In_      const BYTE *lpbANDbits,
2454   _In_      const BYTE *lpbXORbits
2455 )
2456 {
2457     ICONINFO iinfo;
2458     HICON hIcon;
2459 
2460     TRACE_(icon)("%dx%d, planes %d, bpp %d, xor %p, and %p\n",
2461                  nWidth, nHeight, cPlanes, cBitsPixel, lpbXORbits, lpbANDbits);
2462 
2463     iinfo.fIcon = TRUE;
2464     iinfo.xHotspot = nWidth / 2;
2465     iinfo.yHotspot = nHeight / 2;
2466     if (cPlanes * cBitsPixel > 1)
2467     {
2468         iinfo.hbmColor = CreateBitmap( nWidth, nHeight, cPlanes, cBitsPixel, lpbXORbits );
2469         iinfo.hbmMask = CreateBitmap( nWidth, nHeight, 1, 1, lpbANDbits );
2470     }
2471     else
2472     {
2473         iinfo.hbmMask = CreateBitmap( nWidth, nHeight * 2, 1, 1, lpbANDbits );
2474         iinfo.hbmColor = NULL;
2475     }
2476 
2477     hIcon = CreateIconIndirect( &iinfo );
2478 
2479     DeleteObject( iinfo.hbmMask );
2480     if (iinfo.hbmColor) DeleteObject( iinfo.hbmColor );
2481 
2482     return hIcon;
2483 }
2484 
CreateIconFromResource(_In_ PBYTE presbits,_In_ DWORD dwResSize,_In_ BOOL fIcon,_In_ DWORD dwVer)2485 HICON WINAPI CreateIconFromResource(
2486   _In_  PBYTE presbits,
2487   _In_  DWORD dwResSize,
2488   _In_  BOOL fIcon,
2489   _In_  DWORD dwVer
2490 )
2491 {
2492     return CreateIconFromResourceEx( presbits, dwResSize, fIcon, dwVer, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
2493 }
2494 
CreateIconFromResourceEx(_In_ PBYTE pbIconBits,_In_ DWORD cbIconBits,_In_ BOOL fIcon,_In_ DWORD dwVersion,_In_ int cxDesired,_In_ int cyDesired,_In_ UINT uFlags)2495 HICON WINAPI CreateIconFromResourceEx(
2496   _In_  PBYTE pbIconBits,
2497   _In_  DWORD cbIconBits,
2498   _In_  BOOL fIcon,
2499   _In_  DWORD dwVersion,
2500   _In_  int cxDesired,
2501   _In_  int cyDesired,
2502   _In_  UINT uFlags
2503 )
2504 {
2505     CURSORDATA cursorData;
2506     HICON hIcon;
2507     BOOL isAnimated;
2508 
2509     TRACE("%p, %lu, %lu, %lu, %i, %i, %lu.\n", pbIconBits, cbIconBits, fIcon, dwVersion, cxDesired, cyDesired, uFlags);
2510 
2511     if(uFlags & LR_DEFAULTSIZE)
2512     {
2513         if(!cxDesired) cxDesired = GetSystemMetrics(fIcon ? SM_CXICON : SM_CXCURSOR);
2514         if(!cyDesired) cyDesired = GetSystemMetrics(fIcon ? SM_CYICON : SM_CYCURSOR);
2515     }
2516 
2517     ZeroMemory(&cursorData, sizeof(cursorData));
2518     cursorData.cx = cxDesired;
2519     cursorData.cy = cyDesired;
2520     cursorData.rt = (USHORT)((ULONG_PTR)(fIcon ? RT_ICON : RT_CURSOR));
2521 
2522     /* Convert to win32k-ready data */
2523     if(!memcmp(pbIconBits, "RIFF", 4))
2524     {
2525         if(!CURSORICON_GetCursorDataFromANI(&cursorData, pbIconBits, cbIconBits, uFlags))
2526         {
2527             ERR("Could not get cursor data from .ani.\n");
2528             return NULL;
2529         }
2530         isAnimated = !!(cursorData.CURSORF_flags & CURSORF_ACON);
2531     }
2532     else
2533     {
2534         /* It is possible to pass Icon Directories to this API */
2535         int wResId = LookupIconIdFromDirectoryEx(pbIconBits, fIcon, cxDesired, cyDesired, uFlags);
2536         HANDLE ResHandle = NULL;
2537 #ifdef __REACTOS__
2538         if (wResId && (pbIconBits[4] != sizeof(BITMAPINFOHEADER)))
2539 #else
2540         if(wResId)
2541 #endif
2542         {
2543             HINSTANCE hinst;
2544             HRSRC hrsrc;
2545             CURSORICONDIR* pCurIconDir = (CURSORICONDIR*)pbIconBits;
2546 
2547             TRACE("Pointer points to a directory structure.\n");
2548 
2549             /* So this is a pointer to an icon directory structure. Find the module */
2550             if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
2551                     (LPCWSTR)pbIconBits,
2552                     &hinst))
2553             {
2554                 return NULL;
2555             }
2556 
2557             /* Check we were given the right type of resource */
2558             if((fIcon && pCurIconDir->idType == 2) || (!fIcon && pCurIconDir->idType == 1))
2559             {
2560                 WARN("Got a %s directory pointer, but called for a %s\n", fIcon ? "cursor" : "icon", fIcon ? "icon" : "cursor");
2561                 return NULL;
2562             }
2563 
2564             /* Get the relevant resource pointer */
2565             hrsrc = FindResourceW(
2566                 hinst,
2567                 MAKEINTRESOURCEW(wResId),
2568                 fIcon ? RT_ICON : RT_CURSOR);
2569             if (!hrsrc)
2570                 return NULL;
2571 
2572             ResHandle = LoadResource(hinst, hrsrc);
2573             if (!ResHandle)
2574                 return NULL;
2575 
2576             pbIconBits = LockResource(ResHandle);
2577             if (!pbIconBits)
2578             {
2579                 FreeResource(ResHandle);
2580                 return NULL;
2581             }
2582         }
2583         if(!fIcon)
2584         {
2585             WORD* pt = (WORD*)pbIconBits;
2586             cursorData.xHotspot = *pt++;
2587             cursorData.yHotspot = *pt++;
2588             pbIconBits = (PBYTE)pt;
2589         }
2590 
2591         if (!CURSORICON_GetCursorDataFromBMI(&cursorData, (BITMAPINFO*)pbIconBits))
2592         {
2593             ERR("Couldn't fill the CURSORDATA structure.\n");
2594             if (ResHandle)
2595                 FreeResource(ResHandle);
2596             return NULL;
2597         }
2598         if (ResHandle)
2599             FreeResource(ResHandle);
2600         isAnimated = FALSE;
2601     }
2602 
2603     if (uFlags & LR_SHARED)
2604         cursorData.CURSORF_flags |= CURSORF_LRSHARED;
2605 
2606     hIcon = NtUserxCreateEmptyCurObject(isAnimated);
2607     if (!hIcon)
2608         goto end_error;
2609 
2610     if(!NtUserSetCursorIconData(hIcon, NULL, NULL, &cursorData))
2611     {
2612         ERR("NtUserSetCursorIconData failed.\n");
2613         NtUserDestroyCursor(hIcon, TRUE);
2614         goto end_error;
2615     }
2616 
2617     if(isAnimated)
2618         HeapFree(GetProcessHeap(), 0, cursorData.aspcur);
2619 
2620     return hIcon;
2621 
2622     /* Clean up */
2623 end_error:
2624     if(isAnimated)
2625         HeapFree(GetProcessHeap(), 0, cursorData.aspcur);
2626     DeleteObject(cursorData.hbmMask);
2627     if(cursorData.hbmColor) DeleteObject(cursorData.hbmColor);
2628     if(cursorData.hbmAlpha) DeleteObject(cursorData.hbmAlpha);
2629 
2630     return NULL;
2631 }
2632 
CreateIconIndirect(_In_ PICONINFO piconinfo)2633 HICON WINAPI CreateIconIndirect(
2634   _In_  PICONINFO piconinfo
2635 )
2636 {
2637     /* As simple as creating a handle, and let win32k deal with the bitmaps */
2638     HICON hiconRet;
2639     CURSORDATA cursorData;
2640 
2641     TRACE("%p.\n", piconinfo);
2642 
2643     ZeroMemory(&cursorData, sizeof(cursorData));
2644 
2645     if(!CURSORICON_GetCursorDataFromIconInfo(&cursorData, piconinfo))
2646         return NULL;
2647 
2648     hiconRet = NtUserxCreateEmptyCurObject(FALSE);
2649     if(!hiconRet)
2650         goto end_error;
2651 
2652     if(!NtUserSetCursorIconData(hiconRet, NULL, NULL, &cursorData))
2653     {
2654         NtUserDestroyCursor(hiconRet, FALSE);
2655         goto end_error;
2656     }
2657 
2658     TRACE("Returning 0x%08x.\n", hiconRet);
2659 
2660     return hiconRet;
2661 
2662 end_error:
2663     /* Clean up */
2664     DeleteObject(cursorData.hbmMask);
2665     if(cursorData.hbmColor) DeleteObject(cursorData.hbmColor);
2666     if(cursorData.hbmAlpha) DeleteObject(cursorData.hbmAlpha);
2667 
2668     return NULL;
2669 }
2670 
CreateCursor(_In_opt_ HINSTANCE hInst,_In_ int xHotSpot,_In_ int yHotSpot,_In_ int nWidth,_In_ int nHeight,_In_ const VOID * pvANDPlane,_In_ const VOID * pvXORPlane)2671 HCURSOR WINAPI CreateCursor(
2672   _In_opt_  HINSTANCE hInst,
2673   _In_      int xHotSpot,
2674   _In_      int yHotSpot,
2675   _In_      int nWidth,
2676   _In_      int nHeight,
2677   _In_      const VOID *pvANDPlane,
2678   _In_      const VOID *pvXORPlane
2679 )
2680 {
2681     ICONINFO info;
2682     HCURSOR hCursor;
2683 
2684     TRACE_(cursor)("%dx%d spot=%d,%d xor=%p and=%p\n",
2685                     nWidth, nHeight, xHotSpot, yHotSpot, pvXORPlane, pvANDPlane);
2686 
2687     info.fIcon = FALSE;
2688     info.xHotspot = xHotSpot;
2689     info.yHotspot = yHotSpot;
2690     info.hbmMask = CreateBitmap( nWidth, nHeight, 1, 1, pvANDPlane );
2691     info.hbmColor = CreateBitmap( nWidth, nHeight, 1, 1, pvXORPlane );
2692     hCursor = CreateIconIndirect( &info );
2693     DeleteObject( info.hbmMask );
2694     DeleteObject( info.hbmColor );
2695     return hCursor;
2696 }
2697 
SetSystemCursor(_In_ HCURSOR hcur,_In_ DWORD id)2698 BOOL WINAPI SetSystemCursor(
2699   _In_  HCURSOR hcur,
2700   _In_  DWORD id
2701 )
2702 {
2703     if (hcur == NULL)
2704     {
2705        hcur = LoadImageW(NULL, MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
2706        if (hcur == NULL)
2707        {
2708           return FALSE;
2709        }
2710     }
2711     return NtUserSetSystemCursor(hcur,id);
2712 }
2713 
SetCursorPos(_In_ int X,_In_ int Y)2714 BOOL WINAPI SetCursorPos(
2715   _In_  int X,
2716   _In_  int Y
2717 )
2718 {
2719     return NtUserxSetCursorPos(X,Y);
2720 }
2721 
GetCursorPos(_Out_ LPPOINT lpPoint)2722 BOOL WINAPI GetCursorPos(
2723   _Out_  LPPOINT lpPoint
2724 )
2725 {
2726     return NtUserxGetCursorPos(lpPoint);
2727 }
2728 
ShowCursor(_In_ BOOL bShow)2729 int WINAPI ShowCursor(
2730   _In_  BOOL bShow
2731 )
2732 {
2733     return NtUserxShowCursor(bShow);
2734 }
2735 
GetCursor(void)2736 HCURSOR WINAPI GetCursor(void)
2737 {
2738     return (HCURSOR)NtUserGetThreadState(THREADSTATE_GETCURSOR);
2739 }
2740 
DestroyCursor(_In_ HCURSOR hCursor)2741 BOOL WINAPI DestroyCursor(
2742   _In_  HCURSOR hCursor
2743 )
2744 {
2745     return NtUserDestroyCursor(hCursor, FALSE);
2746 }
2747 
2748 HCURSOR
2749 WINAPI
GetCursorFrameInfo(HCURSOR hCursor,DWORD reserved,DWORD istep,PINT rate_jiffies,DWORD * num_steps)2750 GetCursorFrameInfo(HCURSOR hCursor, DWORD reserved, DWORD istep, PINT rate_jiffies, DWORD *num_steps)
2751 {
2752    return NtUserGetCursorFrameInfo(hCursor, istep, rate_jiffies, num_steps);
2753 }
2754