[User32]
[reactos.git] / reactos / win32ss / user / user32 / windows / cursoricon.c
1 /*
2 * Cursor and icon support
3 *
4 * Copyright 1995 Alexandre Julliard
5 * 1996 Martin Von Loewis
6 * 1997 Alex Korobka
7 * 1998 Turchanov Sergey
8 * 2007 Henri Verbeet
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25 #include <user32.h>
26
27 #include <wine/debug.h>
28
29 WINE_DEFAULT_DEBUG_CHANNEL(cursor);
30 WINE_DECLARE_DEBUG_CHANNEL(icon);
31 WINE_DECLARE_DEBUG_CHANNEL(resource);
32
33 #include "pshpack1.h"
34
35 typedef struct {
36 BYTE bWidth;
37 BYTE bHeight;
38 BYTE bColorCount;
39 BYTE bReserved;
40 WORD xHotspot;
41 WORD yHotspot;
42 DWORD dwDIBSize;
43 DWORD dwDIBOffset;
44 } CURSORICONFILEDIRENTRY;
45
46 typedef struct
47 {
48 WORD idReserved;
49 WORD idType;
50 WORD idCount;
51 CURSORICONFILEDIRENTRY idEntries[1];
52 } CURSORICONFILEDIR;
53
54 #include "poppack.h"
55
56 static HDC screen_dc;
57
58 static const WCHAR DISPLAYW[] = {'D','I','S','P','L','A','Y',0};
59
60
61 static CRITICAL_SECTION IconCrst;
62 static CRITICAL_SECTION_DEBUG critsect_debug =
63 {
64 0, 0, &IconCrst,
65 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
66 0, 0, { (DWORD_PTR)(__FILE__ ": IconCrst") }
67 };
68 static CRITICAL_SECTION IconCrst = { &critsect_debug, -1, 0, 0, 0, 0 };
69
70 /***********************************************************************
71 * CreateCursorIconHandle
72 *
73 * Creates a handle with everything in there
74 */
75 static
76 HICON
77 CreateCursorIconHandle( PICONINFO IconInfo )
78 {
79 HICON hIcon = NtUserxCreateEmptyCurObject(0);
80 if(!hIcon)
81 return NULL;
82
83 NtUserSetCursorContents(hIcon, IconInfo);
84 return hIcon;
85 }
86
87
88
89 /***********************************************************************
90 * map_fileW
91 *
92 * Helper function to map a file to memory:
93 * name - file name
94 * [RETURN] ptr - pointer to mapped file
95 * [RETURN] filesize - pointer size of file to be stored if not NULL
96 */
97 static void *map_fileW( LPCWSTR name, LPDWORD filesize )
98 {
99 HANDLE hFile, hMapping;
100 LPVOID ptr = NULL;
101
102 hFile = CreateFileW( name, GENERIC_READ, FILE_SHARE_READ, NULL,
103 OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0 );
104 if (hFile != INVALID_HANDLE_VALUE)
105 {
106 hMapping = CreateFileMappingW( hFile, NULL, PAGE_READONLY, 0, 0, NULL );
107 if (hMapping)
108 {
109 ptr = MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 );
110 CloseHandle( hMapping );
111 if (filesize)
112 *filesize = GetFileSize( hFile, NULL );
113 }
114 CloseHandle( hFile );
115 }
116 return ptr;
117 }
118
119
120 /***********************************************************************
121 * get_dib_width_bytes
122 *
123 * Return the width of a DIB bitmap in bytes. DIB bitmap data is 32-bit aligned.
124 */
125 static int get_dib_width_bytes( int width, int depth )
126 {
127 int words;
128
129 switch(depth)
130 {
131 case 1: words = (width + 31) / 32; break;
132 case 4: words = (width + 7) / 8; break;
133 case 8: words = (width + 3) / 4; break;
134 case 15:
135 case 16: words = (width + 1) / 2; break;
136 case 24: words = (width * 3 + 3)/4; break;
137 default:
138 WARN("(%d): Unsupported depth\n", depth );
139 /* fall through */
140 case 32:
141 words = width;
142 }
143 return 4 * words;
144 }
145
146
147 /***********************************************************************
148 * bitmap_info_size
149 *
150 * Return the size of the bitmap info structure including color table.
151 */
152 static int bitmap_info_size( const BITMAPINFO * info, WORD coloruse )
153 {
154 unsigned int colors, size, masks = 0;
155
156 if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
157 {
158 const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *)info;
159 colors = (core->bcBitCount <= 8) ? 1 << core->bcBitCount : 0;
160 return sizeof(BITMAPCOREHEADER) + colors *
161 ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBTRIPLE) : sizeof(WORD));
162 }
163 else /* assume BITMAPINFOHEADER */
164 {
165 colors = info->bmiHeader.biClrUsed;
166 if (colors > 256) /* buffer overflow otherwise */
167 colors = 256;
168 if (!colors && (info->bmiHeader.biBitCount <= 8))
169 colors = 1 << info->bmiHeader.biBitCount;
170 if (info->bmiHeader.biCompression == BI_BITFIELDS) masks = 3;
171 size = max( info->bmiHeader.biSize, sizeof(BITMAPINFOHEADER) + masks * sizeof(DWORD) );
172 return size + colors * ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBQUAD) : sizeof(WORD));
173 }
174 }
175
176
177 /***********************************************************************
178 * is_dib_monochrome
179 *
180 * Returns whether a DIB can be converted to a monochrome DDB.
181 *
182 * A DIB can be converted if its color table contains only black and
183 * white. Black must be the first color in the color table.
184 *
185 * Note : If the first color in the color table is white followed by
186 * black, we can't convert it to a monochrome DDB with
187 * SetDIBits, because black and white would be inverted.
188 */
189 static BOOL is_dib_monochrome( const BITMAPINFO* info )
190 {
191 if (info->bmiHeader.biBitCount != 1) return FALSE;
192
193 if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
194 {
195 const RGBTRIPLE *rgb = ((const BITMAPCOREINFO*)info)->bmciColors;
196
197 /* Check if the first color is black */
198 if ((rgb->rgbtRed == 0) && (rgb->rgbtGreen == 0) && (rgb->rgbtBlue == 0))
199 {
200 rgb++;
201
202 /* Check if the second color is white */
203 return ((rgb->rgbtRed == 0xff) && (rgb->rgbtGreen == 0xff)
204 && (rgb->rgbtBlue == 0xff));
205 }
206 else return FALSE;
207 }
208 else /* assume BITMAPINFOHEADER */
209 {
210 const RGBQUAD *rgb = info->bmiColors;
211
212 /* Check if the first color is black */
213 if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) &&
214 (rgb->rgbBlue == 0) && (rgb->rgbReserved == 0))
215 {
216 rgb++;
217
218 /* Check if the second color is white */
219 return ((rgb->rgbRed == 0xff) && (rgb->rgbGreen == 0xff)
220 && (rgb->rgbBlue == 0xff) && (rgb->rgbReserved == 0));
221 }
222 else return FALSE;
223 }
224 }
225
226 /***********************************************************************
227 * DIB_GetBitmapInfo
228 *
229 * Get the info from a bitmap header.
230 * Return 1 for INFOHEADER, 0 for COREHEADER,
231 */
232 static int DIB_GetBitmapInfo( const BITMAPINFOHEADER *header, LONG *width,
233 LONG *height, WORD *bpp, DWORD *compr )
234 {
235 if (header->biSize == sizeof(BITMAPCOREHEADER))
236 {
237 const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *)header;
238 *width = core->bcWidth;
239 *height = core->bcHeight;
240 *bpp = core->bcBitCount;
241 *compr = 0;
242 return 0;
243 }
244 else if (header->biSize == sizeof(BITMAPINFOHEADER) ||
245 header->biSize == sizeof(BITMAPV4HEADER) ||
246 header->biSize == sizeof(BITMAPV5HEADER))
247 {
248 *width = header->biWidth;
249 *height = header->biHeight;
250 *bpp = header->biBitCount;
251 *compr = header->biCompression;
252 return 1;
253 }
254 ERR("(%d): unknown/wrong size for header\n", header->biSize );
255 return -1;
256 }
257
258
259 /*
260 * The following macro functions account for the irregularities of
261 * accessing cursor and icon resources in files and resource entries.
262 */
263 typedef BOOL (*fnGetCIEntry)( LPCVOID dir, DWORD size, int n,
264 int *width, int *height, int *bits );
265
266 /**********************************************************************
267 * CURSORICON_FindBestIcon
268 *
269 * Find the icon closest to the requested size and bit depth.
270 */
271 static int CURSORICON_FindBestIcon( LPCVOID dir, DWORD size, fnGetCIEntry get_entry,
272 int width, int height, int depth )
273 {
274 int i, cx, cy, bits, bestEntry = -1;
275 UINT iTotalDiff, iXDiff=0, iYDiff=0, iColorDiff;
276 UINT iTempXDiff, iTempYDiff, iTempColorDiff;
277
278 /* Find Best Fit */
279 iTotalDiff = 0xFFFFFFFF;
280 iColorDiff = 0xFFFFFFFF;
281 for ( i = 0; get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
282 {
283 iTempXDiff = abs(width - cx);
284 iTempYDiff = abs(height - cy);
285
286 if(iTotalDiff > (iTempXDiff + iTempYDiff))
287 {
288 iXDiff = iTempXDiff;
289 iYDiff = iTempYDiff;
290 iTotalDiff = iXDiff + iYDiff;
291 }
292 }
293
294 /* Find Best Colors for Best Fit */
295 for ( i = 0; get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
296 {
297 if(abs(width - cx) == iXDiff && abs(height - cy) == iYDiff)
298 {
299 iTempColorDiff = abs(depth - bits);
300 if(iColorDiff > iTempColorDiff)
301 {
302 bestEntry = i;
303 iColorDiff = iTempColorDiff;
304 }
305 }
306 }
307
308 return bestEntry;
309 }
310
311 static BOOL CURSORICON_GetResIconEntry( LPCVOID dir, DWORD size, int n,
312 int *width, int *height, int *bits )
313 {
314 const CURSORICONDIR *resdir = dir;
315 const ICONRESDIR *icon;
316
317 if ( resdir->idCount <= n )
318 return FALSE;
319 if ((const char *)&resdir->idEntries[n + 1] - (const char *)dir > size)
320 return FALSE;
321 icon = &resdir->idEntries[n].ResInfo.icon;
322 *width = icon->bWidth;
323 *height = icon->bHeight;
324 *bits = resdir->idEntries[n].wBitCount;
325 return TRUE;
326 }
327
328 /**********************************************************************
329 * CURSORICON_FindBestCursor
330 *
331 * Find the cursor closest to the requested size.
332 *
333 * FIXME: parameter 'color' ignored.
334 */
335 static int CURSORICON_FindBestCursor( LPCVOID dir, DWORD size, fnGetCIEntry get_entry,
336 int width, int height, int depth )
337 {
338 int i, maxwidth, maxheight, cx, cy, bits, bestEntry = -1;
339
340 /* Double height to account for AND and XOR masks */
341
342 height *= 2;
343
344 /* First find the largest one smaller than or equal to the requested size*/
345
346 maxwidth = maxheight = 0;
347 for ( i = 0; get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
348 {
349 if ((cx <= width) && (cy <= height) &&
350 (cx > maxwidth) && (cy > maxheight))
351 {
352 bestEntry = i;
353 maxwidth = cx;
354 maxheight = cy;
355 }
356 }
357 if (bestEntry != -1) return bestEntry;
358
359 /* Now find the smallest one larger than the requested size */
360
361 maxwidth = maxheight = 255;
362 for ( i = 0; get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
363 {
364 if (((cx < maxwidth) && (cy < maxheight)) || (bestEntry == -1))
365 {
366 bestEntry = i;
367 maxwidth = cx;
368 maxheight = cy;
369 }
370 }
371
372 return bestEntry;
373 }
374
375 static BOOL CURSORICON_GetResCursorEntry( LPCVOID dir, DWORD size, int n,
376 int *width, int *height, int *bits )
377 {
378 const CURSORICONDIR *resdir = dir;
379 const CURSORRESDIR *cursor;
380
381 if ( resdir->idCount <= n )
382 return FALSE;
383 if ((const char *)&resdir->idEntries[n + 1] - (const char *)dir > size)
384 return FALSE;
385 cursor = &resdir->idEntries[n].ResInfo.cursor;
386 *width = cursor->wWidth;
387 *height = cursor->wHeight;
388 *bits = resdir->idEntries[n].wBitCount;
389 return TRUE;
390 }
391
392 static CURSORICONDIRENTRY *CURSORICON_FindBestIconRes( CURSORICONDIR * dir, DWORD size,
393 int width, int height, int depth )
394 {
395 int n;
396
397 n = CURSORICON_FindBestIcon( dir, size, CURSORICON_GetResIconEntry,
398 width, height, depth );
399 if ( n < 0 )
400 return NULL;
401 return &dir->idEntries[n];
402 }
403
404 static CURSORICONDIRENTRY *CURSORICON_FindBestCursorRes( CURSORICONDIR *dir, DWORD size,
405 int width, int height, int depth )
406 {
407 int n = CURSORICON_FindBestCursor( dir, size, CURSORICON_GetResCursorEntry,
408 width, height, depth );
409 if ( n < 0 )
410 return NULL;
411 return &dir->idEntries[n];
412 }
413
414 static BOOL CURSORICON_GetFileEntry( LPCVOID dir, DWORD size, int n,
415 int *width, int *height, int *bits )
416 {
417 const CURSORICONFILEDIR *filedir = dir;
418 const CURSORICONFILEDIRENTRY *entry;
419 const BITMAPINFOHEADER *info;
420
421 if ( filedir->idCount <= n )
422 return FALSE;
423 if ((const char *)&filedir->idEntries[n + 1] - (const char *)dir > size)
424 return FALSE;
425 entry = &filedir->idEntries[n];
426 info = (const BITMAPINFOHEADER *)((const char *)dir + entry->dwDIBOffset);
427 if ((const char *)(info + 1) - (const char *)dir > size) return FALSE;
428 *width = entry->bWidth;
429 *height = entry->bHeight;
430 *bits = info->biBitCount;
431 return TRUE;
432 }
433
434 static CURSORICONFILEDIRENTRY *CURSORICON_FindBestCursorFile( CURSORICONFILEDIR *dir, DWORD size,
435 int width, int height, int depth )
436 {
437 int n = CURSORICON_FindBestCursor( dir, size, CURSORICON_GetFileEntry,
438 width, height, depth );
439 if ( n < 0 )
440 return NULL;
441 return &dir->idEntries[n];
442 }
443
444 static CURSORICONFILEDIRENTRY *CURSORICON_FindBestIconFile( CURSORICONFILEDIR *dir, DWORD size,
445 int width, int height, int depth )
446 {
447 int n = CURSORICON_FindBestIcon( dir, size, CURSORICON_GetFileEntry,
448 width, height, depth );
449 if ( n < 0 )
450 return NULL;
451 return &dir->idEntries[n];
452 }
453
454 /***********************************************************************
455 * create_icon_bitmaps
456 *
457 * Create the color, mask and alpha bitmaps from the DIB info.
458 */
459 static BOOL create_icon_bitmaps( const BITMAPINFO *bmi, int width, int height,
460 HBITMAP *color, HBITMAP *mask )
461 {
462 BOOL monochrome = is_dib_monochrome( bmi );
463 unsigned int size = bitmap_info_size( bmi, DIB_RGB_COLORS );
464 BITMAPINFO *info;
465 void *color_bits, *mask_bits;
466 BOOL ret = FALSE;
467 HDC hdc = 0;
468
469 if (!(info = HeapAlloc( GetProcessHeap(), 0, max( size, FIELD_OFFSET( BITMAPINFO, bmiColors[2] )))))
470 return FALSE;
471 if (!(hdc = CreateCompatibleDC(NULL))) goto done;
472
473 memcpy( info, bmi, size );
474 info->bmiHeader.biHeight /= 2;
475
476 color_bits = (char *)bmi + size;
477 mask_bits = (char *)color_bits +
478 get_dib_width_bytes( bmi->bmiHeader.biWidth,
479 bmi->bmiHeader.biBitCount ) * abs(info->bmiHeader.biHeight);
480
481 if (monochrome)
482 {
483 if (!(*mask = CreateBitmap( width, height * 2, 1, 1, NULL ))) goto done;
484 *color = 0;
485
486 /* copy color data into second half of mask bitmap */
487 SelectObject( hdc, *mask );
488 StretchDIBits( hdc, 0, height, width, height,
489 0, 0, info->bmiHeader.biWidth, info->bmiHeader.biHeight,
490 color_bits, info, DIB_RGB_COLORS, SRCCOPY );
491 }
492 else
493 {
494 if (!(*mask = CreateBitmap( width, height, 1, 1, NULL ))) goto done;
495 if (!(*color = CreateBitmap( width, height, GetDeviceCaps(hdc, PLANES),
496 GetDeviceCaps(hdc, BITSPIXEL), NULL )))
497 {
498 DeleteObject( *mask );
499 goto done;
500 }
501 SelectObject( hdc, *color );
502 StretchDIBits( hdc, 0, 0, width, height,
503 0, 0, info->bmiHeader.biWidth, info->bmiHeader.biHeight,
504 color_bits, info, DIB_RGB_COLORS, SRCCOPY );
505
506 /* convert info to monochrome to copy the mask */
507 info->bmiHeader.biBitCount = 1;
508 if (info->bmiHeader.biSize != sizeof(BITMAPCOREHEADER))
509 {
510 RGBQUAD *rgb = info->bmiColors;
511
512 info->bmiHeader.biClrUsed = info->bmiHeader.biClrImportant = 2;
513 rgb[0].rgbBlue = rgb[0].rgbGreen = rgb[0].rgbRed = 0x00;
514 rgb[1].rgbBlue = rgb[1].rgbGreen = rgb[1].rgbRed = 0xff;
515 rgb[0].rgbReserved = rgb[1].rgbReserved = 0;
516 }
517 else
518 {
519 RGBTRIPLE *rgb = (RGBTRIPLE *)(((BITMAPCOREHEADER *)info) + 1);
520
521 rgb[0].rgbtBlue = rgb[0].rgbtGreen = rgb[0].rgbtRed = 0x00;
522 rgb[1].rgbtBlue = rgb[1].rgbtGreen = rgb[1].rgbtRed = 0xff;
523 }
524 }
525
526 SelectObject( hdc, *mask );
527 StretchDIBits( hdc, 0, 0, width, height,
528 0, 0, info->bmiHeader.biWidth, info->bmiHeader.biHeight,
529 mask_bits, info, DIB_RGB_COLORS, SRCCOPY );
530 ret = TRUE;
531
532 done:
533 if(hdc) DeleteDC( hdc );
534 HeapFree( GetProcessHeap(), 0, info );
535 return ret;
536 }
537
538 static HICON CURSORICON_CreateIconFromBMI( BITMAPINFO *bmi,
539 POINT hotspot, BOOL bIcon,
540 DWORD dwVersion,
541 INT width, INT height,
542 UINT cFlag )
543 {
544 HBITMAP color = 0, mask = 0;
545 BOOL do_stretch;
546 ICONINFO IconInfo;
547
548 if (dwVersion == 0x00020000)
549 {
550 FIXME_(cursor)("\t2.xx resources are not supported\n");
551 return 0;
552 }
553
554 /* Check bitmap header */
555
556 if ( (bmi->bmiHeader.biSize != sizeof(BITMAPCOREHEADER)) &&
557 (bmi->bmiHeader.biSize != sizeof(BITMAPINFOHEADER) ||
558 bmi->bmiHeader.biCompression != BI_RGB) )
559 {
560 WARN_(cursor)("\tinvalid resource bitmap header.\n");
561 return 0;
562 }
563
564 if (!width) width = bmi->bmiHeader.biWidth;
565 if (!height) height = bmi->bmiHeader.biHeight/2;
566 do_stretch = (bmi->bmiHeader.biHeight/2 != height) ||
567 (bmi->bmiHeader.biWidth != width);
568
569 /* Scale the hotspot */
570 if (bIcon)
571 {
572 hotspot.x = width / 2;
573 hotspot.y = height / 2;
574 }
575 else if (do_stretch)
576 {
577 hotspot.x = (hotspot.x * width) / bmi->bmiHeader.biWidth;
578 hotspot.y = (hotspot.y * height) / (bmi->bmiHeader.biHeight / 2);
579 }
580
581 if (!screen_dc) screen_dc = CreateDCW( DISPLAYW, NULL, NULL, NULL );
582 if (!screen_dc) return 0;
583
584 if (!create_icon_bitmaps( bmi, width, height, &color, &mask )) return 0;
585
586 IconInfo.xHotspot = hotspot.x;
587 IconInfo.yHotspot = hotspot.y;
588 IconInfo.fIcon = bIcon;
589 IconInfo.hbmColor = color;
590 IconInfo.hbmMask = mask;
591
592 return CreateCursorIconHandle(&IconInfo);
593 }
594
595
596 /**********************************************************************
597 * .ANI cursor support
598 */
599 #define RIFF_FOURCC( c0, c1, c2, c3 ) \
600 ( (DWORD)(BYTE)(c0) | ( (DWORD)(BYTE)(c1) << 8 ) | \
601 ( (DWORD)(BYTE)(c2) << 16 ) | ( (DWORD)(BYTE)(c3) << 24 ) )
602
603 #define ANI_RIFF_ID RIFF_FOURCC('R', 'I', 'F', 'F')
604 #define ANI_LIST_ID RIFF_FOURCC('L', 'I', 'S', 'T')
605 #define ANI_ACON_ID RIFF_FOURCC('A', 'C', 'O', 'N')
606 #define ANI_anih_ID RIFF_FOURCC('a', 'n', 'i', 'h')
607 #define ANI_seq__ID RIFF_FOURCC('s', 'e', 'q', ' ')
608 #define ANI_fram_ID RIFF_FOURCC('f', 'r', 'a', 'm')
609
610 #define ANI_FLAG_ICON 0x1
611 #define ANI_FLAG_SEQUENCE 0x2
612
613 typedef struct {
614 DWORD header_size;
615 DWORD num_frames;
616 DWORD num_steps;
617 DWORD width;
618 DWORD height;
619 DWORD bpp;
620 DWORD num_planes;
621 DWORD display_rate;
622 DWORD flags;
623 } ani_header;
624
625 typedef struct {
626 DWORD data_size;
627 const unsigned char *data;
628 } riff_chunk_t;
629
630 static void dump_ani_header( const ani_header *header )
631 {
632 TRACE(" header size: %d\n", header->header_size);
633 TRACE(" frames: %d\n", header->num_frames);
634 TRACE(" steps: %d\n", header->num_steps);
635 TRACE(" width: %d\n", header->width);
636 TRACE(" height: %d\n", header->height);
637 TRACE(" bpp: %d\n", header->bpp);
638 TRACE(" planes: %d\n", header->num_planes);
639 TRACE(" display rate: %d\n", header->display_rate);
640 TRACE(" flags: 0x%08x\n", header->flags);
641 }
642
643
644 /*
645 * RIFF:
646 * DWORD "RIFF"
647 * DWORD size
648 * DWORD riff_id
649 * BYTE[] data
650 *
651 * LIST:
652 * DWORD "LIST"
653 * DWORD size
654 * DWORD list_id
655 * BYTE[] data
656 *
657 * CHUNK:
658 * DWORD chunk_id
659 * DWORD size
660 * BYTE[] data
661 */
662 static void riff_find_chunk( DWORD chunk_id, DWORD chunk_type, const riff_chunk_t *parent_chunk, riff_chunk_t *chunk )
663 {
664 const unsigned char *ptr = parent_chunk->data;
665 const unsigned char *end = parent_chunk->data + (parent_chunk->data_size - (2 * sizeof(DWORD)));
666
667 if (chunk_type == ANI_LIST_ID || chunk_type == ANI_RIFF_ID) end -= sizeof(DWORD);
668
669 while (ptr < end)
670 {
671 if ((!chunk_type && *(const DWORD *)ptr == chunk_id )
672 || (chunk_type && *(const DWORD *)ptr == chunk_type && *((const DWORD *)ptr + 2) == chunk_id ))
673 {
674 ptr += sizeof(DWORD);
675 chunk->data_size = (*(const DWORD *)ptr + 1) & ~1;
676 ptr += sizeof(DWORD);
677 if (chunk_type == ANI_LIST_ID || chunk_type == ANI_RIFF_ID) ptr += sizeof(DWORD);
678 chunk->data = ptr;
679
680 return;
681 }
682
683 ptr += sizeof(DWORD);
684 ptr += (*(const DWORD *)ptr + 1) & ~1;
685 ptr += sizeof(DWORD);
686 }
687 }
688
689
690 /*
691 * .ANI layout:
692 *
693 * RIFF:'ACON' RIFF chunk
694 * |- CHUNK:'anih' Header
695 * |- CHUNK:'seq ' Sequence information (optional)
696 * \- LIST:'fram' Frame list
697 * |- CHUNK:icon Cursor frames
698 * |- CHUNK:icon
699 * |- ...
700 * \- CHUNK:icon
701 */
702 static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size,
703 INT width, INT height, INT depth )
704 {
705 HCURSOR cursor;
706 ani_header header = {0};
707 LPBYTE frame_bits = 0;
708 POINT hotspot;
709 CURSORICONFILEDIRENTRY *entry;
710
711 riff_chunk_t root_chunk = { bits_size, bits };
712 riff_chunk_t ACON_chunk = {0};
713 riff_chunk_t anih_chunk = {0};
714 riff_chunk_t fram_chunk = {0};
715 const unsigned char *icon_data;
716
717 TRACE("bits %p, bits_size %d\n", bits, bits_size);
718
719 if (!bits) return 0;
720
721 riff_find_chunk( ANI_ACON_ID, ANI_RIFF_ID, &root_chunk, &ACON_chunk );
722 if (!ACON_chunk.data)
723 {
724 ERR("Failed to get root chunk.\n");
725 return 0;
726 }
727
728 riff_find_chunk( ANI_anih_ID, 0, &ACON_chunk, &anih_chunk );
729 if (!anih_chunk.data)
730 {
731 ERR("Failed to get 'anih' chunk.\n");
732 return 0;
733 }
734 memcpy( &header, anih_chunk.data, sizeof(header) );
735 dump_ani_header( &header );
736
737 riff_find_chunk( ANI_fram_ID, ANI_LIST_ID, &ACON_chunk, &fram_chunk );
738 if (!fram_chunk.data)
739 {
740 ERR("Failed to get icon list.\n");
741 return 0;
742 }
743
744 /* FIXME: For now, just load the first frame. Before we can load all the
745 * frames, we need to write the needed code in wineserver, etc. to handle
746 * cursors. Once this code is written, we can extend it to support .ani
747 * cursors and then update user32 and winex11.drv to load all frames.
748 *
749 * Hopefully this will at least make some games (C&C3, etc.) more playable
750 * in the meantime.
751 */
752 FIXME("Loading all frames for .ani cursors not implemented.\n");
753 icon_data = fram_chunk.data + (2 * sizeof(DWORD));
754
755 entry = CURSORICON_FindBestIconFile( (CURSORICONFILEDIR *) icon_data,
756 bits + bits_size - icon_data,
757 width, height, depth );
758
759 frame_bits = HeapAlloc( GetProcessHeap(), 0, entry->dwDIBSize );
760 memcpy( frame_bits, icon_data + entry->dwDIBOffset, entry->dwDIBSize );
761
762 if (!header.width || !header.height)
763 {
764 header.width = entry->bWidth;
765 header.height = entry->bHeight;
766 }
767
768 hotspot.x = entry->xHotspot;
769 hotspot.y = entry->yHotspot;
770
771 cursor = CURSORICON_CreateIconFromBMI( (BITMAPINFO *) frame_bits, hotspot,
772 FALSE, 0x00030000, header.width, header.height, 0 );
773
774 HeapFree( GetProcessHeap(), 0, frame_bits );
775
776 return cursor;
777 }
778
779
780 /**********************************************************************
781 * CreateIconFromResourceEx (USER32.@)
782 *
783 * FIXME: Convert to mono when cFlag is LR_MONOCHROME. Do something
784 * with cbSize parameter as well.
785 */
786 HICON WINAPI CreateIconFromResourceEx( PBYTE bits, DWORD cbSize,
787 BOOL bIcon, DWORD dwVersion,
788 int width, int height,
789 UINT cFlag )
790 {
791 POINT hotspot;
792 BITMAPINFO *bmi;
793
794 TRACE_(cursor)("%p (%u bytes), ver %08x, %ix%i %s %s\n",
795 bits, cbSize, dwVersion, width, height,
796 bIcon ? "icon" : "cursor", (cFlag & LR_MONOCHROME) ? "mono" : "" );
797
798 if (bIcon)
799 {
800 hotspot.x = width / 2;
801 hotspot.y = height / 2;
802 bmi = (BITMAPINFO *)bits;
803 }
804 else /* get the hotspot */
805 {
806 SHORT *pt = (SHORT *)bits;
807 hotspot.x = pt[0];
808 hotspot.y = pt[1];
809 bmi = (BITMAPINFO *)(pt + 2);
810 }
811
812 return CURSORICON_CreateIconFromBMI( bmi, hotspot, bIcon, dwVersion,
813 width, height, cFlag );
814 }
815
816
817 /**********************************************************************
818 * CreateIconFromResource (USER32.@)
819 */
820 HICON WINAPI CreateIconFromResource( PBYTE bits, DWORD cbSize,
821 BOOL bIcon, DWORD dwVersion)
822 {
823 return CreateIconFromResourceEx( bits, cbSize, bIcon, dwVersion, 0,0,0);
824 }
825
826
827 static HICON CURSORICON_LoadFromFile( LPCWSTR filename,
828 INT width, INT height, INT depth,
829 BOOL fCursor, UINT loadflags)
830 {
831 CURSORICONFILEDIRENTRY *entry;
832 CURSORICONFILEDIR *dir;
833 DWORD filesize = 0;
834 HICON hIcon = 0;
835 LPBYTE bits;
836 POINT hotspot;
837
838 TRACE("loading %s\n", debugstr_w( filename ));
839
840 bits = map_fileW( filename, &filesize );
841 if (!bits)
842 return hIcon;
843
844 /* Check for .ani. */
845 if (memcmp( bits, "RIFF", 4 ) == 0)
846 {
847 hIcon = CURSORICON_CreateIconFromANI( bits, filesize, width, height,
848 depth );
849 goto end;
850 }
851
852 dir = (CURSORICONFILEDIR*) bits;
853 if ( filesize < sizeof(*dir) )
854 goto end;
855
856 if ( filesize < (sizeof(*dir) + sizeof(dir->idEntries[0])*(dir->idCount-1)) )
857 goto end;
858
859 if ( fCursor )
860 entry = CURSORICON_FindBestCursorFile( dir, filesize, width, height, depth );
861 else
862 entry = CURSORICON_FindBestIconFile( dir, filesize, width, height, depth );
863
864 if ( !entry )
865 goto end;
866
867 /* check that we don't run off the end of the file */
868 if ( entry->dwDIBOffset > filesize )
869 goto end;
870 if ( entry->dwDIBOffset + entry->dwDIBSize > filesize )
871 goto end;
872
873 hotspot.x = entry->xHotspot;
874 hotspot.y = entry->yHotspot;
875 hIcon = CURSORICON_CreateIconFromBMI( (BITMAPINFO *)&bits[entry->dwDIBOffset],
876 hotspot, !fCursor, 0x00030000,
877 width, height, loadflags );
878 end:
879 TRACE("loaded %s -> %p\n", debugstr_w( filename ), hIcon );
880 UnmapViewOfFile( bits );
881 return hIcon;
882 }
883
884 /**********************************************************************
885 * CURSORICON_Load
886 *
887 * Load a cursor or icon from resource or file.
888 */
889 static HICON CURSORICON_Load(HINSTANCE hInstance, LPCWSTR name,
890 INT width, INT height, INT depth,
891 BOOL fCursor, UINT loadflags)
892 {
893 HANDLE handle = 0;
894 HICON hIcon = 0;
895 HRSRC hRsrc;
896 //HRSRC hGroupRsrc;
897 DWORD size;
898 CURSORICONDIR *dir;
899 CURSORICONDIRENTRY *dirEntry;
900 LPBYTE bits;
901 WORD wResId;
902 DWORD dwBytesInRes;
903
904 TRACE("%p, %s, %dx%d, depth %d, fCursor %d, flags 0x%04x\n",
905 hInstance, debugstr_w(name), width, height, depth, fCursor, loadflags);
906
907 if ( loadflags & LR_LOADFROMFILE ) /* Load from file */
908 return CURSORICON_LoadFromFile( name, width, height, depth, fCursor, loadflags );
909
910 if (!hInstance) hInstance = User32Instance; /* Load OEM cursor/icon */
911
912 /* don't cache 16-bit instances (FIXME: should never get 16-bit instances in the first place) */
913 if ((ULONG_PTR)hInstance >> 16 == 0) loadflags &= ~LR_SHARED;
914
915 /* Get directory resource ID */
916
917 if (!(hRsrc = FindResourceW( hInstance, name,
918 (LPWSTR)(fCursor ? RT_GROUP_CURSOR : RT_GROUP_ICON) )))
919 return 0;
920 //hGroupRsrc = hRsrc;
921
922 /* Find the best entry in the directory */
923
924 if (!(handle = LoadResource( hInstance, hRsrc ))) return 0;
925 if (!(dir = LockResource( handle ))) return 0;
926 size = SizeofResource( hInstance, hRsrc );
927 if (fCursor)
928 dirEntry = CURSORICON_FindBestCursorRes( dir, size, width, height, depth );
929 else
930 dirEntry = CURSORICON_FindBestIconRes( dir, size, width, height, depth );
931 if (!dirEntry) return 0;
932 wResId = dirEntry->wResId;
933 dwBytesInRes = dirEntry->dwBytesInRes;
934 FreeResource( handle );
935
936 /* Load the resource */
937
938 if (!(hRsrc = FindResourceW(hInstance,MAKEINTRESOURCEW(wResId),
939 (LPWSTR)(fCursor ? RT_CURSOR : RT_ICON) ))) return 0;
940
941 /* If shared icon, check whether it was already loaded */
942 if ( (loadflags & LR_SHARED)
943 && (hIcon = NtUserFindExistingCursorIcon( hInstance, hRsrc, 0, 0 ) ) != 0 )
944 return hIcon;
945
946 if (!(handle = LoadResource( hInstance, hRsrc ))) return 0;
947 bits = LockResource( handle );
948 hIcon = CreateIconFromResourceEx( bits, dwBytesInRes,
949 !fCursor, 0x00030000, width, height, loadflags);
950 FreeResource( handle );
951
952 /* If shared icon, add to icon cache */
953
954 if (hIcon && 0 != (loadflags & LR_SHARED))
955 {
956 #if 1
957 NtUserSetCursorIconData((HICON)hIcon, NULL, NULL, hInstance, hRsrc,
958 (HRSRC)NULL);
959 #else
960 ICONINFO iconInfo;
961
962 if(NtUserGetIconInfo(ResIcon, &iconInfo, NULL, NULL, NULL, FALSE))
963 NtUserSetCursorIconData((HICON)hIcon, hinst, NULL, &iconInfo);
964 #endif
965 }
966
967 return hIcon;
968 }
969
970
971 /*************************************************************************
972 * CURSORICON_ExtCopy
973 *
974 * Copies an Image from the Cache if LR_COPYFROMRESOURCE is specified
975 *
976 * PARAMS
977 * Handle [I] handle to an Image
978 * nType [I] Type of Handle (IMAGE_CURSOR | IMAGE_ICON)
979 * iDesiredCX [I] The Desired width of the Image
980 * iDesiredCY [I] The desired height of the Image
981 * nFlags [I] The flags from CopyImage
982 *
983 * RETURNS
984 * Success: The new handle of the Image
985 *
986 * NOTES
987 * LR_COPYDELETEORG and LR_MONOCHROME are currently not implemented.
988 * LR_MONOCHROME should be implemented by CreateIconFromResourceEx.
989 * LR_COPYFROMRESOURCE will only work if the Image is in the Cache.
990 *
991 *
992 */
993
994 static HICON CURSORICON_ExtCopy(HICON hIcon, UINT nType,
995 INT iDesiredCX, INT iDesiredCY,
996 UINT nFlags)
997 {
998 HICON hNew=0;
999
1000 TRACE_(icon)("hIcon %p, nType %u, iDesiredCX %i, iDesiredCY %i, nFlags %u\n",
1001 hIcon, nType, iDesiredCX, iDesiredCY, nFlags);
1002
1003 if(hIcon == 0)
1004 {
1005 return 0;
1006 }
1007
1008 /* Best Fit or Monochrome */
1009 if( (nFlags & LR_COPYFROMRESOURCE
1010 && (iDesiredCX > 0 || iDesiredCY > 0))
1011 || nFlags & LR_MONOCHROME)
1012 {
1013 TRACE("Copying from resource isn't implemented yet\n");
1014 hNew = CopyIcon(hIcon);
1015
1016 #if 0
1017 ICONCACHE* pIconCache = CURSORICON_FindCache(hIcon);
1018
1019 /* Not Found in Cache, then do a straight copy
1020 */
1021 if(pIconCache == NULL)
1022 {
1023 hNew = CopyIcon( hIcon );
1024 if(nFlags & LR_COPYFROMRESOURCE)
1025 {
1026 TRACE_(icon)("LR_COPYFROMRESOURCE: Failed to load from cache\n");
1027 }
1028 }
1029 else
1030 {
1031 int iTargetCY = iDesiredCY, iTargetCX = iDesiredCX;
1032 LPBYTE pBits;
1033 HANDLE hMem;
1034 HRSRC hRsrc;
1035 DWORD dwBytesInRes;
1036 WORD wResId;
1037 CURSORICONDIR *pDir;
1038 CURSORICONDIRENTRY *pDirEntry;
1039 BOOL bIsIcon = (nType == IMAGE_ICON);
1040
1041 /* Completing iDesiredCX CY for Monochrome Bitmaps if needed
1042 */
1043 if(((nFlags & LR_MONOCHROME) && !(nFlags & LR_COPYFROMRESOURCE))
1044 || (iDesiredCX == 0 && iDesiredCY == 0))
1045 {
1046 iDesiredCY = GetSystemMetrics(bIsIcon ?
1047 SM_CYICON : SM_CYCURSOR);
1048 iDesiredCX = GetSystemMetrics(bIsIcon ?
1049 SM_CXICON : SM_CXCURSOR);
1050 }
1051
1052 /* Retrieve the CURSORICONDIRENTRY
1053 */
1054 if (!(hMem = LoadResource( pIconCache->hModule ,
1055 pIconCache->hGroupRsrc)))
1056 {
1057 return 0;
1058 }
1059 if (!(pDir = LockResource( hMem )))
1060 {
1061 return 0;
1062 }
1063
1064 /* Find Best Fit
1065 */
1066 if(bIsIcon)
1067 {
1068 pDirEntry = CURSORICON_FindBestIconRes(
1069 pDir, iDesiredCX, iDesiredCY, 256 );
1070 }
1071 else
1072 {
1073 pDirEntry = CURSORICON_FindBestCursorRes(
1074 pDir, iDesiredCX, iDesiredCY, 1);
1075 }
1076
1077 wResId = pDirEntry->wResId;
1078 dwBytesInRes = pDirEntry->dwBytesInRes;
1079 FreeResource(hMem);
1080
1081 TRACE_(icon)("ResID %u, BytesInRes %u, Width %d, Height %d DX %d, DY %d\n",
1082 wResId, dwBytesInRes, pDirEntry->ResInfo.icon.bWidth,
1083 pDirEntry->ResInfo.icon.bHeight, iDesiredCX, iDesiredCY);
1084
1085 /* Get the Best Fit
1086 */
1087 if (!(hRsrc = FindResourceW(pIconCache->hModule ,
1088 MAKEINTRESOURCEW(wResId), (LPWSTR)(bIsIcon ? RT_ICON : RT_CURSOR))))
1089 {
1090 return 0;
1091 }
1092 if (!(hMem = LoadResource( pIconCache->hModule , hRsrc )))
1093 {
1094 return 0;
1095 }
1096
1097 pBits = LockResource( hMem );
1098
1099 if(nFlags & LR_DEFAULTSIZE)
1100 {
1101 iTargetCY = GetSystemMetrics(SM_CYICON);
1102 iTargetCX = GetSystemMetrics(SM_CXICON);
1103 }
1104
1105 /* Create a New Icon with the proper dimension
1106 */
1107 hNew = CreateIconFromResourceEx( pBits, dwBytesInRes,
1108 bIsIcon, 0x00030000, iTargetCX, iTargetCY, nFlags);
1109 FreeResource(hMem);
1110 }
1111 #endif
1112 }
1113 else hNew = CopyIcon( hIcon );
1114 return hNew;
1115 }
1116
1117
1118 /***********************************************************************
1119 * CreateCursor (USER32.@)
1120 */
1121 HCURSOR WINAPI CreateCursor( HINSTANCE hInstance,
1122 INT xHotSpot, INT yHotSpot,
1123 INT nWidth, INT nHeight,
1124 LPCVOID lpANDbits, LPCVOID lpXORbits )
1125 {
1126 ICONINFO info;
1127 HCURSOR hCursor;
1128
1129 TRACE_(cursor)("%dx%d spot=%d,%d xor=%p and=%p\n",
1130 nWidth, nHeight, xHotSpot, yHotSpot, lpXORbits, lpANDbits);
1131
1132 info.fIcon = FALSE;
1133 info.xHotspot = xHotSpot;
1134 info.yHotspot = yHotSpot;
1135 info.hbmMask = CreateBitmap( nWidth, nHeight, 1, 1, lpANDbits );
1136 info.hbmColor = CreateBitmap( nWidth, nHeight, 1, 1, lpXORbits );
1137 hCursor = CreateIconIndirect( &info );
1138 DeleteObject( info.hbmMask );
1139 DeleteObject( info.hbmColor );
1140 return hCursor;
1141 }
1142
1143
1144 /***********************************************************************
1145 * CreateIcon (USER32.@)
1146 *
1147 * Creates an icon based on the specified bitmaps. The bitmaps must be
1148 * provided in a device dependent format and will be resized to
1149 * (SM_CXICON,SM_CYICON) and depth converted to match the screen's color
1150 * depth. The provided bitmaps must be top-down bitmaps.
1151 * Although Windows does not support 15bpp(*) this API must support it
1152 * for Winelib applications.
1153 *
1154 * (*) Windows does not support 15bpp but it supports the 555 RGB 16bpp
1155 * format!
1156 *
1157 * RETURNS
1158 * Success: handle to an icon
1159 * Failure: NULL
1160 *
1161 * FIXME: Do we need to resize the bitmaps?
1162 */
1163 HICON WINAPI CreateIcon(
1164 HINSTANCE hInstance, /* [in] the application's hInstance */
1165 int nWidth, /* [in] the width of the provided bitmaps */
1166 int nHeight, /* [in] the height of the provided bitmaps */
1167 BYTE bPlanes, /* [in] the number of planes in the provided bitmaps */
1168 BYTE bBitsPixel, /* [in] the number of bits per pixel of the lpXORbits bitmap */
1169 const BYTE* lpANDbits, /* [in] a monochrome bitmap representing the icon's mask */
1170 const BYTE* lpXORbits) /* [in] the icon's 'color' bitmap */
1171 {
1172 ICONINFO iinfo;
1173 HICON hIcon;
1174
1175 TRACE_(icon)("%dx%d, planes %d, bpp %d, xor %p, and %p\n",
1176 nWidth, nHeight, bPlanes, bBitsPixel, lpXORbits, lpANDbits);
1177
1178 iinfo.fIcon = TRUE;
1179 iinfo.xHotspot = nWidth / 2;
1180 iinfo.yHotspot = nHeight / 2;
1181 if (bPlanes * bBitsPixel > 1)
1182 {
1183 iinfo.hbmColor = CreateBitmap( nWidth, nHeight, bPlanes, bBitsPixel, lpXORbits );
1184 iinfo.hbmMask = CreateBitmap( nWidth, nHeight, 1, 1, lpANDbits );
1185 }
1186 else
1187 {
1188 iinfo.hbmMask = CreateBitmap( nWidth, nHeight * 2, 1, 1, lpANDbits );
1189 iinfo.hbmColor = NULL;
1190 }
1191
1192 hIcon = CreateIconIndirect( &iinfo );
1193
1194 DeleteObject( iinfo.hbmMask );
1195 if (iinfo.hbmColor) DeleteObject( iinfo.hbmColor );
1196
1197 return hIcon;
1198 }
1199
1200
1201 /***********************************************************************
1202 * CopyIcon (USER32.@)
1203 */
1204 HICON WINAPI CopyIcon( HICON hIcon )
1205 {
1206 HICON hRetIcon = NULL;
1207 ICONINFO IconInfo;
1208
1209 if(GetIconInfo(hIcon, &IconInfo))
1210 {
1211 hRetIcon = CreateIconIndirect(&IconInfo);
1212 DeleteObject(IconInfo.hbmColor);
1213 DeleteObject(IconInfo.hbmMask);
1214 }
1215
1216 return hRetIcon;
1217 }
1218
1219
1220 /***********************************************************************
1221 * DestroyIcon (USER32.@)
1222 */
1223 BOOL WINAPI DestroyIcon( HICON hIcon )
1224 {
1225 TRACE_(icon)("%p\n", hIcon );
1226
1227 return NtUserDestroyCursor(hIcon, 0);
1228 }
1229
1230
1231 /***********************************************************************
1232 * DestroyCursor (USER32.@)
1233 */
1234 BOOL WINAPI DestroyCursor( HCURSOR hCursor )
1235 {
1236 if (GetCursor() == hCursor)
1237 {
1238 WARN_(cursor)("Destroying active cursor!\n" );
1239 return FALSE;
1240 }
1241 return DestroyIcon( hCursor );
1242 }
1243
1244 /***********************************************************************
1245 * DrawIcon (USER32.@)
1246 */
1247 BOOL WINAPI DrawIcon( HDC hdc, INT x, INT y, HICON hIcon )
1248 {
1249 return DrawIconEx( hdc, x, y, hIcon, 0, 0, 0, 0, DI_NORMAL | DI_COMPAT | DI_DEFAULTSIZE );
1250 }
1251
1252 /***********************************************************************
1253 * ShowCursor (USER32.@)
1254 */
1255 INT WINAPI DECLSPEC_HOTPATCH ShowCursor( BOOL bShow )
1256 {
1257 return NtUserxShowCursor(bShow);
1258 }
1259
1260 /***********************************************************************
1261 * GetCursor (USER32.@)
1262 */
1263 HCURSOR WINAPI GetCursor(void)
1264 {
1265 return (HCURSOR)NtUserGetThreadState(THREADSTATE_GETCURSOR);
1266 }
1267
1268
1269 /***********************************************************************
1270 * SetSystemCursor (USER32.@)
1271 */
1272 BOOL WINAPI SetSystemCursor(HCURSOR hcur, DWORD id)
1273 {
1274 FIXME("(%p,%08x),stub!\n", hcur, id);
1275 return TRUE;
1276 }
1277
1278
1279 /**********************************************************************
1280 * LookupIconIdFromDirectoryEx (USER32.@)
1281 */
1282 INT WINAPI LookupIconIdFromDirectoryEx( LPBYTE xdir, BOOL bIcon,
1283 INT width, INT height, UINT cFlag )
1284 {
1285 CURSORICONDIR *dir = (CURSORICONDIR*)xdir;
1286 UINT retVal = 0;
1287 if( dir && !dir->idReserved && (dir->idType & 3) )
1288 {
1289 CURSORICONDIRENTRY* entry;
1290
1291 const HDC hdc = GetDC(0);
1292 const int depth = (cFlag & LR_MONOCHROME) ?
1293 1 : GetDeviceCaps(hdc, BITSPIXEL);
1294 ReleaseDC(0, hdc);
1295
1296 if( bIcon )
1297 entry = CURSORICON_FindBestIconRes( dir, ~0u, width, height, depth );
1298 else
1299 entry = CURSORICON_FindBestCursorRes( dir, ~0u, width, height, depth );
1300
1301 if( entry ) retVal = entry->wResId;
1302 }
1303 else WARN_(cursor)("invalid resource directory\n");
1304 return retVal;
1305 }
1306
1307 /**********************************************************************
1308 * LookupIconIdFromDirectory (USER32.@)
1309 */
1310 INT WINAPI LookupIconIdFromDirectory( LPBYTE dir, BOOL bIcon )
1311 {
1312 return LookupIconIdFromDirectoryEx( dir, bIcon,
1313 bIcon ? GetSystemMetrics(SM_CXICON) : GetSystemMetrics(SM_CXCURSOR),
1314 bIcon ? GetSystemMetrics(SM_CYICON) : GetSystemMetrics(SM_CYCURSOR), bIcon ? 0 : LR_MONOCHROME );
1315 }
1316
1317 /***********************************************************************
1318 * LoadCursorW (USER32.@)
1319 */
1320 HCURSOR WINAPI LoadCursorW(HINSTANCE hInstance, LPCWSTR name)
1321 {
1322 TRACE("%p, %s\n", hInstance, debugstr_w(name));
1323
1324 return LoadImageW( hInstance, name, IMAGE_CURSOR, 0, 0,
1325 LR_SHARED | LR_DEFAULTSIZE );
1326 }
1327
1328 /***********************************************************************
1329 * LoadCursorA (USER32.@)
1330 */
1331 HCURSOR WINAPI LoadCursorA(HINSTANCE hInstance, LPCSTR name)
1332 {
1333 TRACE("%p, %s\n", hInstance, debugstr_a(name));
1334
1335 return LoadImageA( hInstance, name, IMAGE_CURSOR, 0, 0,
1336 LR_SHARED | LR_DEFAULTSIZE );
1337 }
1338
1339 /***********************************************************************
1340 * LoadCursorFromFileW (USER32.@)
1341 */
1342 HCURSOR WINAPI LoadCursorFromFileW (LPCWSTR name)
1343 {
1344 TRACE("%s\n", debugstr_w(name));
1345
1346 return LoadImageW( 0, name, IMAGE_CURSOR, 0, 0,
1347 LR_LOADFROMFILE | LR_DEFAULTSIZE );
1348 }
1349
1350 /***********************************************************************
1351 * LoadCursorFromFileA (USER32.@)
1352 */
1353 HCURSOR WINAPI LoadCursorFromFileA (LPCSTR name)
1354 {
1355 TRACE("%s\n", debugstr_a(name));
1356
1357 return LoadImageA( 0, name, IMAGE_CURSOR, 0, 0,
1358 LR_LOADFROMFILE | LR_DEFAULTSIZE );
1359 }
1360
1361 /***********************************************************************
1362 * LoadIconW (USER32.@)
1363 */
1364 HICON WINAPI LoadIconW(HINSTANCE hInstance, LPCWSTR name)
1365 {
1366 TRACE("%p, %s\n", hInstance, debugstr_w(name));
1367
1368 return LoadImageW( hInstance, name, IMAGE_ICON, 0, 0,
1369 LR_SHARED | LR_DEFAULTSIZE );
1370 }
1371
1372 /***********************************************************************
1373 * LoadIconA (USER32.@)
1374 */
1375 HICON WINAPI LoadIconA(HINSTANCE hInstance, LPCSTR name)
1376 {
1377 TRACE("%p, %s\n", hInstance, debugstr_a(name));
1378
1379 return LoadImageA( hInstance, name, IMAGE_ICON, 0, 0,
1380 LR_SHARED | LR_DEFAULTSIZE );
1381 }
1382
1383 /**********************************************************************
1384 * GetIconInfo (USER32.@)
1385 */
1386 BOOL WINAPI GetIconInfo(HICON hIcon, PICONINFO iconinfo)
1387 {
1388 return NtUserGetIconInfo(hIcon, iconinfo, 0, 0, 0, 0);
1389 }
1390
1391 /* copy an icon bitmap, even when it can't be selected into a DC */
1392 /* helper for CreateIconIndirect */
1393 static void stretch_blt_icon( HDC hdc_dst, int dst_x, int dst_y, int dst_width, int dst_height,
1394 HBITMAP src, int width, int height )
1395 {
1396 HDC hdc = CreateCompatibleDC( 0 );
1397
1398 if (!SelectObject( hdc, src )) /* do it the hard way */
1399 {
1400 BITMAPINFO *info;
1401 void *bits;
1402
1403 if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) return;
1404 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1405 info->bmiHeader.biWidth = width;
1406 info->bmiHeader.biHeight = height;
1407 info->bmiHeader.biPlanes = GetDeviceCaps( hdc_dst, PLANES );
1408 info->bmiHeader.biBitCount = GetDeviceCaps( hdc_dst, BITSPIXEL );
1409 info->bmiHeader.biCompression = BI_RGB;
1410 info->bmiHeader.biSizeImage = height * get_dib_width_bytes( width, info->bmiHeader.biBitCount );
1411 info->bmiHeader.biXPelsPerMeter = 0;
1412 info->bmiHeader.biYPelsPerMeter = 0;
1413 info->bmiHeader.biClrUsed = 0;
1414 info->bmiHeader.biClrImportant = 0;
1415 bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage );
1416 if (bits && GetDIBits( hdc, src, 0, height, bits, info, DIB_RGB_COLORS ))
1417 StretchDIBits( hdc_dst, dst_x, dst_y, dst_width, dst_height,
1418 0, 0, width, height, bits, info, DIB_RGB_COLORS, SRCCOPY );
1419
1420 HeapFree( GetProcessHeap(), 0, bits );
1421 HeapFree( GetProcessHeap(), 0, info );
1422 }
1423 else StretchBlt( hdc_dst, dst_x, dst_y, dst_width, dst_height, hdc, 0, 0, width, height, SRCCOPY );
1424
1425 DeleteDC( hdc );
1426 }
1427
1428 /**********************************************************************
1429 * CreateIconIndirect (USER32.@)
1430 */
1431 HICON WINAPI CreateIconIndirect(PICONINFO iconinfo)
1432 {
1433 BITMAP bmpXor, bmpAnd;
1434 HBITMAP color = 0, mask;
1435 int width, height;
1436 HDC hdc;
1437 ICONINFO iinfo;
1438
1439 TRACE("color %p, mask %p, hotspot %ux%u, fIcon %d\n",
1440 iconinfo->hbmColor, iconinfo->hbmMask,
1441 iconinfo->xHotspot, iconinfo->yHotspot, iconinfo->fIcon);
1442
1443 if (!iconinfo->hbmMask) return 0;
1444
1445 GetObjectW( iconinfo->hbmMask, sizeof(bmpAnd), &bmpAnd );
1446 TRACE("mask: width %d, height %d, width bytes %d, planes %u, bpp %u\n",
1447 bmpAnd.bmWidth, bmpAnd.bmHeight, bmpAnd.bmWidthBytes,
1448 bmpAnd.bmPlanes, bmpAnd.bmBitsPixel);
1449
1450 if (iconinfo->hbmColor)
1451 {
1452 GetObjectW( iconinfo->hbmColor, sizeof(bmpXor), &bmpXor );
1453 TRACE("color: width %d, height %d, width bytes %d, planes %u, bpp %u\n",
1454 bmpXor.bmWidth, bmpXor.bmHeight, bmpXor.bmWidthBytes,
1455 bmpXor.bmPlanes, bmpXor.bmBitsPixel);
1456
1457 // the size of the mask bitmap always determines the icon size!
1458 width = bmpAnd.bmWidth;
1459 height = bmpAnd.bmHeight;
1460 if (bmpXor.bmPlanes * bmpXor.bmBitsPixel != 1 )
1461 {
1462 color = CreateBitmap( width, height, bmpXor.bmPlanes, bmpXor.bmBitsPixel, NULL );
1463 if(!color)
1464 {
1465 ERR("Unable to create color bitmap!\n");
1466 return NULL;
1467 }
1468 mask = CreateBitmap( width, height, 1, 1, NULL );
1469 if(!mask)
1470 {
1471 ERR("Unable to create mask bitmap!\n");
1472 DeleteObject(color);
1473 return NULL;
1474 }
1475 }
1476 else
1477 {
1478 mask = CreateBitmap( width, height * 2, 1, 1, NULL );
1479 if(!mask)
1480 {
1481 ERR("Unable to create mask bitmap!\n");
1482 return NULL;
1483 }
1484 }
1485 }
1486 else
1487 {
1488 width = bmpAnd.bmWidth;
1489 height = bmpAnd.bmHeight;
1490 mask = CreateBitmap( width, height, 1, 1, NULL );
1491 }
1492
1493 hdc = CreateCompatibleDC( 0 );
1494 SelectObject( hdc, mask );
1495 stretch_blt_icon( hdc, 0, 0, width, height, iconinfo->hbmMask, bmpAnd.bmWidth, bmpAnd.bmHeight );
1496
1497 if (color)
1498 {
1499 SelectObject( hdc, color );
1500 stretch_blt_icon( hdc, 0, 0, width, height, iconinfo->hbmColor, width, height );
1501 }
1502 else if (iconinfo->hbmColor)
1503 {
1504 stretch_blt_icon( hdc, 0, height, width, height, iconinfo->hbmColor, width, height );
1505 }
1506 else height /= 2;
1507
1508 DeleteDC( hdc );
1509
1510 iinfo.hbmColor = color ;
1511 iinfo.hbmMask = mask ;
1512 iinfo.fIcon = iconinfo->fIcon;
1513 if (iinfo.fIcon)
1514 {
1515 iinfo.xHotspot = width / 2;
1516 iinfo.yHotspot = height / 2;
1517 }
1518 else
1519 {
1520 iinfo.xHotspot = iconinfo->xHotspot;
1521 iinfo.yHotspot = iconinfo->yHotspot;
1522 }
1523
1524 return CreateCursorIconHandle(&iinfo);
1525 }
1526
1527 /******************************************************************************
1528 * DrawIconEx (USER32.@) Draws an icon or cursor on device context
1529 *
1530 * NOTES
1531 * Why is this using SM_CXICON instead of SM_CXCURSOR?
1532 *
1533 * PARAMS
1534 * hdc [I] Handle to device context
1535 * x0 [I] X coordinate of upper left corner
1536 * y0 [I] Y coordinate of upper left corner
1537 * hIcon [I] Handle to icon to draw
1538 * cxWidth [I] Width of icon
1539 * cyWidth [I] Height of icon
1540 * istep [I] Index of frame in animated cursor
1541 * hbr [I] Handle to background brush
1542 * flags [I] Icon-drawing flags
1543 *
1544 * RETURNS
1545 * Success: TRUE
1546 * Failure: FALSE
1547 */
1548 BOOL WINAPI DrawIconEx( HDC hdc, INT xLeft, INT yTop, HICON hIcon,
1549 INT cxWidth, INT cyWidth, UINT istepIfAniCur,
1550 HBRUSH hbrFlickerFreeDraw, UINT diFlags )
1551 {
1552 return NtUserDrawIconEx(hdc, xLeft, yTop, hIcon, cxWidth, cyWidth,
1553 istepIfAniCur, hbrFlickerFreeDraw, diFlags,
1554 0, 0);
1555 }
1556
1557 /***********************************************************************
1558 * DIB_FixColorsToLoadflags
1559 *
1560 * Change color table entries when LR_LOADTRANSPARENT or LR_LOADMAP3DCOLORS
1561 * are in loadflags
1562 */
1563 static void DIB_FixColorsToLoadflags(BITMAPINFO * bmi, UINT loadflags, BYTE pix)
1564 {
1565 int colors;
1566 COLORREF c_W, c_S, c_F, c_L, c_C;
1567 int incr,i;
1568 RGBQUAD *ptr;
1569 int bitmap_type;
1570 LONG width;
1571 LONG height;
1572 WORD bpp;
1573 DWORD compr;
1574
1575 if (((bitmap_type = DIB_GetBitmapInfo((BITMAPINFOHEADER*) bmi, &width, &height, &bpp, &compr)) == -1))
1576 {
1577 WARN_(resource)("Invalid bitmap\n");
1578 return;
1579 }
1580
1581 if (bpp > 8) return;
1582
1583 if (bitmap_type == 0) /* BITMAPCOREHEADER */
1584 {
1585 incr = 3;
1586 colors = 1 << bpp;
1587 }
1588 else
1589 {
1590 incr = 4;
1591 colors = bmi->bmiHeader.biClrUsed;
1592 if (colors > 256) colors = 256;
1593 if (!colors && (bpp <= 8)) colors = 1 << bpp;
1594 }
1595
1596 c_W = GetSysColor(COLOR_WINDOW);
1597 c_S = GetSysColor(COLOR_3DSHADOW);
1598 c_F = GetSysColor(COLOR_3DFACE);
1599 c_L = GetSysColor(COLOR_3DLIGHT);
1600
1601 if (loadflags & LR_LOADTRANSPARENT) {
1602 switch (bpp) {
1603 case 1: pix = pix >> 7; break;
1604 case 4: pix = pix >> 4; break;
1605 case 8: break;
1606 default:
1607 WARN_(resource)("(%d): Unsupported depth\n", bpp);
1608 return;
1609 }
1610 if (pix >= colors) {
1611 WARN_(resource)("pixel has color index greater than biClrUsed!\n");
1612 return;
1613 }
1614 if (loadflags & LR_LOADMAP3DCOLORS) c_W = c_F;
1615 ptr = (RGBQUAD*)((char*)bmi->bmiColors+pix*incr);
1616 ptr->rgbBlue = GetBValue(c_W);
1617 ptr->rgbGreen = GetGValue(c_W);
1618 ptr->rgbRed = GetRValue(c_W);
1619 }
1620 if (loadflags & LR_LOADMAP3DCOLORS)
1621 for (i=0; i<colors; i++) {
1622 ptr = (RGBQUAD*)((char*)bmi->bmiColors+i*incr);
1623 c_C = RGB(ptr->rgbRed, ptr->rgbGreen, ptr->rgbBlue);
1624 if (c_C == RGB(128, 128, 128)) {
1625 ptr->rgbRed = GetRValue(c_S);
1626 ptr->rgbGreen = GetGValue(c_S);
1627 ptr->rgbBlue = GetBValue(c_S);
1628 } else if (c_C == RGB(192, 192, 192)) {
1629 ptr->rgbRed = GetRValue(c_F);
1630 ptr->rgbGreen = GetGValue(c_F);
1631 ptr->rgbBlue = GetBValue(c_F);
1632 } else if (c_C == RGB(223, 223, 223)) {
1633 ptr->rgbRed = GetRValue(c_L);
1634 ptr->rgbGreen = GetGValue(c_L);
1635 ptr->rgbBlue = GetBValue(c_L);
1636 }
1637 }
1638 }
1639
1640
1641 /**********************************************************************
1642 * BITMAP_Load
1643 */
1644 static HBITMAP BITMAP_Load( HINSTANCE instance, LPCWSTR name,
1645 INT desiredx, INT desiredy, UINT loadflags )
1646 {
1647 HBITMAP hbitmap = 0, orig_bm;
1648 HRSRC hRsrc;
1649 HGLOBAL handle;
1650 char *ptr = NULL;
1651 BITMAPINFO *info, *fix_info = NULL, *scaled_info = NULL;
1652 int size;
1653 BYTE pix;
1654 char *bits;
1655 LONG width, height, new_width, new_height;
1656 WORD bpp_dummy;
1657 DWORD compr_dummy, offbits = 0;
1658 INT bm_type;
1659 HDC screen_mem_dc = NULL;
1660
1661 if (!(loadflags & LR_LOADFROMFILE))
1662 {
1663 if (!instance)
1664 {
1665 /* OEM bitmap: try to load the resource from user32.dll */
1666 instance = User32Instance;
1667 }
1668
1669 if (!(hRsrc = FindResourceW( instance, name, (LPWSTR)RT_BITMAP ))) return 0;
1670 if (!(handle = LoadResource( instance, hRsrc ))) return 0;
1671
1672 if ((info = LockResource( handle )) == NULL) return 0;
1673 }
1674 else
1675 {
1676 BITMAPFILEHEADER * bmfh;
1677
1678 if (!(ptr = map_fileW( name, NULL ))) return 0;
1679 info = (BITMAPINFO *)(ptr + sizeof(BITMAPFILEHEADER));
1680 bmfh = (BITMAPFILEHEADER *)ptr;
1681 if (bmfh->bfType != 0x4d42 /* 'BM' */)
1682 {
1683 WARN("Invalid/unsupported bitmap format!\n");
1684 goto end_close;
1685 }
1686 if (bmfh->bfOffBits) offbits = bmfh->bfOffBits - sizeof(BITMAPFILEHEADER);
1687 }
1688
1689 size = bitmap_info_size(info, DIB_RGB_COLORS);
1690 fix_info = HeapAlloc(GetProcessHeap(), 0, size);
1691 scaled_info = HeapAlloc(GetProcessHeap(), 0, size);
1692
1693 if (!fix_info || !scaled_info) goto end;
1694 memcpy(fix_info, info, size);
1695
1696 pix = *((LPBYTE)info + size);
1697 DIB_FixColorsToLoadflags(fix_info, loadflags, pix);
1698
1699 memcpy(scaled_info, fix_info, size);
1700 bm_type = DIB_GetBitmapInfo( &fix_info->bmiHeader, &width, &height,
1701 &bpp_dummy, &compr_dummy);
1702 if(desiredx != 0)
1703 new_width = desiredx;
1704 else
1705 new_width = width;
1706
1707 if(desiredy != 0)
1708 new_height = height > 0 ? desiredy : -desiredy;
1709 else
1710 new_height = height;
1711
1712 if (bm_type == 0)
1713 {
1714 BITMAPCOREHEADER *core = (BITMAPCOREHEADER *)&scaled_info->bmiHeader;
1715 core->bcWidth = new_width;
1716 core->bcHeight = new_height;
1717 }
1718 else if (bm_type == 1)
1719 {
1720 /* Some sanity checks for BITMAPINFO (not applicable to BITMAPCOREINFO) */
1721 if (info->bmiHeader.biHeight > 65535 || info->bmiHeader.biWidth > 65535) {
1722 WARN("Broken BitmapInfoHeader!\n");
1723 goto end;
1724 }
1725
1726 scaled_info->bmiHeader.biWidth = new_width;
1727 scaled_info->bmiHeader.biHeight = new_height;
1728 }
1729 else
1730 goto end;
1731
1732 if (new_height < 0) new_height = -new_height;
1733
1734 if (!screen_dc) screen_dc = CreateDCW( DISPLAYW, NULL, NULL, NULL );
1735 if (!(screen_mem_dc = CreateCompatibleDC( screen_dc ))) goto end;
1736
1737 bits = (char *)info + (offbits ? offbits : size);
1738
1739 if (loadflags & LR_CREATEDIBSECTION)
1740 {
1741 scaled_info->bmiHeader.biCompression = 0; /* DIBSection can't be compressed */
1742 hbitmap = CreateDIBSection(screen_dc, scaled_info, DIB_RGB_COLORS, NULL, 0, 0);
1743 }
1744 else
1745 {
1746 if (is_dib_monochrome(fix_info))
1747 hbitmap = CreateBitmap(new_width, new_height, 1, 1, NULL);
1748 else
1749 hbitmap = CreateCompatibleBitmap(screen_dc, new_width, new_height);
1750 }
1751
1752 orig_bm = SelectObject(screen_mem_dc, hbitmap);
1753 StretchDIBits(screen_mem_dc, 0, 0, new_width, new_height, 0, 0, width, height, bits, fix_info, DIB_RGB_COLORS, SRCCOPY);
1754 SelectObject(screen_mem_dc, orig_bm);
1755
1756 end:
1757 if (screen_mem_dc) DeleteDC(screen_mem_dc);
1758 HeapFree(GetProcessHeap(), 0, scaled_info);
1759 HeapFree(GetProcessHeap(), 0, fix_info);
1760 end_close:
1761 if (loadflags & LR_LOADFROMFILE) UnmapViewOfFile( ptr );
1762
1763 return hbitmap;
1764 }
1765
1766 /**********************************************************************
1767 * LoadImageA (USER32.@)
1768 *
1769 * See LoadImageW.
1770 */
1771 HANDLE WINAPI LoadImageA( HINSTANCE hinst, LPCSTR name, UINT type,
1772 INT desiredx, INT desiredy, UINT loadflags)
1773 {
1774 HANDLE res;
1775 LPWSTR u_name;
1776
1777 if (IS_INTRESOURCE(name))
1778 return LoadImageW(hinst, (LPCWSTR)name, type, desiredx, desiredy, loadflags);
1779
1780 _SEH2_TRY
1781 {
1782 DWORD len = MultiByteToWideChar( CP_ACP, 0, name, -1, NULL, 0 );
1783 u_name = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1784 MultiByteToWideChar( CP_ACP, 0, name, -1, u_name, len );
1785 }
1786 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1787 {
1788 SetLastError( ERROR_INVALID_PARAMETER );
1789 _SEH2_YIELD(return 0);
1790 }
1791 _SEH2_END
1792 res = LoadImageW(hinst, u_name, type, desiredx, desiredy, loadflags);
1793 HeapFree(GetProcessHeap(), 0, u_name);
1794 return res;
1795 }
1796
1797
1798 /******************************************************************************
1799 * LoadImageW (USER32.@) Loads an icon, cursor, or bitmap
1800 *
1801 * PARAMS
1802 * hinst [I] Handle of instance that contains image
1803 * name [I] Name of image
1804 * type [I] Type of image
1805 * desiredx [I] Desired width
1806 * desiredy [I] Desired height
1807 * loadflags [I] Load flags
1808 *
1809 * RETURNS
1810 * Success: Handle to newly loaded image
1811 * Failure: NULL
1812 *
1813 * FIXME: Implementation lacks some features, see LR_ defines in winuser.h
1814 */
1815 HANDLE WINAPI LoadImageW( HINSTANCE hinst, LPCWSTR name, UINT type,
1816 INT desiredx, INT desiredy, UINT loadflags )
1817 {
1818 TRACE_(resource)("(%p,%s,%d,%d,%d,0x%08x)\n",
1819 hinst,debugstr_w(name),type,desiredx,desiredy,loadflags);
1820
1821 if (loadflags & LR_DEFAULTSIZE) {
1822 if (type == IMAGE_ICON) {
1823 if (!desiredx) desiredx = GetSystemMetrics(SM_CXICON);
1824 if (!desiredy) desiredy = GetSystemMetrics(SM_CYICON);
1825 } else if (type == IMAGE_CURSOR) {
1826 if (!desiredx) desiredx = GetSystemMetrics(SM_CXCURSOR);
1827 if (!desiredy) desiredy = GetSystemMetrics(SM_CYCURSOR);
1828 }
1829 }
1830 if (loadflags & LR_LOADFROMFILE) loadflags &= ~LR_SHARED;
1831 switch (type) {
1832 case IMAGE_BITMAP:
1833 return BITMAP_Load( hinst, name, desiredx, desiredy, loadflags );
1834
1835 case IMAGE_ICON:
1836 if (!screen_dc) screen_dc = CreateDCW( DISPLAYW, NULL, NULL, NULL );
1837 if (screen_dc)
1838 {
1839 return CURSORICON_Load(hinst, name, desiredx, desiredy,
1840 GetDeviceCaps(screen_dc, BITSPIXEL),
1841 FALSE, loadflags);
1842 }
1843 break;
1844
1845 case IMAGE_CURSOR:
1846 return CURSORICON_Load(hinst, name, desiredx, desiredy,
1847 1, TRUE, loadflags);
1848 }
1849 return 0;
1850 }
1851
1852 /******************************************************************************
1853 * CopyImage (USER32.@) Creates new image and copies attributes to it
1854 *
1855 * PARAMS
1856 * hnd [I] Handle to image to copy
1857 * type [I] Type of image to copy
1858 * desiredx [I] Desired width of new image
1859 * desiredy [I] Desired height of new image
1860 * flags [I] Copy flags
1861 *
1862 * RETURNS
1863 * Success: Handle to newly created image
1864 * Failure: NULL
1865 *
1866 * BUGS
1867 * Only Windows NT 4.0 supports the LR_COPYRETURNORG flag for bitmaps,
1868 * all other versions (95/2000/XP have been tested) ignore it.
1869 *
1870 * NOTES
1871 * If LR_CREATEDIBSECTION is absent, the copy will be monochrome for
1872 * a monochrome source bitmap or if LR_MONOCHROME is present, otherwise
1873 * the copy will have the same depth as the screen.
1874 * The content of the image will only be copied if the bit depth of the
1875 * original image is compatible with the bit depth of the screen, or
1876 * if the source is a DIB section.
1877 * The LR_MONOCHROME flag is ignored if LR_CREATEDIBSECTION is present.
1878 */
1879 HANDLE WINAPI CopyImage( HANDLE hnd, UINT type, INT desiredx,
1880 INT desiredy, UINT flags )
1881 {
1882 TRACE("hnd=%p, type=%u, desiredx=%d, desiredy=%d, flags=%x\n",
1883 hnd, type, desiredx, desiredy, flags);
1884
1885 switch (type)
1886 {
1887 case IMAGE_BITMAP:
1888 {
1889 HBITMAP res = NULL;
1890 DIBSECTION ds;
1891 int objSize;
1892 BITMAPINFO * bi;
1893
1894 objSize = GetObjectW( hnd, sizeof(ds), &ds );
1895 if (!objSize) return 0;
1896 if ((desiredx < 0) || (desiredy < 0)) return 0;
1897
1898 if (flags & LR_COPYFROMRESOURCE)
1899 {
1900 FIXME("The flag LR_COPYFROMRESOURCE is not implemented for bitmaps\n");
1901 }
1902
1903 if (desiredx == 0) desiredx = ds.dsBm.bmWidth;
1904 if (desiredy == 0) desiredy = ds.dsBm.bmHeight;
1905
1906 /* Allocate memory for a BITMAPINFOHEADER structure and a
1907 color table. The maximum number of colors in a color table
1908 is 256 which corresponds to a bitmap with depth 8.
1909 Bitmaps with higher depths don't have color tables. */
1910 bi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
1911 if (!bi) return 0;
1912
1913 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
1914 bi->bmiHeader.biPlanes = ds.dsBm.bmPlanes;
1915 bi->bmiHeader.biBitCount = ds.dsBm.bmBitsPixel;
1916 bi->bmiHeader.biCompression = BI_RGB;
1917
1918 if (flags & LR_CREATEDIBSECTION)
1919 {
1920 /* Create a DIB section. LR_MONOCHROME is ignored */
1921 void * bits;
1922 HDC dc = CreateCompatibleDC(NULL);
1923
1924 if (objSize == sizeof(DIBSECTION))
1925 {
1926 /* The source bitmap is a DIB.
1927 Get its attributes to create an exact copy */
1928 memcpy(bi, &ds.dsBmih, sizeof(BITMAPINFOHEADER));
1929 }
1930
1931 /* Get the color table or the color masks */
1932 GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
1933
1934 bi->bmiHeader.biWidth = desiredx;
1935 bi->bmiHeader.biHeight = desiredy;
1936 bi->bmiHeader.biSizeImage = 0;
1937
1938 res = CreateDIBSection(dc, bi, DIB_RGB_COLORS, &bits, NULL, 0);
1939 DeleteDC(dc);
1940 }
1941 else
1942 {
1943 /* Create a device-dependent bitmap */
1944
1945 BOOL monochrome = (flags & LR_MONOCHROME);
1946
1947 if (objSize == sizeof(DIBSECTION))
1948 {
1949 /* The source bitmap is a DIB section.
1950 Get its attributes */
1951 HDC dc = CreateCompatibleDC(NULL);
1952 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
1953 bi->bmiHeader.biBitCount = ds.dsBm.bmBitsPixel;
1954 GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
1955 DeleteDC(dc);
1956
1957 if (!monochrome && ds.dsBm.bmBitsPixel == 1)
1958 {
1959 /* Look if the colors of the DIB are black and white */
1960
1961 monochrome =
1962 (bi->bmiColors[0].rgbRed == 0xff
1963 && bi->bmiColors[0].rgbGreen == 0xff
1964 && bi->bmiColors[0].rgbBlue == 0xff
1965 && bi->bmiColors[0].rgbReserved == 0
1966 && bi->bmiColors[1].rgbRed == 0
1967 && bi->bmiColors[1].rgbGreen == 0
1968 && bi->bmiColors[1].rgbBlue == 0
1969 && bi->bmiColors[1].rgbReserved == 0)
1970 ||
1971 (bi->bmiColors[0].rgbRed == 0
1972 && bi->bmiColors[0].rgbGreen == 0
1973 && bi->bmiColors[0].rgbBlue == 0
1974 && bi->bmiColors[0].rgbReserved == 0
1975 && bi->bmiColors[1].rgbRed == 0xff
1976 && bi->bmiColors[1].rgbGreen == 0xff
1977 && bi->bmiColors[1].rgbBlue == 0xff
1978 && bi->bmiColors[1].rgbReserved == 0);
1979 }
1980 }
1981 else if (!monochrome)
1982 {
1983 monochrome = ds.dsBm.bmBitsPixel == 1;
1984 }
1985
1986 if (monochrome)
1987 {
1988 res = CreateBitmap(desiredx, desiredy, 1, 1, NULL);
1989 }
1990 else
1991 {
1992 HDC screenDC = GetDC(NULL);
1993 res = CreateCompatibleBitmap(screenDC, desiredx, desiredy);
1994 ReleaseDC(NULL, screenDC);
1995 }
1996 }
1997
1998 if (res)
1999 {
2000 /* Only copy the bitmap if it's a DIB section or if it's
2001 compatible to the screen */
2002 BOOL copyContents;
2003
2004 if (objSize == sizeof(DIBSECTION))
2005 {
2006 copyContents = TRUE;
2007 }
2008 else
2009 {
2010 HDC screenDC = GetDC(NULL);
2011 int screen_depth = GetDeviceCaps(screenDC, BITSPIXEL);
2012 ReleaseDC(NULL, screenDC);
2013
2014 copyContents = (ds.dsBm.bmBitsPixel == 1 || ds.dsBm.bmBitsPixel == screen_depth);
2015 }
2016
2017 if (copyContents)
2018 {
2019 /* The source bitmap may already be selected in a device context,
2020 use GetDIBits/StretchDIBits and not StretchBlt */
2021
2022 HDC dc;
2023 void * bits;
2024
2025 dc = CreateCompatibleDC(NULL);
2026
2027 bi->bmiHeader.biWidth = ds.dsBm.bmWidth;
2028 bi->bmiHeader.biHeight = ds.dsBm.bmHeight;
2029 bi->bmiHeader.biSizeImage = 0;
2030 bi->bmiHeader.biClrUsed = 0;
2031 bi->bmiHeader.biClrImportant = 0;
2032
2033 /* Fill in biSizeImage */
2034 GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, NULL, bi, DIB_RGB_COLORS);
2035 bits = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bi->bmiHeader.biSizeImage);
2036
2037 if (bits)
2038 {
2039 HBITMAP oldBmp;
2040
2041 /* Get the image bits of the source bitmap */
2042 GetDIBits(dc, hnd, 0, ds.dsBm.bmHeight, bits, bi, DIB_RGB_COLORS);
2043
2044 /* Copy it to the destination bitmap */
2045 oldBmp = SelectObject(dc, res);
2046 StretchDIBits(dc, 0, 0, desiredx, desiredy,
2047 0, 0, ds.dsBm.bmWidth, ds.dsBm.bmHeight,
2048 bits, bi, DIB_RGB_COLORS, SRCCOPY);
2049 SelectObject(dc, oldBmp);
2050
2051 HeapFree(GetProcessHeap(), 0, bits);
2052 }
2053
2054 DeleteDC(dc);
2055 }
2056
2057 if (flags & LR_COPYDELETEORG)
2058 {
2059 DeleteObject(hnd);
2060 }
2061 }
2062 HeapFree(GetProcessHeap(), 0, bi);
2063 return res;
2064 }
2065 case IMAGE_ICON:
2066 return CURSORICON_ExtCopy(hnd,type, desiredx, desiredy, flags);
2067 case IMAGE_CURSOR:
2068 /* Should call CURSORICON_ExtCopy but more testing
2069 * needs to be done before we change this
2070 */
2071 if (flags) FIXME("Flags are ignored\n");
2072 return CopyCursor(hnd);
2073 }
2074 return 0;
2075 }
2076
2077
2078 /******************************************************************************
2079 * LoadBitmapW (USER32.@) Loads bitmap from the executable file
2080 *
2081 * RETURNS
2082 * Success: Handle to specified bitmap
2083 * Failure: NULL
2084 */
2085 HBITMAP WINAPI LoadBitmapW(
2086 HINSTANCE instance, /* [in] Handle to application instance */
2087 LPCWSTR name) /* [in] Address of bitmap resource name */
2088 {
2089 return LoadImageW( instance, name, IMAGE_BITMAP, 0, 0, 0 );
2090 }
2091
2092 /**********************************************************************
2093 * LoadBitmapA (USER32.@)
2094 *
2095 * See LoadBitmapW.
2096 */
2097 HBITMAP WINAPI LoadBitmapA( HINSTANCE instance, LPCSTR name )
2098 {
2099 return LoadImageA( instance, name, IMAGE_BITMAP, 0, 0, 0 );
2100 }
2101
2102 HCURSOR
2103 CursorIconToCursor(HICON hIcon,
2104 BOOL SemiTransparent)
2105 {
2106 UNIMPLEMENTED;
2107 return 0;
2108 }
2109
2110 /*
2111 * @implemented
2112 */
2113 BOOL
2114 WINAPI
2115 DECLSPEC_HOTPATCH
2116 SetCursorPos(int X, int Y)
2117 {
2118 return NtUserxSetCursorPos(X,Y);
2119 }
2120
2121 /*
2122 * @implemented
2123 */
2124 BOOL
2125 WINAPI
2126 DECLSPEC_HOTPATCH
2127 GetCursorPos(LPPOINT lpPoint)
2128 {
2129 BOOL res;
2130 /* Windows doesn't check if lpPoint == NULL, we do */
2131 if(!lpPoint)
2132 {
2133 SetLastError(ERROR_INVALID_PARAMETER);
2134 return FALSE;
2135 }
2136
2137 res = NtUserxGetCursorPos(lpPoint);
2138
2139 return res;
2140 }
2141
2142 /* INTERNAL ******************************************************************/
2143
2144 /* This callback routine is called directly after switching to gui mode */
2145 NTSTATUS
2146 WINAPI
2147 User32SetupDefaultCursors(PVOID Arguments,
2148 ULONG ArgumentLength)
2149 {
2150 BOOL *DefaultCursor = (BOOL*)Arguments;
2151 HCURSOR hCursor;
2152
2153 if(*DefaultCursor)
2154 {
2155 /* set default cursor */
2156 hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
2157 SetCursor(hCursor);
2158 }
2159 else
2160 {
2161 /* FIXME load system cursor scheme */
2162 SetCursor(0);
2163 hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
2164 SetCursor(hCursor);
2165 }
2166
2167 return(ZwCallbackReturn(&hCursor, sizeof(HCURSOR), STATUS_SUCCESS));
2168 }
2169
2170 BOOL get_icon_size(HICON hIcon, SIZE *size)
2171 {
2172 ICONINFO info;
2173 BITMAP bitmap;
2174
2175 if (!GetIconInfo(hIcon, &info)) return FALSE;
2176 if (!GetObject(info.hbmMask, sizeof(bitmap), &bitmap)) return FALSE;
2177
2178 size->cx = bitmap.bmWidth;
2179 size->cy = bitmap.bmHeight;
2180
2181 /* Black and white icons store both the XOR and AND bitmap in hbmMask */
2182 if (!info.hbmColor)
2183 {
2184 size->cy /= 2;
2185 }
2186 else
2187 {
2188 DeleteObject(info.hbmColor);
2189 }
2190
2191 DeleteObject(info.hbmMask);
2192
2193 return TRUE;
2194 }
2195
2196 NTSTATUS WINAPI
2197 User32CallCopyImageFromKernel(PVOID Arguments, ULONG ArgumentLength)
2198 {
2199 PCOPYIMAGE_CALLBACK_ARGUMENTS Common;
2200 HANDLE Result;
2201 Common = (PCOPYIMAGE_CALLBACK_ARGUMENTS) Arguments;
2202
2203 Result = CopyImage(Common->hImage,
2204 Common->uType,
2205 Common->cxDesired,
2206 Common->cyDesired,
2207 Common->fuFlags);
2208
2209 return ZwCallbackReturn(&Result, sizeof(HANDLE), STATUS_SUCCESS);
2210 }
2211
2212 HCURSOR
2213 WINAPI
2214 GetCursorFrameInfo(HCURSOR hCursor, DWORD reserved, DWORD istep, PINT rate_jiffies, DWORD *num_steps)
2215 {
2216 return NtUserGetCursorFrameInfo(hCursor, istep, rate_jiffies, num_steps);
2217 }
2218