Release 960606
[wine.git] / win32 / cursoricon32.c
blob709f8e7d2b65ea98e98165849be43e8e0f398744
1 /*
2 * Cursor and icon support
4 * Copyright 1995 Alexandre Julliard
5 * Copyright 1996 Martin von Loewis
6 */
8 /*
9 * Theory:
11 * Cursors and icons are stored in a global heap block, with the
12 * following layout:
14 * CURSORICONINFO info;
15 * BYTE[] ANDbits;
16 * BYTE[] XORbits;
18 * The bits structures are in the format of a device-dependent bitmap.
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 :-(
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"
42 /* This dictionary could might eventually become a macro for better reuse */
43 struct MAP_DWORD_DWORD{
44 DWORD key;
45 DWORD value;
48 struct MAP_DWORD_DWORD *CURSORICON_map;
49 int CURSORICON_count;
51 BOOL CURSORICON_lookup(DWORD key,DWORD *value)
53 int i;
54 for(i=0;i<CURSORICON_count;i++)
56 if(key==CURSORICON_map[i].key)
58 *value=CURSORICON_map[i].value;
59 return TRUE;
62 return FALSE;
65 void CURSORICON_insert(DWORD key,DWORD value)
67 if(!CURSORICON_count)
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);
76 CURSORICON_map[CURSORICON_count-1].key=key;
77 CURSORICON_map[CURSORICON_count-1].value=value;
80 /**********************************************************************
81 * CURSORICON32_FindBestIcon
83 * Find the icon closest to the requested size and number of colors.
85 static ICONDIRENTRY32 *CURSORICON32_FindBestIcon( CURSORICONDIR32 *dir,
86 int width, int height, int colors )
88 int i, maxcolors, maxwidth, maxheight;
89 ICONDIRENTRY32 *entry, *bestEntry = NULL;
91 if (dir->idCount < 1)
93 fprintf( stderr, "Icon: empty directory!\n" );
94 return NULL;
96 if (dir->idCount == 1) return &dir->idEntries[0].icon; /* No choice... */
98 /* First find the exact size with less colors */
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))
105 bestEntry = entry;
106 maxcolors = entry->bColorCount;
108 if (bestEntry) return bestEntry;
110 /* First find the exact size with more colors */
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))
117 bestEntry = entry;
118 maxcolors = entry->bColorCount;
120 if (bestEntry) return bestEntry;
122 /* Now find a smaller one with less colors */
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))
130 bestEntry = entry;
131 maxwidth = entry->bWidth;
132 maxheight = entry->bHeight;
133 maxcolors = entry->bColorCount;
135 if (bestEntry) return bestEntry;
137 /* Now find a smaller one with more colors */
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))
146 bestEntry = entry;
147 maxwidth = entry->bWidth;
148 maxheight = entry->bHeight;
149 maxcolors = entry->bColorCount;
151 if (bestEntry) return bestEntry;
153 /* Now find a larger one with less colors */
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))
161 bestEntry = entry;
162 maxwidth = entry->bWidth;
163 maxheight = entry->bHeight;
164 maxcolors = entry->bColorCount;
166 if (bestEntry) return bestEntry;
168 /* Now find a larger one with more colors */
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))
175 bestEntry = entry;
176 maxwidth = entry->bWidth;
177 maxheight = entry->bHeight;
178 maxcolors = entry->bColorCount;
181 return bestEntry;
185 /**********************************************************************
186 * CURSORICON32_FindBestCursor
188 * Find the cursor closest to the requested size.
190 static CURSORDIRENTRY32 *CURSORICON32_FindBestCursor( CURSORICONDIR32 *dir,
191 int width, int height )
193 int i, maxwidth, maxheight;
194 CURSORDIRENTRY32 *entry, *bestEntry = NULL;
196 if (dir->idCount < 1)
198 fprintf( stderr, "Cursor: empty directory!\n" );
199 return NULL;
201 if (dir->idCount == 1) return &dir->idEntries[0].cursor; /* No choice... */
203 /* First find the largest one smaller than or equal to the requested size*/
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))
210 bestEntry = entry;
211 maxwidth = entry->wWidth;
212 maxheight = entry->wHeight;
214 if (bestEntry) return bestEntry;
216 /* Now find the smallest one larger than the requested size */
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))
222 bestEntry = entry;
223 maxwidth = entry->wWidth;
224 maxheight = entry->wHeight;
227 return bestEntry;
231 /**********************************************************************
232 * CURSORICON32_LoadDirEntry
234 * Load the icon/cursor directory for a given resource name and find the
235 * best matching entry.
237 static BOOL CURSORICON32_LoadDirEntry(HANDLE hInstance, LPCWSTR name,
238 int width, int height, int colors,
239 BOOL fCursor, CURSORICONDIRENTRY32 *dirEntry)
241 HANDLE32 hRsrc;
242 HANDLE32 hMem;
243 CURSORICONDIR32 *dir;
244 CURSORICONDIRENTRY32 *entry = NULL;
246 if (!(hRsrc = FindResource32( 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 )))
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;
260 FreeResource32( hMem );
261 return (entry != NULL);
265 /**********************************************************************
266 * CURSORICON32_LoadHandler
268 * Create a cursor or icon from a resource.
270 static HANDLE CURSORICON32_LoadHandler( HANDLE32 handle, HINSTANCE hInstance,
271 BOOL fCursor )
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;
282 hRes=0;
283 if (fCursor) /* If cursor, get the hotspot */
285 POINT16 *pt = (POINT16 *)LockResource32( handle );
286 hotspot = *pt;
287 bmi = (BITMAPINFO *)(pt + 1);
289 else bmi = (BITMAPINFO *)LockResource32( handle );
291 /* Create a copy of the bitmap header */
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 );
299 if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
301 if (pInfo->bmiHeader.biCompression != BI_RGB)
303 fprintf(stderr,"Unknown size for compressed icon bitmap.\n");
304 free( pInfo );
305 return 0;
307 pInfo->bmiHeader.biHeight /= 2;
309 else if (pInfo->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
311 BITMAPCOREHEADER *core = (BITMAPCOREHEADER *)pInfo;
312 core->bcHeight /= 2;
314 else
316 fprintf( stderr, "CURSORICON32_Load: Unknown bitmap length %ld!\n",
317 pInfo->bmiHeader.biSize );
318 free( pInfo );
319 return 0;
322 /* Create the XOR bitmap */
324 if (!(hdc = GetDC( 0 )))
326 free( pInfo );
327 return 0;
330 hXorBits = CreateDIBitmap( hdc, &pInfo->bmiHeader, CBM_INIT,
331 (char*)bmi + size, pInfo, DIB_RGB_COLORS );
333 /* Fix the bitmap header to load the monochrome mask */
335 if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
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;
347 else
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;
358 /* Create the AND bitmap */
360 hAndBits = CreateDIBitmap( hdc, &pInfo->bmiHeader, CBM_INIT,
361 bits, pInfo, DIB_RGB_COLORS );
362 ReleaseDC( 0, hdc );
364 /* Now create the CURSORICONINFO structure */
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;
371 if (!(hRes = GlobalAlloc16( GMEM_MOVEABLE,
372 sizeof(CURSORICONINFO) + sizeXor + sizeAnd)))
374 DeleteObject( hXorBits );
375 DeleteObject( hAndBits );
376 return 0;
379 /* Make it owned by the module */
380 if (hInstance) FarSetOwner( hRes, (WORD)(DWORD)GetExePtr(hInstance) );
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;
391 /* Transfer the bitmap bits to the CURSORICONINFO structure */
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;
401 /**********************************************************************
402 * CURSORICON32_Load
404 * Load a cursor or icon.
406 static HANDLE CURSORICON32_Load( HANDLE hInstance, LPCWSTR name, int width,
407 int height, int colors, BOOL fCursor )
409 HANDLE32 handle;
410 HANDLE hRet;
411 HANDLE32 hRsrc;
412 CURSORICONDIRENTRY32 dirEntry;
414 if(!hInstance) /* OEM cursor/icon */
416 WORD resid;
417 if(HIWORD(name))
419 LPSTR ansi;
420 ansi=STRING32_DupUniToAnsi(name);
421 if(ansi[0]=='#') /*Check for '#xxx' name */
423 resid=atoi(ansi+1);
424 free(ansi);
425 }else{
426 free(ansi);
427 return 0;
430 else
431 resid=(WORD)(int)name;
432 return OBM_LoadCursorIcon(resid, fCursor);
435 /* Find the best entry in the directory */
437 if (!CURSORICON32_LoadDirEntry( hInstance, name, width, height,
438 colors, fCursor, &dirEntry )) return 0;
440 /* Load the resource */
442 if (!(hRsrc = FindResource32( hInstance,
443 (LPWSTR) (DWORD) dirEntry.icon.wResId,
444 (LPWSTR) (fCursor ? RT_CURSOR : RT_ICON )))) return 0;
445 if (!(handle = LoadResource32( hInstance, hRsrc ))) return 0;
447 /* Use the resource handle as key to detect multiple loading */
448 if(CURSORICON_lookup(handle,&hRet))
449 return hRet;
451 hRet = CURSORICON32_LoadHandler( handle, hInstance, fCursor );
452 /* Obsolete - FreeResource32(handle);*/
453 CURSORICON_insert(handle,hRet);
454 return hRet;
458 /***********************************************************************
459 * LoadCursor
461 HCURSOR WIN32_LoadCursorW( HANDLE hInstance, LPCWSTR name )
463 return CURSORICON32_Load( hInstance, name,
464 SYSMETRICS_CXCURSOR, SYSMETRICS_CYCURSOR, 1, TRUE);
467 HCURSOR WIN32_LoadCursorA(HANDLE hInstance, LPCSTR name)
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);
477 return res;
481 /***********************************************************************
482 * LoadIcon
484 HICON WIN32_LoadIconW( HANDLE hInstance, LPCWSTR name )
486 return CURSORICON32_Load( hInstance, name,
487 SYSMETRICS_CXICON, SYSMETRICS_CYICON,
488 MIN( 16, 1 << screenDepth ), FALSE );
491 HICON WIN32_LoadIconA( HANDLE hInstance, LPCSTR name)
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);
501 return res;