fix icon loading code to load the correct icon for the current display bpp settings
[reactos.git] / reactos / dll / win32 / user32 / windows / icon.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 *
21 * PROJECT: ReactOS user32.dll
22 * FILE: lib/user32/windows/icon.c
23 * PURPOSE: Icon
24 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
25 * UPDATE HISTORY:
26 * 09-05-2001 CSH Created
27 */
28
29 /* INCLUDES ******************************************************************/
30
31 #include <user32.h>
32
33 #include <wine/debug.h>
34
35
36 /* FUNCTIONS *****************************************************************/
37
38 HICON
39 ICON_CreateIconFromData(HDC hDC, PVOID ImageData, ICONIMAGE* IconImage, int cxDesired, int cyDesired, int xHotspot, int yHotspot)
40 {
41 BYTE BitmapInfoBuffer[sizeof(BITMAPINFOHEADER) + 2 * sizeof(RGBQUAD)];
42 BITMAPINFO *bwBIH = (BITMAPINFO *)BitmapInfoBuffer;
43 ICONINFO IconInfo;
44
45 IconInfo.fIcon = TRUE;
46 IconInfo.xHotspot = xHotspot;
47 IconInfo.yHotspot = yHotspot;
48
49 /* Load the XOR bitmap */
50 IconInfo.hbmColor = CreateDIBitmap(hDC, &IconImage->icHeader, CBM_INIT,
51 ImageData, (BITMAPINFO*)IconImage,
52 DIB_RGB_COLORS);
53
54 /* Make ImageData point to the start of the AND image data. */
55 ImageData = ((PBYTE)ImageData) + (((IconImage->icHeader.biWidth *
56 IconImage->icHeader.biBitCount + 31) & ~31) >> 3) *
57 (IconImage->icHeader.biHeight );
58
59 /* Create a BITMAPINFO header for the monocrome part of the icon. */
60 bwBIH->bmiHeader.biBitCount = 1;
61 bwBIH->bmiHeader.biWidth = IconImage->icHeader.biWidth;
62 bwBIH->bmiHeader.biHeight = IconImage->icHeader.biHeight;
63 bwBIH->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
64 bwBIH->bmiHeader.biPlanes = 1;
65 bwBIH->bmiHeader.biSizeImage = 0;
66 bwBIH->bmiHeader.biCompression = BI_RGB;
67 bwBIH->bmiHeader.biClrImportant = 0;
68 bwBIH->bmiHeader.biClrUsed = 0;
69 bwBIH->bmiHeader.biXPelsPerMeter = 0;
70 bwBIH->bmiHeader.biYPelsPerMeter = 0;
71
72 bwBIH->bmiColors[0].rgbBlue = 0;
73 bwBIH->bmiColors[0].rgbGreen = 0;
74 bwBIH->bmiColors[0].rgbRed = 0;
75 bwBIH->bmiColors[0].rgbReserved = 0;
76
77 bwBIH->bmiColors[1].rgbBlue = 0xff;
78 bwBIH->bmiColors[1].rgbGreen = 0xff;
79 bwBIH->bmiColors[1].rgbRed = 0xff;
80 bwBIH->bmiColors[1].rgbReserved = 0;
81
82 /* Load the AND bitmap. */
83 IconInfo.hbmMask = CreateDIBitmap(hDC, &bwBIH->bmiHeader, 0,
84 ImageData, bwBIH, DIB_RGB_COLORS);
85
86 SetDIBits(hDC, IconInfo.hbmMask, 0, IconImage->icHeader.biHeight,
87 ImageData, bwBIH, DIB_RGB_COLORS);
88
89 /* Create the icon based on everything we have so far */
90 return NtUserCreateCursorIconHandle(&IconInfo, FALSE);
91 }
92
93 HICON
94 ICON_CreateCursorFromData(HDC hDC, PVOID ImageData, ICONIMAGE* IconImage, int cxDesired, int cyDesired, int xHotspot, int yHotspot)
95 {
96 /* FIXME - color cursors */
97 BYTE BitmapInfoBuffer[sizeof(BITMAPINFOHEADER) + 2 * sizeof(RGBQUAD)];
98 BITMAPINFO *bwBIH = (BITMAPINFO *)BitmapInfoBuffer;
99 ICONINFO IconInfo;
100 PVOID XORImageData = ImageData;
101
102 IconInfo.fIcon = FALSE;
103 IconInfo.xHotspot = xHotspot;
104 IconInfo.yHotspot = yHotspot;
105
106 /* Create a BITMAPINFO header for the monocrome part of the icon */
107 bwBIH->bmiHeader.biBitCount = 1;
108 bwBIH->bmiHeader.biWidth = IconImage->icHeader.biWidth;
109 bwBIH->bmiHeader.biHeight = IconImage->icHeader.biHeight;
110 bwBIH->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
111 bwBIH->bmiHeader.biPlanes = 1;
112 bwBIH->bmiHeader.biSizeImage = 0;
113 bwBIH->bmiHeader.biCompression = BI_RGB;
114 bwBIH->bmiHeader.biClrImportant = 0;
115 bwBIH->bmiHeader.biClrUsed = 0;
116 bwBIH->bmiHeader.biXPelsPerMeter = 0;
117 bwBIH->bmiHeader.biYPelsPerMeter = 0;
118
119 bwBIH->bmiColors[0].rgbBlue = 0;
120 bwBIH->bmiColors[0].rgbGreen = 0;
121 bwBIH->bmiColors[0].rgbRed = 0;
122 bwBIH->bmiColors[0].rgbReserved = 0;
123
124 bwBIH->bmiColors[1].rgbBlue = 0xff;
125 bwBIH->bmiColors[1].rgbGreen = 0xff;
126 bwBIH->bmiColors[1].rgbRed = 0xff;
127 bwBIH->bmiColors[1].rgbReserved = 0;
128
129 /* Load the AND bitmap */
130 IconInfo.hbmMask = CreateDIBitmap(hDC, &bwBIH->bmiHeader, 0,
131 XORImageData, bwBIH, DIB_RGB_COLORS);
132 if (IconInfo.hbmMask)
133 {
134 SetDIBits(hDC, IconInfo.hbmMask, 0, IconImage->icHeader.biHeight,
135 XORImageData, bwBIH, DIB_RGB_COLORS);
136 }
137
138 IconInfo.hbmColor = (HBITMAP)0;
139
140 /* Create the icon based on everything we have so far */
141 return NtUserCreateCursorIconHandle(&IconInfo, FALSE);
142 }
143
144
145 /*
146 * @implemented
147 */
148 HICON
149 STDCALL
150 CopyIcon(
151 HICON hIcon)
152 {
153 ICONINFO IconInfo;
154
155 if(NtUserGetCursorIconInfo((HANDLE)hIcon, &IconInfo))
156 {
157 return NtUserCreateCursorIconHandle(&IconInfo, FALSE);
158 }
159 return (HICON)0;
160 }
161
162
163 /*
164 * @implemented
165 */
166 HICON
167 STDCALL
168 CreateIcon(
169 HINSTANCE hInstance,
170 int nWidth,
171 int nHeight,
172 BYTE cPlanes,
173 BYTE cBitsPixel,
174 CONST BYTE *ANDbits,
175 CONST BYTE *XORbits)
176 {
177 ICONINFO IconInfo;
178
179 IconInfo.fIcon = TRUE;
180 IconInfo.xHotspot = nWidth / 2;
181 IconInfo.yHotspot = nHeight / 2;
182 IconInfo.hbmMask = CreateBitmap(nWidth, nHeight, 1, 1, ANDbits);
183 if(!IconInfo.hbmMask)
184 {
185 return (HICON)0;
186 }
187 IconInfo.hbmColor = CreateBitmap(nWidth, nHeight, cPlanes, cBitsPixel, XORbits);
188 if(!IconInfo.hbmColor)
189 {
190 DeleteObject(IconInfo.hbmMask);
191 return (HICON)0;
192 }
193
194 return NtUserCreateCursorIconHandle(&IconInfo, FALSE);
195 }
196
197
198 /*
199 * @implemented
200 */
201 HICON
202 STDCALL
203 CreateIconFromResource(
204 PBYTE presbits,
205 DWORD dwResSize,
206 BOOL fIcon,
207 DWORD dwVer)
208 {
209 return CreateIconFromResourceEx(presbits, dwResSize, fIcon, dwVer, 0, 0, 0);
210 }
211
212
213 /*
214 * @implemented
215 */
216 HICON
217 STDCALL
218 CreateIconFromResourceEx(
219 PBYTE pbIconBits,
220 DWORD cbIconBits,
221 BOOL fIcon,
222 DWORD dwVersion,
223 int cxDesired,
224 int cyDesired,
225 UINT uFlags)
226 {
227 ICONIMAGE* SafeIconImage;
228 HICON hIcon;
229 ULONG HeaderSize;
230 ULONG ColourCount;
231 PVOID Data;
232 HDC hScreenDc;
233 WORD wXHotspot;
234 WORD wYHotspot;
235
236 /*
237 FIXME - does win support LR_SHARED? According to msdn it does but we don't
238 have useful information to identify the icon
239 if (uFlags & LR_SHARED)
240 {
241 DbgPrint("FIXME: need LR_SHARED support in CreateIconFromResourceEx()\n");
242 }
243 */
244
245 DPRINT("dwVersion, cxDesired, cyDesired are all ignored in this implementation!\n");
246
247 if (! fIcon)
248 {
249 wXHotspot = *(WORD*)pbIconBits;
250 pbIconBits+=sizeof(WORD);
251 wYHotspot = *(WORD*)pbIconBits;
252 pbIconBits+=sizeof(WORD);
253 cbIconBits-=2*sizeof(WORD);
254 }
255 else
256 {
257 wXHotspot = cxDesired / 2;
258 wYHotspot = cyDesired / 2;
259 }
260
261 /* get an safe copy of the icon data */
262 SafeIconImage = RtlAllocateHeap(GetProcessHeap(), 0, cbIconBits);
263 if (SafeIconImage == NULL)
264 {
265 return NULL;
266 }
267 memcpy(SafeIconImage, pbIconBits, cbIconBits);
268
269 /* take into acount the origonal height was for both the AND and XOR images */
270 if(fIcon)
271 SafeIconImage->icHeader.biHeight /= 2;
272
273 if (SafeIconImage->icHeader.biSize == sizeof(BITMAPCOREHEADER))
274 {
275 BITMAPCOREHEADER* Core = (BITMAPCOREHEADER*)SafeIconImage;
276 ColourCount = (Core->bcBitCount <= 8) ? (1 << Core->bcBitCount) : 0;
277 HeaderSize = sizeof(BITMAPCOREHEADER) + ColourCount * sizeof(RGBTRIPLE);
278 }
279 else
280 {
281 ColourCount = (SafeIconImage->icHeader.biBitCount <= 8) ?
282 (1 << SafeIconImage->icHeader.biBitCount) : 0;
283 HeaderSize = sizeof(BITMAPINFOHEADER) + ColourCount * sizeof(RGBQUAD);
284 }
285
286 /* make data point to the start of the XOR image data */
287 Data = (PBYTE)SafeIconImage + HeaderSize;
288
289 /* get a handle to the screen dc, the icon we create is going to be compatable with this */
290 hScreenDc = CreateCompatibleDC(NULL);
291 if (hScreenDc == NULL)
292 {
293 RtlFreeHeap(GetProcessHeap(), 0, SafeIconImage);
294 return(NULL);
295 }
296
297 if(fIcon)
298 hIcon = ICON_CreateIconFromData(hScreenDc, Data, SafeIconImage, cxDesired, cyDesired, wXHotspot, wYHotspot);
299 else
300 hIcon = ICON_CreateCursorFromData(hScreenDc, Data, SafeIconImage, cxDesired, cyDesired, wXHotspot, wYHotspot);
301 RtlFreeHeap(GetProcessHeap(), 0, SafeIconImage);
302 DeleteDC(hScreenDc);
303
304 return hIcon;
305 }
306
307
308 /*
309 * @implemented
310 */
311 HICON
312 STDCALL
313 CreateIconIndirect(PICONINFO IconInfo)
314 {
315 BITMAP ColorBitmap;
316 BITMAP MaskBitmap;
317
318 if(!IconInfo)
319 {
320 SetLastError(ERROR_INVALID_PARAMETER);
321 return (HICON)0;
322 }
323
324 if(!GetObjectW(IconInfo->hbmMask, sizeof(BITMAP), &MaskBitmap))
325 {
326 return (HICON)0;
327 }
328 /* FIXME - does there really *have* to be a color bitmap? monochrome cursors don't have one */
329 if(IconInfo->hbmColor && !GetObjectW(IconInfo->hbmColor, sizeof(BITMAP), &ColorBitmap))
330 {
331 return (HICON)0;
332 }
333
334 /* FIXME - i doubt this is right (monochrome cursors */
335 /*if(ColorBitmap.bmWidth != MaskBitmap.bmWidth ||
336 ColorBitmap.bmHeight != MaskBitmap.bmWidth)
337 {
338 SetLastError(ERROR_INVALID_PARAMETER);
339 return (HICON)0;
340 }*/
341
342 return (HICON)NtUserCreateCursorIconHandle(IconInfo, TRUE);
343 }
344
345
346 /*
347 * @implemented
348 */
349 BOOL
350 STDCALL
351 DestroyIcon(
352 HICON hIcon)
353 {
354 return (BOOL)NtUserDestroyCursorIcon((HANDLE)hIcon, 0);
355 }
356
357
358 /*
359 * @implemented
360 */
361 BOOL
362 STDCALL
363 DrawIcon(
364 HDC hDC,
365 int X,
366 int Y,
367 HICON hIcon)
368 {
369 return DrawIconEx(hDC, X, Y, hIcon, 0, 0, 0, NULL, DI_NORMAL | DI_DEFAULTSIZE);
370 }
371
372 /*
373 * @implemented
374 */
375 BOOL
376 STDCALL
377 DrawIconEx(
378 HDC hdc,
379 int xLeft,
380 int yTop,
381 HICON hIcon,
382 int cxWidth,
383 int cyWidth,
384 UINT istepIfAniCur,
385 HBRUSH hbrFlickerFreeDraw,
386 UINT diFlags)
387 {
388 return (BOOL)NtUserDrawIconEx(hdc, xLeft, yTop, hIcon, cxWidth, cyWidth,
389 istepIfAniCur, hbrFlickerFreeDraw, diFlags,
390 0, 0);
391 }
392
393
394 /*
395 * @implemented
396 */
397 BOOL
398 STDCALL
399 GetIconInfo(
400 HICON hIcon,
401 PICONINFO IconInfo)
402 {
403 /* FIXME - copy bitmaps */
404 return (BOOL)NtUserGetCursorIconInfo((HANDLE)hIcon, IconInfo);
405 }
406
407
408 /*
409 * @implemented
410 */
411 HICON
412 STDCALL
413 LoadIconA(
414 HINSTANCE hInstance,
415 LPCSTR lpIconName)
416 {
417 return(LoadImageA(hInstance, lpIconName, IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
418 }
419
420
421 /*
422 * @implemented
423 */
424 HICON
425 STDCALL
426 LoadIconW(
427 HINSTANCE hInstance,
428 LPCWSTR lpIconName)
429 {
430 return(LoadImageW(hInstance, lpIconName, IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
431 }
432
433
434 /*
435 * @implemented
436 */
437 int
438 STDCALL
439 LookupIconIdFromDirectory(
440 PBYTE presbits,
441 BOOL fIcon)
442 {
443 return LookupIconIdFromDirectoryEx(presbits,
444 fIcon,
445 fIcon ? GetSystemMetrics(SM_CXICON) : GetSystemMetrics(SM_CXCURSOR),
446 fIcon ? GetSystemMetrics(SM_CYICON) : GetSystemMetrics(SM_CYCURSOR),
447 LR_DEFAULTCOLOR);
448 }
449
450
451
452
453
454 /*
455 * The following macro function accounts for the irregularities of
456 * accessing cursor and icon resources in files and resource entries.
457 */
458 typedef BOOL
459 (*fnGetCIEntry)(LPVOID dir, int n, int *width, int *height, int *bits );
460
461 /**********************************************************************
462 * CURSORICON_FindBestIcon
463 *
464 * Find the icon closest to the requested size and number of colors.
465 */
466 static int
467 CURSORICON_FindBestIcon(LPVOID dir,
468 fnGetCIEntry get_entry,
469 int Width,
470 int Height,
471 int ColorBits)
472 {
473 int i, cx, cy, Bits, BestBits = 0, BestEntry = -1;
474 UINT iTotalDiff, iXDiff=0, iYDiff=0, iColorDiff;
475 UINT iTempXDiff, iTempYDiff, iTempColorDiff;
476
477 /* Find Best Fit */
478 iTotalDiff = 0xFFFFFFFF;
479 iColorDiff = 0xFFFFFFFF;
480 for (i = 0; get_entry(dir, i, &cx, &cy, &Bits); i++ )
481 {
482 iTempXDiff = abs(Width - cx);
483 iTempYDiff = abs(Height - cy);
484
485 if(iTotalDiff > (iTempXDiff + iTempYDiff))
486 {
487 iXDiff = iTempXDiff;
488 iYDiff = iTempYDiff;
489 iTotalDiff = iXDiff + iYDiff;
490 }
491 }
492
493 /* Find Best Colors for Best Fit */
494 for (i = 0; get_entry(dir, i, &cx, &cy, &Bits); i++ )
495 {
496 if(abs(Width - cx) == iXDiff && abs(Height - cy) == iYDiff)
497 {
498 iTempColorDiff = abs(ColorBits - Bits);
499 if(iColorDiff > iTempColorDiff)
500 {
501 BestEntry = i;
502 BestBits = Bits;
503 iColorDiff = iTempColorDiff;
504 }
505 }
506 }
507
508 DPRINT1("Best Icon: ResId: %d, bits : %d\n", BestEntry, BestBits);
509
510 return BestEntry;
511 }
512
513
514
515 /**********************************************************************
516 * CURSORICON_FindBestCursor
517 *
518 * Find the cursor closest to the requested size.
519 * FIXME: parameter 'color' ignored and entries with more than 1 bpp
520 * ignored too
521 */
522 static int
523 CURSORICON_FindBestCursor(LPVOID dir,
524 fnGetCIEntry get_entry,
525 int Width,
526 int Height,
527 int ColorBits)
528 {
529 int i, MaxWidth, MaxHeight, cx, cy, Bits, BestEntry = -1;
530
531 /* Double height to account for AND and XOR masks */
532 Height *= 2;
533
534 /* First find the largest one smaller than or equal to the requested size*/
535 MaxWidth = MaxHeight = 0;
536 for (i = 0; get_entry(dir, i, &cx, &cy, &Bits); i++ )
537 {
538 if ((cx <= Width) && (cy <= Height) &&
539 (cx > MaxWidth) && (cy > MaxHeight) &&
540 (Bits == 1))
541 {
542 BestEntry = i;
543 MaxWidth = cx;
544 MaxHeight = cy;
545 }
546 }
547 if (BestEntry != -1)
548 return BestEntry;
549
550 /* Now find the smallest one larger than the requested size */
551 MaxWidth = MaxHeight = 255;
552 for (i = 0; get_entry(dir, i, &cx, &cy, &Bits); i++ )
553 {
554 if (((cx < MaxWidth) && (cy < MaxHeight) && (Bits == 1)) ||
555 (BestEntry == -1))
556 {
557 BestEntry = i;
558 MaxWidth = cx;
559 MaxHeight = cy;
560 }
561 }
562
563 return BestEntry;
564 }
565
566
567 static BOOL
568 CURSORICON_GetResIconEntry(LPVOID dir,
569 int n,
570 int *Width,
571 int *Height,
572 int *Bits)
573 {
574 GRPCURSORICONDIR *ResDir = dir;
575 ICONRESDIR *Icon;
576
577 if (ResDir->idCount <= n)
578 return FALSE;
579
580 Icon = &ResDir->idEntries[n].ResInfo.icon;
581 *Width = Icon->bWidth;
582 *Height = Icon->bHeight;
583 *Bits = ResDir->idEntries[n].wBitCount;
584 return TRUE;
585 }
586
587 static BOOL
588 CURSORICON_GetResCursorEntry(LPVOID dir,
589 int n,
590 int *Width,
591 int *Height,
592 int *Bits)
593 {
594 GRPCURSORICONDIR *ResDir = dir;
595 CURSORRESDIR *Cursor;
596
597 if (ResDir->idCount <= n)
598 return FALSE;
599
600 Cursor = &ResDir->idEntries[n].ResInfo.cursor;
601 *Width = Cursor->wWidth;
602 *Height = Cursor->wHeight;
603 *Bits = ResDir->idEntries[n].wBitCount;
604 return TRUE;
605 }
606
607 static GRPCURSORICONDIRENTRY *
608 CURSORICON_FindBestIconRes(GRPCURSORICONDIR * dir,
609 int Width,
610 int Height,
611 int ColorBits)
612 {
613 int n;
614 n = CURSORICON_FindBestIcon(dir,
615 CURSORICON_GetResIconEntry,
616 Width,
617 Height,
618 ColorBits);
619 if (n < 0)
620 return NULL;
621
622 return &dir->idEntries[n];
623 }
624
625 static GRPCURSORICONDIRENTRY *
626 CURSORICON_FindBestCursorRes(GRPCURSORICONDIR *dir,
627 int Width,
628 int Height,
629 int ColorBits)
630 {
631 int n;
632 n = CURSORICON_FindBestCursor(dir,
633 CURSORICON_GetResCursorEntry,
634 Width,
635 Height,
636 ColorBits);
637 if (n < 0)
638 return NULL;
639
640 return &dir->idEntries[n];
641 }
642
643
644 INT WINAPI
645 LookupIconIdFromDirectoryEx(PBYTE xdir,
646 BOOL bIcon,
647 INT width,
648 INT height,
649 UINT cFlag)
650 {
651 GRPCURSORICONDIR *dir = (GRPCURSORICONDIR*)xdir;
652 UINT retVal = 0;
653 if(dir && !dir->idReserved && (IMAGE_ICON == dir->idType || IMAGE_CURSOR == dir->idType))
654 {
655 GRPCURSORICONDIRENTRY *entry = NULL;
656 int ColorBits;
657
658 if (cFlag & LR_MONOCHROME)
659 {
660 ColorBits = 1;
661 }
662 else
663 {
664 HDC hdc = GetDC(0);
665 ColorBits = GetDeviceCaps(hdc, BITSPIXEL);
666 ReleaseDC(0, hdc);
667 }
668
669 if(bIcon)
670 entry = CURSORICON_FindBestIconRes(dir, width, height, ColorBits);
671 else
672 entry = CURSORICON_FindBestCursorRes(dir, width, height, 1);
673
674 if (entry)
675 retVal = entry->nID;
676 }
677 else
678 DPRINT1("%s() : Invalid resource directory\n", __FUNCTION__);
679
680 return retVal;
681 }