update for HEAD-2003091401
[reactos.git] / lib / 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 <windows.h>
32 #include <user32.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <debug.h>
36
37 /* FUNCTIONS *****************************************************************/
38
39 HICON
40 ICON_CreateIconFromData(HDC hDC, PVOID ImageData, ICONIMAGE* IconImage, int cxDesired, int cyDesired, int xHotspot, int yHotspot)
41 {
42   HANDLE hXORBitmap;
43   HANDLE hANDBitmap;
44   BITMAPINFO* bwBIH;
45   ICONINFO IconInfo;
46   HICON hIcon;
47
48   //load the XOR bitmap
49   hXORBitmap = CreateDIBitmap(hDC, &IconImage->icHeader, CBM_INIT,
50                                ImageData, (BITMAPINFO*)IconImage, DIB_RGB_COLORS);
51
52   //make ImageData point to the start of the AND image data
53   ImageData = ((PBYTE)ImageData) + (((IconImage->icHeader.biWidth * 
54                                       IconImage->icHeader.biBitCount + 31) & ~31) >> 3) * 
55                                       (IconImage->icHeader.biHeight );
56
57   //create a BITMAPINFO header for the monocrome part of the icon
58   bwBIH = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof (BITMAPINFOHEADER)+2*sizeof(RGBQUAD));
59
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 = (((IconImage->icHeader.biWidth * 1 + 31) & ~31) >> 3) * 
66                                     (IconImage->icHeader.biHeight );
67   bwBIH->bmiHeader.biCompression = BI_RGB;
68   bwBIH->bmiHeader.biClrImportant = 0;
69   bwBIH->bmiHeader.biClrUsed = 0;
70   bwBIH->bmiHeader.biXPelsPerMeter = 0;
71   bwBIH->bmiHeader.biYPelsPerMeter = 0;
72
73   bwBIH->bmiColors[0].rgbBlue = 0;
74   bwBIH->bmiColors[0].rgbGreen = 0;
75   bwBIH->bmiColors[0].rgbRed = 0;
76   bwBIH->bmiColors[0].rgbReserved = 0;
77
78   bwBIH->bmiColors[1].rgbBlue = 0xff;
79   bwBIH->bmiColors[1].rgbGreen = 0xff;
80   bwBIH->bmiColors[1].rgbRed = 0xff;
81   bwBIH->bmiColors[1].rgbReserved = 0;
82
83   //load the AND bitmap
84   hANDBitmap = CreateDIBitmap(hDC, &bwBIH->bmiHeader, CBM_INIT,
85                                ImageData, bwBIH, DIB_RGB_COLORS);
86
87   RtlFreeHeap(RtlGetProcessHeap(), 0, bwBIH);
88
89   IconInfo.fIcon = TRUE;
90   IconInfo.xHotspot = xHotspot;
91   IconInfo.yHotspot = yHotspot;
92   IconInfo.hbmColor = hXORBitmap;
93   IconInfo.hbmMask = hANDBitmap;
94
95   //Create the icon based on everything we have so far
96   hIcon = CreateIconIndirect(&IconInfo);
97
98   //clean up
99   DeleteObject(hXORBitmap);
100   DeleteObject(hANDBitmap);
101
102   return hIcon;
103 }
104
105
106 /*
107  * @implemented
108  */
109 HICON
110 STDCALL
111 CopyIcon(
112   HICON hIcon)
113 {
114   ICONINFO IconInfo;
115   NtUserGetIconInfo(hIcon, &IconInfo.fIcon,
116                            &IconInfo.xHotspot,
117                            &IconInfo.yHotspot,
118                            &IconInfo.hbmMask,
119                            &IconInfo.hbmColor);
120   return CreateIconIndirect(&IconInfo);
121 }
122
123
124 /*
125  * @implemented
126  */
127 HICON
128 STDCALL
129 CreateIcon(
130   HINSTANCE hInstance,
131   int nWidth,
132   int nHeight,
133   BYTE cPlanes,
134   BYTE cBitsPixel,
135   CONST BYTE *lpbANDbits,
136   CONST BYTE *lpbXORbits)
137 {
138   DPRINT("hInstance not used in this implementation\n");
139   return NtGdiCreateIcon(TRUE,
140                         nWidth,
141                         nHeight,
142                         cPlanes,
143                         cBitsPixel,
144                         nWidth/2,
145                         nHeight/2,
146                         lpbANDbits,
147                         lpbXORbits);
148 }
149
150
151 /*
152  * @implemented
153  */
154 HICON
155 STDCALL
156 CreateIconFromResource(
157   PBYTE presbits,
158   DWORD dwResSize,
159   WINBOOL fIcon,
160   DWORD dwVer)
161 {
162   return CreateIconFromResourceEx( presbits, dwResSize, fIcon, dwVer, 0,0,0);
163 }
164
165
166 /*
167  * @implemented
168  */
169 HICON
170 STDCALL
171 CreateIconFromResourceEx(
172   PBYTE pbIconBits,
173   DWORD cbIconBits,
174   WINBOOL fIcon,
175   DWORD dwVersion,
176   int cxDesired,
177   int cyDesired,
178   UINT uFlags)
179 {
180   ICONIMAGE* SafeIconImage;
181   HICON hIcon;
182   ULONG HeaderSize;
183   ULONG ColourCount;
184   PVOID Data;
185   HDC hScreenDc;
186   WORD wXHotspot;
187   WORD wYHotspot;
188
189   DPRINT("dwVersion, cxDesired, cyDesired are all ignored in this implementation!\n");
190
191   if (!fIcon)
192   {
193           wXHotspot = (WORD)*pbIconBits;
194           pbIconBits+=2;
195           wYHotspot = (WORD)*pbIconBits;
196           pbIconBits+=2;
197           cbIconBits-=4;
198   }
199   else
200   {
201           wXHotspot = cxDesired / 2;
202           wYHotspot = cyDesired / 2;
203   }
204
205   //get an safe copy of the icon data
206   SafeIconImage = RtlAllocateHeap(RtlGetProcessHeap(), 0, cbIconBits);
207   memcpy(SafeIconImage, pbIconBits, cbIconBits);
208
209   //take into acount the origonal hight was for both the AND and XOR images
210   SafeIconImage->icHeader.biHeight /= 2;
211
212   if (SafeIconImage->icHeader.biSize == sizeof(BITMAPCOREHEADER))
213   {
214       BITMAPCOREHEADER* Core = (BITMAPCOREHEADER*)SafeIconImage;
215       ColourCount = (Core->bcBitCount <= 8) ? (1 << Core->bcBitCount) : 0;
216       HeaderSize = sizeof(BITMAPCOREHEADER) + ColourCount * sizeof(RGBTRIPLE);
217   }
218   else
219   {
220       ColourCount = (SafeIconImage->icHeader.biBitCount <= 8) ? 
221                        (1 << SafeIconImage->icHeader.biBitCount) : 0;
222       HeaderSize = sizeof(BITMAPINFOHEADER) + ColourCount * sizeof(RGBQUAD);
223   }
224
225   //make data point to the start of the XOR image data
226   Data = (PBYTE)SafeIconImage + HeaderSize;
227
228   //get a handle to the screen dc, the icon we create is going to be compatable with this
229   hScreenDc = CreateDCW(L"DISPLAY", NULL, NULL, NULL);
230   if (hScreenDc == NULL)
231   {
232      RtlFreeHeap(RtlGetProcessHeap(), 0, SafeIconImage);
233      return(NULL);
234   }
235
236   hIcon = ICON_CreateIconFromData(hScreenDc, Data, SafeIconImage, cxDesired, cyDesired, wXHotspot, wYHotspot);
237   RtlFreeHeap(RtlGetProcessHeap(), 0, SafeIconImage);
238   return hIcon;
239 }
240
241
242 /*
243  * @implemented
244  */
245 HICON
246 STDCALL
247 CreateIconIndirect(
248   PICONINFO piconinfo)
249 {
250   BITMAP bmMask;
251   BITMAP bmColor;
252
253   NtGdiGetObject( piconinfo->hbmMask, sizeof(BITMAP), &bmMask );
254   NtGdiGetObject( piconinfo->hbmColor, sizeof(BITMAP), &bmColor );
255
256   return NtGdiCreateIcon(piconinfo->fIcon,
257                         bmColor.bmWidth,
258                         bmColor.bmHeight,
259                         bmColor.bmPlanes,
260                         bmColor.bmBitsPixel,
261                         piconinfo->xHotspot,
262                         piconinfo->yHotspot,
263                         bmMask.bmBits,
264                         bmColor.bmBits);
265 }
266
267
268 /*
269  * @implemented
270  */
271 WINBOOL
272 STDCALL
273 DestroyIcon(
274   HICON hIcon)
275 {
276   return NtGdiDeleteObject(hIcon);
277 }
278
279
280 /*
281  * @implemented
282  */
283 WINBOOL
284 STDCALL
285 DrawIcon(
286   HDC hDC,
287   int X,
288   int Y,
289   HICON hIcon)
290 {
291   return DrawIconEx (hDC, X, Y, hIcon, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT | DI_DEFAULTSIZE);
292 }
293
294 /* Ported from WINE20030408 */
295 /*
296  * @implemented
297  */
298 WINBOOL
299 STDCALL
300 DrawIconEx(
301   HDC hdc,
302   int xLeft,
303   int yTop,
304   HICON hIcon,
305   int cxWidth,
306   int cyWidth,
307   UINT istepIfAniCur,
308   HBRUSH hbrFlickerFreeDraw,
309   UINT diFlags)
310 {
311     ICONINFO IconInfo;
312     BITMAP XORBitmap;
313     HDC hDC_off = 0, hMemDC;
314     BOOL result = FALSE, DoOffscreen;
315     HBITMAP hB_off = 0, hOld = 0;
316
317     if (!NtUserGetIconInfo(hIcon, &IconInfo.fIcon,
318                                   &IconInfo.xHotspot,
319                                   &IconInfo.yHotspot,
320                                   &IconInfo.hbmMask,
321                                   &IconInfo.hbmColor))
322       return FALSE;
323
324     NtGdiGetObject(IconInfo.hbmColor, sizeof(BITMAP), &XORBitmap);
325
326     DPRINT("(hdc=%p,pos=%d.%d,hicon=%p,extend=%d.%d,istep=%d,br=%p,flags=0x%08x)\n",
327                  hdc,xLeft,yTop,hIcon,cxWidth,cyWidth,istepIfAniCur,hbrFlickerFreeDraw,diFlags );
328
329     hMemDC = CreateCompatibleDC (hdc);
330     if (diFlags & DI_COMPAT)
331         DPRINT("Ignoring flag DI_COMPAT\n");
332
333     if (!diFlags)
334     {
335           diFlags = DI_NORMAL;
336     }
337
338     // Calculate the size of the destination image.
339     if (cxWidth == 0)
340     {
341       if (diFlags & DI_DEFAULTSIZE)
342              cxWidth = GetSystemMetrics (SM_CXICON);
343       else
344              cxWidth = XORBitmap.bmWidth;
345     }
346     if (cyWidth == 0)
347     {
348       if (diFlags & DI_DEFAULTSIZE)
349         cyWidth = GetSystemMetrics (SM_CYICON);
350       else
351             cyWidth = XORBitmap.bmHeight;
352     }
353
354     DoOffscreen = (GetObjectType( hbrFlickerFreeDraw ) == OBJ_BRUSH);
355
356     if (DoOffscreen)
357     {
358       RECT r;
359
360       r.left = 0;
361       r.top = 0;
362       r.right = cxWidth;
363       r.bottom = cxWidth;
364
365       DbgPrint("in DrawIconEx calling: CreateCompatibleDC\n");
366       hDC_off = CreateCompatibleDC(hdc);
367
368       DbgPrint("in DrawIconEx calling: CreateCompatibleBitmap\n");
369       hB_off = CreateCompatibleBitmap(hdc, cxWidth, cyWidth);
370       if (hDC_off && hB_off)
371       {
372         DbgPrint("in DrawIconEx calling: SelectObject\n");
373         hOld = SelectObject(hDC_off, hB_off);
374
375         DbgPrint("in DrawIconEx calling: FillRect\n");
376         FillRect(hDC_off, &r, hbrFlickerFreeDraw);
377       }
378     }
379
380     if (hMemDC && (!DoOffscreen || (hDC_off && hB_off)))
381     {
382       COLORREF  oldFg, oldBg;
383       INT     nStretchMode;
384
385       nStretchMode = SetStretchBltMode (hdc, STRETCH_DELETESCANS);
386
387       oldFg = SetTextColor( hdc, RGB(0,0,0) );
388
389       oldBg = SetBkColor( hdc, RGB(255,255,255) );
390
391       if (IconInfo.hbmColor && IconInfo.hbmMask)
392       {
393         HBITMAP hBitTemp = SelectObject( hMemDC, IconInfo.hbmMask );
394         if (diFlags & DI_MASK)
395         {
396           if (DoOffscreen)
397             StretchBlt (hDC_off, 0, 0, cxWidth, cyWidth,
398                hMemDC, 0, 0, XORBitmap.bmWidth, XORBitmap.bmHeight, SRCAND);
399           else
400             StretchBlt (hdc, xLeft, yTop, cxWidth, cyWidth,
401                  hMemDC, 0, 0, XORBitmap.bmWidth, XORBitmap.bmHeight, SRCAND);
402         }
403         SelectObject( hMemDC, IconInfo.hbmColor );
404         if (diFlags & DI_IMAGE)
405         {
406           if (DoOffscreen)
407             StretchBlt (hDC_off, 0, 0, cxWidth, cyWidth,
408                hMemDC, 0, 0, XORBitmap.bmWidth, XORBitmap.bmHeight, SRCPAINT);
409           else
410             StretchBlt (hdc, xLeft, yTop, cxWidth, cyWidth,
411               hMemDC, 0, 0, XORBitmap.bmWidth, XORBitmap.bmHeight, SRCPAINT);
412         }
413         SelectObject( hMemDC, hBitTemp );
414         result = TRUE;
415       }
416
417       SetTextColor( hdc, oldFg );
418       SetBkColor( hdc, oldBg );
419       if (IconInfo.hbmColor)
420         DeleteObject( IconInfo.hbmColor );
421
422       if (IconInfo.hbmMask) 
423         DeleteObject( IconInfo.hbmMask );
424
425       SetStretchBltMode (hdc, nStretchMode);
426
427       if (DoOffscreen)
428       {
429         BitBlt(hdc, xLeft, yTop, cxWidth, cyWidth, hDC_off, 0, 0, SRCCOPY);
430         SelectObject(hDC_off, hOld);
431       }
432     }
433
434     if (hMemDC)
435       DeleteDC( hMemDC );
436     if (hDC_off)
437       DeleteDC(hDC_off);
438     if (hB_off)
439       DeleteObject(hB_off);
440     return result;
441 }
442
443
444 /*
445  * @implemented
446  */
447 WINBOOL
448 STDCALL
449 GetIconInfo(
450   HICON hIcon,
451   PICONINFO piconinfo)
452 {
453   ICONINFO IconInfo;
454   WINBOOL res;
455   
456   if(!piconinfo)
457   {
458     SetLastError(ERROR_NOACCESS);
459     return FALSE;
460   }
461   
462   res = NtUserGetIconInfo(hIcon,
463                           &piconinfo->fIcon,
464                           &piconinfo->xHotspot,
465                           &piconinfo->yHotspot,
466                           &piconinfo->hbmMask,
467                           &piconinfo->hbmColor);
468   if(res)
469     RtlCopyMemory(piconinfo, &IconInfo, sizeof(ICONINFO));
470   return res;
471 }
472
473
474 /*
475  * @implemented
476  */
477 HICON
478 STDCALL
479 LoadIconA(
480   HINSTANCE hInstance,
481   LPCSTR lpIconName)
482 {
483   return(LoadImageA(hInstance, lpIconName, IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
484 }
485
486
487 /*
488  * @implemented
489  */
490 HICON
491 STDCALL
492 LoadIconW(
493   HINSTANCE hInstance,
494   LPCWSTR lpIconName)
495 {
496   return(LoadImageW(hInstance, lpIconName, IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
497 }
498
499
500 /*
501  * @implemented
502  */
503 int
504 STDCALL
505 LookupIconIdFromDirectory(
506   PBYTE presbits,
507   WINBOOL fIcon)
508 {
509     return LookupIconIdFromDirectoryEx( presbits, fIcon,
510            fIcon ? GetSystemMetrics(SM_CXICON) : GetSystemMetrics(SM_CXCURSOR),
511            fIcon ? GetSystemMetrics(SM_CYICON) : GetSystemMetrics(SM_CYCURSOR), LR_DEFAULTCOLOR );
512 }
513
514 /* Ported from WINE20030408 */
515 CURSORICONDIRENTRY*
516 CURSORICON_FindBestCursor( CURSORICONDIR *dir, int width, int height, int colors)
517 {
518     int i;
519     CURSORICONDIRENTRY *entry, *bestEntry = NULL;
520     UINT iTotalDiff, iXDiff=0, iYDiff=0, iColorDiff;
521     UINT iTempXDiff, iTempYDiff, iTempColorDiff;
522
523     if (dir->idCount < 1)
524     {
525         DPRINT("Empty directory!\n");
526         return NULL;
527     }
528     if (dir->idCount == 1) 
529       return &dir->idEntries[0];  /* No choice... */
530
531     /* Find Best Fit */
532     iTotalDiff = 0xFFFFFFFF;
533     iColorDiff = 0xFFFFFFFF;
534     for (i = 0, entry = &dir->idEntries[0]; i < dir->idCount; i++,entry++)
535     {
536                 iTempXDiff = abs(width - entry->bWidth);
537                 iTempYDiff = abs(height - entry->bHeight);
538
539         if(iTotalDiff > (iTempXDiff + iTempYDiff))
540         {
541             iXDiff = iTempXDiff;
542             iYDiff = iTempYDiff;
543             iTotalDiff = iXDiff + iYDiff;
544         }
545     }
546
547     /* Find Best Colors for Best Fit */
548     for (i = 0, entry = &dir->idEntries[0]; i < dir->idCount; i++,entry++)
549     {
550         if(abs(width - entry->bWidth) == (int) iXDiff &&
551             abs(height - entry->bHeight) == (int) iYDiff)
552         {
553             iTempColorDiff = abs(colors - entry->bColorCount);
554
555             if(iColorDiff > iTempColorDiff)
556                 {
557                 bestEntry = entry;
558                 iColorDiff = iTempColorDiff;
559                 }
560         }
561     }
562
563     return bestEntry;
564 }
565
566 /* Ported from WINE20030408 */
567 CURSORICONDIRENTRY*
568 CURSORICON_FindBestIcon( CURSORICONDIR *dir, int width, int height, int colors)
569 {
570     int i;
571     CURSORICONDIRENTRY *entry, *bestEntry = NULL;
572     UINT iTotalDiff, iXDiff=0, iYDiff=0, iColorDiff;
573     UINT iTempXDiff, iTempYDiff, iTempColorDiff;
574
575     if (dir->idCount < 1)
576     {
577         DPRINT("Empty directory!\n");
578         return NULL;
579     }
580     if (dir->idCount == 1)
581       return &dir->idEntries[0];  /* No choice... */
582
583     /* Find Best Fit */
584     iTotalDiff = 0xFFFFFFFF;
585     iColorDiff = 0xFFFFFFFF;
586     for (i = 0, entry = &dir->idEntries[0]; i < dir->idCount; i++,entry++)
587       {
588         iTempXDiff = abs(width - entry->bWidth);
589
590         iTempYDiff = abs(height - entry->bHeight);
591
592         if(iTotalDiff > (iTempXDiff + iTempYDiff))
593         {
594             iXDiff = iTempXDiff;
595             iYDiff = iTempYDiff;
596             iTotalDiff = iXDiff + iYDiff;
597         }
598       }
599
600     /* Find Best Colors for Best Fit */
601     for (i = 0, entry = &dir->idEntries[0]; i < dir->idCount; i++,entry++)
602       {
603         if(abs(width - entry->bWidth) == (int) iXDiff &&
604            abs(height - entry->bHeight) == (int) iYDiff)
605         {
606             iTempColorDiff = abs(colors - entry->bColorCount);
607             if(iColorDiff > iTempColorDiff)
608             {
609                 bestEntry = entry;
610                 iColorDiff = iTempColorDiff;
611             }
612         }
613     }
614
615     return bestEntry;
616 }
617
618 /* Ported from WINE20030408 */
619 /*
620  * @implemented
621  */
622 int
623 STDCALL
624 LookupIconIdFromDirectoryEx(
625   PBYTE presbits,
626   WINBOOL fIcon,
627   int cxDesired,
628   int cyDesired,
629   UINT Flags)
630 {
631     GRPCURSORICONDIR *dir = (GRPCURSORICONDIR*)presbits;
632     UINT retVal = 0;
633
634     if( dir && !dir->idReserved && (dir->idType & 3) )
635     {
636         GRPCURSORICONDIRENTRY* entry;
637         HDC hdc;
638         UINT palEnts;
639         int colors;
640         hdc = GetDC(0);
641 #if 0
642         palEnts = GetSystemPaletteEntries(hdc, 0, 0, NULL);
643         if (palEnts == 0)
644         palEnts = 256;
645 #endif
646         palEnts = 16;  //use this until GetSystemPaletteEntries works
647         colors = (Flags & LR_MONOCHROME) ? 2 : palEnts;
648
649         ReleaseDC(0, hdc);
650
651         entry = (GRPCURSORICONDIRENTRY*)CURSORICON_FindBestIcon( (CURSORICONDIR*)dir,
652                                                            cxDesired,
653                                                            cyDesired,
654                                                            colors );
655
656         if( entry )
657             retVal = entry->nID;
658     }
659     else
660     {
661        DbgPrint("invalid resource directory\n");
662     }
663     return retVal;
664 }