Release 960712
[wine] / win32 / cursoricon32.c
1 /*
2  * Cursor and icon support
3  *
4  * Copyright 1995 Alexandre Julliard
5  * Copyright 1996 Martin von Loewis
6  */
7
8 /*
9  * Theory:
10  *
11  * Cursors and icons are stored in a global heap block, with the
12  * following layout:
13  *
14  * CURSORICONINFO info;
15  * BYTE[]         ANDbits;
16  * BYTE[]         XORbits;
17  *
18  * The bits structures are in the format of a device-dependent bitmap.
19  *
20  * This layout is very sub-optimal, as the bitmap bits are stored in
21  * the X client instead of in the server like other bitmaps; however,
22  * some programs (notably Paint Brush) expect to be able to manipulate
23  * the bits directly :-(
24  */
25
26 #include <string.h>
27 #include <stdlib.h>
28 #include "windows.h"
29 #include "bitmap.h"
30 #include "callback.h"
31 #include "cursoricon.h"
32 #include "sysmetrics.h"
33 #include "win.h"
34 #include "struct32.h"
35 #include "string32.h"
36 #include "resource32.h"
37 #include "stddebug.h"
38 #include "debug.h"
39 #include "xmalloc.h"
40 #include "task.h"
41
42 /* This dictionary could might eventually become a macro for better reuse */
43 struct MAP_DWORD_DWORD{
44         DWORD key;
45         DWORD value;
46 };
47
48 struct MAP_DWORD_DWORD *CURSORICON_map;
49 int CURSORICON_count;
50
51 BOOL CURSORICON_lookup(DWORD key,DWORD *value)
52 {
53         int i;
54         for(i=0;i<CURSORICON_count;i++)
55         {
56                 if(key==CURSORICON_map[i].key)
57                 {
58                         *value=CURSORICON_map[i].value;
59                         return TRUE;
60                 }
61         }
62         return FALSE;
63 }
64
65 void CURSORICON_insert(DWORD key,DWORD value)
66 {
67         if(!CURSORICON_count)
68         {
69                 CURSORICON_count=1;
70                 CURSORICON_map=malloc(sizeof(struct MAP_DWORD_DWORD));
71         }else{
72                 CURSORICON_count++;
73                 CURSORICON_map=realloc(CURSORICON_map,
74                         sizeof(struct MAP_DWORD_DWORD)*CURSORICON_count);
75         }
76         CURSORICON_map[CURSORICON_count-1].key=key;
77         CURSORICON_map[CURSORICON_count-1].value=value;
78 }
79
80 /**********************************************************************
81  *          CURSORICON32_FindBestIcon
82  *
83  * Find the icon closest to the requested size and number of colors.
84  */
85 static ICONDIRENTRY32 *CURSORICON32_FindBestIcon( CURSORICONDIR32 *dir, 
86         int width, int height, int colors )
87 {
88     int i, maxcolors, maxwidth, maxheight;
89     ICONDIRENTRY32 *entry, *bestEntry = NULL;
90
91     if (dir->idCount < 1)
92     {
93         fprintf( stderr, "Icon: empty directory!\n" );
94         return NULL;
95     }
96     if (dir->idCount == 1) return &dir->idEntries[0].icon;  /* No choice... */
97
98     /* First find the exact size with less colors */
99
100     maxcolors = 0;
101     for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
102         if ((entry->bWidth == width) && (entry->bHeight == height) &&
103             (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors))
104         {
105             bestEntry = entry;
106             maxcolors = entry->bColorCount;
107         }
108     if (bestEntry) return bestEntry;
109
110     /* First find the exact size with more colors */
111
112     maxcolors = 255;
113     for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
114         if ((entry->bWidth == width) && (entry->bHeight == height) &&
115             (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors))
116         {
117             bestEntry = entry;
118             maxcolors = entry->bColorCount;
119         }
120     if (bestEntry) return bestEntry;
121
122     /* Now find a smaller one with less colors */
123
124     maxcolors = maxwidth = maxheight = 0;
125     for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
126         if ((entry->bWidth <= width) && (entry->bHeight <= height) &&
127             (entry->bWidth >= maxwidth) && (entry->bHeight >= maxheight) &&
128             (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors))
129         {
130             bestEntry = entry;
131             maxwidth  = entry->bWidth;
132             maxheight = entry->bHeight;
133             maxcolors = entry->bColorCount;
134         }
135     if (bestEntry) return bestEntry;
136
137     /* Now find a smaller one with more colors */
138
139     maxcolors = 255;
140     maxwidth = maxheight = 0;
141     for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
142         if ((entry->bWidth <= width) && (entry->bHeight <= height) &&
143             (entry->bWidth >= maxwidth) && (entry->bHeight >= maxheight) &&
144             (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors))
145         {
146             bestEntry = entry;
147             maxwidth  = entry->bWidth;
148             maxheight = entry->bHeight;
149             maxcolors = entry->bColorCount;
150         }
151     if (bestEntry) return bestEntry;
152
153     /* Now find a larger one with less colors */
154
155     maxcolors = 0;
156     maxwidth = maxheight = 255;
157     for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
158         if ((entry->bWidth <= maxwidth) && (entry->bHeight <= maxheight) &&
159             (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors))
160         {
161             bestEntry = entry;
162             maxwidth  = entry->bWidth;
163             maxheight = entry->bHeight;
164             maxcolors = entry->bColorCount;
165         }
166     if (bestEntry) return bestEntry;
167
168     /* Now find a larger one with more colors */
169
170     maxcolors = maxwidth = maxheight = 255;
171     for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
172         if ((entry->bWidth <= maxwidth) && (entry->bHeight <= maxheight) &&
173             (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors))
174         {
175             bestEntry = entry;
176             maxwidth  = entry->bWidth;
177             maxheight = entry->bHeight;
178             maxcolors = entry->bColorCount;
179         }
180
181     return bestEntry;
182 }
183
184
185 /**********************************************************************
186  *          CURSORICON32_FindBestCursor
187  *
188  * Find the cursor closest to the requested size.
189  */
190 static CURSORDIRENTRY32 *CURSORICON32_FindBestCursor( CURSORICONDIR32 *dir,
191                                                   int width, int height )
192 {
193     int i, maxwidth, maxheight;
194     CURSORDIRENTRY32 *entry, *bestEntry = NULL;
195
196     if (dir->idCount < 1)
197     {
198         fprintf( stderr, "Cursor: empty directory!\n" );
199         return NULL;
200     }
201     if (dir->idCount == 1) return &dir->idEntries[0].cursor; /* No choice... */
202
203     /* First find the largest one smaller than or equal to the requested size*/
204
205     maxwidth = maxheight = 0;
206     for(i = 0,entry = &dir->idEntries[0].cursor; i < dir->idCount; i++,entry++)
207         if ((entry->wWidth <= width) && (entry->wHeight <= height) &&
208             (entry->wWidth > maxwidth) && (entry->wHeight > maxheight))
209         {
210             bestEntry = entry;
211             maxwidth  = entry->wWidth;
212             maxheight = entry->wHeight;
213         }
214     if (bestEntry) return bestEntry;
215
216     /* Now find the smallest one larger than the requested size */
217
218     maxwidth = maxheight = 255;
219     for(i = 0,entry = &dir->idEntries[0].cursor; i < dir->idCount; i++,entry++)
220         if ((entry->wWidth < maxwidth) && (entry->wHeight < maxheight))
221         {
222             bestEntry = entry;
223             maxwidth  = entry->wWidth;
224             maxheight = entry->wHeight;
225         }
226
227     return bestEntry;
228 }
229
230
231 /**********************************************************************
232  *          CURSORICON32_LoadDirEntry
233  *
234  * Load the icon/cursor directory for a given resource name and find the
235  * best matching entry.
236  */
237 static BOOL CURSORICON32_LoadDirEntry(HANDLE hInstance, LPCWSTR name,
238                                     int width, int height, int colors,
239                                     BOOL fCursor, CURSORICONDIRENTRY32 *dirEntry)
240 {
241     HANDLE32 hRsrc;
242     HANDLE32 hMem;
243     CURSORICONDIR32 *dir;
244     CURSORICONDIRENTRY32 *entry = NULL;
245
246     if (!(hRsrc = FindResource32W( hInstance, name,
247                 (LPCWSTR)(fCursor ? RT_GROUP_CURSOR : RT_GROUP_ICON) )))
248         return FALSE;
249     if (!(hMem = LoadResource32( hInstance, hRsrc ))) return FALSE;
250     if ((dir = (CURSORICONDIR32 *)LockResource32( hMem )))
251     {
252         if (fCursor)
253             entry = (CURSORICONDIRENTRY32 *)CURSORICON32_FindBestCursor( dir,
254                                                                width, height );
255         else
256             entry = (CURSORICONDIRENTRY32 *)CURSORICON32_FindBestIcon( dir,
257                                                        width, height, colors );
258         if (entry) *dirEntry = *entry;
259     }
260     FreeResource32( hMem );
261     return (entry != NULL);
262 }
263
264
265 /**********************************************************************
266  *          CURSORICON32_LoadHandler 
267  *
268  * Create a cursor or icon from a resource.
269  */
270 static HANDLE CURSORICON32_LoadHandler( HANDLE32 handle, HINSTANCE hInstance,
271                                       BOOL fCursor )
272 {
273     HANDLE hAndBits, hXorBits, hRes;
274     HDC hdc;
275     int size, sizeAnd, sizeXor;
276     POINT16 hotspot = { 0 ,0 };
277     BITMAPOBJ *bmpXor, *bmpAnd;
278     BITMAPINFO *bmi, *pInfo;
279     CURSORICONINFO *info;
280     char *bits;
281
282         hRes=0;
283     if (fCursor)  /* If cursor, get the hotspot */
284     {
285         POINT16 *pt = (POINT16 *)LockResource32( handle );
286         hotspot = *pt;
287         bmi = (BITMAPINFO *)(pt + 1);
288     }
289     else bmi = (BITMAPINFO *)LockResource32( handle );
290
291     /* Create a copy of the bitmap header */
292
293     size = DIB_BitmapInfoSize( bmi, DIB_RGB_COLORS );
294     /* Make sure we have room for the monochrome bitmap later on */
295     size = MAX( size, sizeof(BITMAPINFOHEADER) + 2*sizeof(RGBQUAD) );
296     pInfo = (BITMAPINFO *)xmalloc( size );
297     memcpy( pInfo, bmi, size );
298
299     if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
300     {
301         if (pInfo->bmiHeader.biCompression != BI_RGB)
302         {
303             fprintf(stderr,"Unknown size for compressed icon bitmap.\n");
304             free( pInfo );
305             return 0;
306         }
307         pInfo->bmiHeader.biHeight /= 2;
308     }
309     else if (pInfo->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
310     {
311         BITMAPCOREHEADER *core = (BITMAPCOREHEADER *)pInfo;
312         core->bcHeight /= 2;
313     }
314     else
315     {
316         fprintf( stderr, "CURSORICON32_Load: Unknown bitmap length %ld!\n",
317                  pInfo->bmiHeader.biSize );
318         free( pInfo );
319         return 0;
320     }
321
322     /* Create the XOR bitmap */
323
324     if (!(hdc = GetDC( 0 )))
325     {
326         free( pInfo );
327         return 0;
328     }
329
330     hXorBits = CreateDIBitmap( hdc, &pInfo->bmiHeader, CBM_INIT,
331                                (char*)bmi + size, pInfo, DIB_RGB_COLORS );
332
333     /* Fix the bitmap header to load the monochrome mask */
334
335     if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
336     {
337         BITMAPINFOHEADER *bih = &pInfo->bmiHeader;
338         RGBQUAD *rgb = pInfo->bmiColors;
339         bits = (char *)bmi + size +
340             DIB_GetImageWidthBytes(bih->biWidth,bih->biBitCount)*bih->biHeight;
341         bih->biBitCount = 1;
342         bih->biClrUsed = bih->biClrImportant = 2;
343         rgb[0].rgbBlue = rgb[0].rgbGreen = rgb[0].rgbRed = 0x00;
344         rgb[1].rgbBlue = rgb[1].rgbGreen = rgb[1].rgbRed = 0xff;
345         rgb[0].rgbReserved = rgb[1].rgbReserved = 0;
346     }
347     else
348     {
349         BITMAPCOREHEADER *bch = (BITMAPCOREHEADER *)pInfo;
350         RGBTRIPLE *rgb = (RGBTRIPLE *)(bch + 1);
351         bits = (char *)bmi + size +
352             DIB_GetImageWidthBytes(bch->bcWidth,bch->bcBitCount)*bch->bcHeight;
353         bch->bcBitCount = 1;
354         rgb[0].rgbtBlue = rgb[0].rgbtGreen = rgb[0].rgbtRed = 0x00;
355         rgb[1].rgbtBlue = rgb[1].rgbtGreen = rgb[1].rgbtRed = 0xff;
356     }
357
358     /* Create the AND bitmap */
359
360     hAndBits = CreateDIBitmap( hdc, &pInfo->bmiHeader, CBM_INIT,
361                                bits, pInfo, DIB_RGB_COLORS );
362     ReleaseDC( 0, hdc );
363
364     /* Now create the CURSORICONINFO structure */
365
366     bmpXor = (BITMAPOBJ *) GDI_GetObjPtr( hXorBits, BITMAP_MAGIC );
367     bmpAnd = (BITMAPOBJ *) GDI_GetObjPtr( hAndBits, BITMAP_MAGIC );
368     sizeXor = bmpXor->bitmap.bmHeight * bmpXor->bitmap.bmWidthBytes;
369     sizeAnd = bmpAnd->bitmap.bmHeight * bmpAnd->bitmap.bmWidthBytes;
370
371     if (!(hRes = GlobalAlloc16( GMEM_MOVEABLE,
372                                 sizeof(CURSORICONINFO) + sizeXor + sizeAnd)))
373     {
374         DeleteObject( hXorBits );
375         DeleteObject( hAndBits );
376         return 0;
377     }
378
379     /* Make it owned by the module */
380     if (hInstance) FarSetOwner( hRes, (WORD)(DWORD)GetExePtr(hInstance) );
381
382     info = (CURSORICONINFO *)GlobalLock16( hRes );
383     info->ptHotSpot.x   = hotspot.x;
384     info->ptHotSpot.y   = hotspot.y;
385     info->nWidth        = bmpXor->bitmap.bmWidth;
386     info->nHeight       = bmpXor->bitmap.bmHeight;
387     info->nWidthBytes   = bmpXor->bitmap.bmWidthBytes;
388     info->bPlanes       = bmpXor->bitmap.bmPlanes;
389     info->bBitsPerPixel = bmpXor->bitmap.bmBitsPixel;
390
391     /* Transfer the bitmap bits to the CURSORICONINFO structure */
392
393     GetBitmapBits( hAndBits, sizeAnd, (char *)(info + 1) );
394     GetBitmapBits( hXorBits, sizeXor, (char *)(info + 1) + sizeAnd );
395     DeleteObject( hXorBits );
396     DeleteObject( hAndBits );
397     GlobalUnlock16( hRes );
398     return hRes;
399 }
400
401 /**********************************************************************
402  *          CURSORICON32_Load
403  *
404  * Load a cursor or icon.
405  */
406 static HANDLE CURSORICON32_Load( HANDLE hInstance, LPCWSTR name, int width,
407                                int height, int colors, BOOL fCursor )
408 {
409     HANDLE32 handle;
410         HANDLE hRet;
411     HANDLE32  hRsrc;
412     CURSORICONDIRENTRY32 dirEntry;
413
414         if(!hInstance)  /* OEM cursor/icon */
415         {
416                 WORD resid;
417                 if(HIWORD(name))
418                 {
419                         LPSTR ansi;
420                         ansi=STRING32_DupUniToAnsi(name);
421                         if(ansi[0]=='#')        /*Check for '#xxx' name */
422                         {
423                                 resid=atoi(ansi+1);
424                                 free(ansi);
425                         }else{
426                                 free(ansi);
427                                 return 0;
428                         }
429                 }
430                 else
431                         resid=(WORD)(int)name;
432                 return OBM_LoadCursorIcon(resid, fCursor);
433         }
434
435     /* Find the best entry in the directory */
436
437     if (!CURSORICON32_LoadDirEntry( hInstance, name, width, height,
438                                   colors, fCursor, &dirEntry )) return 0;
439
440     /* Load the resource */
441
442     if (!(hRsrc = FindResource32W( hInstance,
443                       (LPWSTR) (DWORD) dirEntry.icon.wResId,
444                       (LPWSTR) (fCursor ? RT_CURSOR : RT_ICON )))) return 0;
445     if (!(handle = LoadResource32( hInstance, hRsrc ))) return 0;
446
447         /* Use the resource handle as key to detect multiple loading */
448         if(CURSORICON_lookup(handle,&hRet))
449                 return hRet;
450
451     hRet = CURSORICON32_LoadHandler( handle, hInstance, fCursor );
452     /* Obsolete - FreeResource32(handle);*/
453         CURSORICON_insert(handle,hRet);
454     return hRet;
455 }
456
457
458 /***********************************************************************
459  *           LoadCursor
460  */
461 HCURSOR WIN32_LoadCursorW( HANDLE hInstance, LPCWSTR name )
462 {
463     return CURSORICON32_Load( hInstance, name,
464                             SYSMETRICS_CXCURSOR, SYSMETRICS_CYCURSOR, 1, TRUE);
465 }
466
467 HCURSOR WIN32_LoadCursorA(HANDLE hInstance, LPCSTR name)
468 {
469         HCURSOR res=0;
470         if(!HIWORD(name))
471                 return WIN32_LoadCursorW(hInstance, name);
472         else {
473                 LPWSTR uni = STRING32_DupAnsiToUni(name);
474                 res = WIN32_LoadCursorW(hInstance, uni);
475                 free(uni);
476         }
477         return res;
478 }
479
480
481 /***********************************************************************
482  *           LoadIcon
483  */
484 HICON WIN32_LoadIconW( HANDLE hInstance, LPCWSTR name )
485 {
486     return CURSORICON32_Load( hInstance, name,
487                             SYSMETRICS_CXICON, SYSMETRICS_CYICON,
488                             MIN( 16, 1 << screenDepth ), FALSE );
489 }
490
491 HICON WIN32_LoadIconA( HANDLE hInstance, LPCSTR name)
492 {
493         HICON res=0;
494         if(!HIWORD(name))
495                 return WIN32_LoadIconW(hInstance, name);
496         else {
497                 LPWSTR uni = STRING32_DupAnsiToUni(name);
498                 res = WIN32_LoadIconW(hInstance, uni);
499                 free(uni);
500         }
501         return res;
502 }
503