Release 961222
[wine/multimedia.git] / win32 / cursoricon32.c
blob40a3251e4d674f4bdfa82dbf3fc0fc5c09f0e6c4
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 "callback.h"
30 #include "cursoricon.h"
31 #include "sysmetrics.h"
32 #include "win.h"
33 #include "bitmap.h"
34 #include "struct32.h"
35 #include "heap.h"
36 #include "task.h"
37 #include "stddebug.h"
38 #include "debug.h"
39 #include "xmalloc.h"
41 /* This dictionary could might eventually become a macro for better reuse */
42 struct MAP_DWORD_DWORD{
43 DWORD key;
44 DWORD value;
47 struct MAP_DWORD_DWORD *CURSORICON_map;
48 int CURSORICON_count;
50 BOOL CURSORICON_lookup(DWORD key,DWORD *value)
52 int i;
53 for(i=0;i<CURSORICON_count;i++)
55 if(key==CURSORICON_map[i].key)
57 *value=CURSORICON_map[i].value;
58 return TRUE;
61 return FALSE;
64 void CURSORICON_insert(DWORD key,DWORD value)
66 if(!CURSORICON_count)
68 CURSORICON_count=1;
69 CURSORICON_map=malloc(sizeof(struct MAP_DWORD_DWORD));
70 }else{
71 CURSORICON_count++;
72 CURSORICON_map=realloc(CURSORICON_map,
73 sizeof(struct MAP_DWORD_DWORD)*CURSORICON_count);
75 CURSORICON_map[CURSORICON_count-1].key=key;
76 CURSORICON_map[CURSORICON_count-1].value=value;
79 /**********************************************************************
80 * CURSORICON32_FindBestIcon
82 * Find the icon closest to the requested size and number of colors.
84 static ICONDIRENTRY32 *CURSORICON32_FindBestIcon( CURSORICONDIR32 *dir,
85 int width, int height, int colors )
87 int i, maxcolors, maxwidth, maxheight;
88 ICONDIRENTRY32 *entry, *bestEntry = NULL;
90 if (dir->idCount < 1)
92 fprintf( stderr, "Icon: empty directory!\n" );
93 return NULL;
95 if (dir->idCount == 1) return &dir->idEntries[0].icon; /* No choice... */
97 /* First find the exact size with less colors */
99 maxcolors = 0;
100 for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
101 if ((entry->bWidth == width) && (entry->bHeight == height) &&
102 (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors))
104 bestEntry = entry;
105 maxcolors = entry->bColorCount;
107 if (bestEntry) return bestEntry;
109 /* First find the exact size with more colors */
111 maxcolors = 255;
112 for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
113 if ((entry->bWidth == width) && (entry->bHeight == height) &&
114 (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors))
116 bestEntry = entry;
117 maxcolors = entry->bColorCount;
119 if (bestEntry) return bestEntry;
121 /* Now find a smaller one with less colors */
123 maxcolors = maxwidth = maxheight = 0;
124 for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
125 if ((entry->bWidth <= width) && (entry->bHeight <= height) &&
126 (entry->bWidth >= maxwidth) && (entry->bHeight >= maxheight) &&
127 (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors))
129 bestEntry = entry;
130 maxwidth = entry->bWidth;
131 maxheight = entry->bHeight;
132 maxcolors = entry->bColorCount;
134 if (bestEntry) return bestEntry;
136 /* Now find a smaller one with more colors */
138 maxcolors = 255;
139 maxwidth = maxheight = 0;
140 for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
141 if ((entry->bWidth <= width) && (entry->bHeight <= height) &&
142 (entry->bWidth >= maxwidth) && (entry->bHeight >= maxheight) &&
143 (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors))
145 bestEntry = entry;
146 maxwidth = entry->bWidth;
147 maxheight = entry->bHeight;
148 maxcolors = entry->bColorCount;
150 if (bestEntry) return bestEntry;
152 /* Now find a larger one with less colors */
154 maxcolors = 0;
155 maxwidth = maxheight = 255;
156 for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
157 if ((entry->bWidth <= maxwidth) && (entry->bHeight <= maxheight) &&
158 (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors))
160 bestEntry = entry;
161 maxwidth = entry->bWidth;
162 maxheight = entry->bHeight;
163 maxcolors = entry->bColorCount;
165 if (bestEntry) return bestEntry;
167 /* Now find a larger one with more colors */
169 maxcolors = maxwidth = maxheight = 255;
170 for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
171 if ((entry->bWidth <= maxwidth) && (entry->bHeight <= maxheight) &&
172 (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors))
174 bestEntry = entry;
175 maxwidth = entry->bWidth;
176 maxheight = entry->bHeight;
177 maxcolors = entry->bColorCount;
180 return bestEntry;
184 /**********************************************************************
185 * CURSORICON32_FindBestCursor
187 * Find the cursor closest to the requested size.
189 static CURSORDIRENTRY32 *CURSORICON32_FindBestCursor( CURSORICONDIR32 *dir,
190 int width, int height )
192 int i, maxwidth, maxheight;
193 CURSORDIRENTRY32 *entry, *bestEntry = NULL;
195 if (dir->idCount < 1)
197 fprintf( stderr, "Cursor: empty directory!\n" );
198 return NULL;
200 if (dir->idCount == 1) return &dir->idEntries[0].cursor; /* No choice... */
202 /* First find the largest one smaller than or equal to the requested size*/
204 maxwidth = maxheight = 0;
205 for(i = 0,entry = &dir->idEntries[0].cursor; i < dir->idCount; i++,entry++)
206 if ((entry->wWidth <= width) && (entry->wHeight <= height) &&
207 (entry->wWidth > maxwidth) && (entry->wHeight > maxheight))
209 bestEntry = entry;
210 maxwidth = entry->wWidth;
211 maxheight = entry->wHeight;
213 if (bestEntry) return bestEntry;
215 /* Now find the smallest one larger than the requested size */
217 maxwidth = maxheight = 255;
218 for(i = 0,entry = &dir->idEntries[0].cursor; i < dir->idCount; i++,entry++)
219 if ((entry->wWidth < maxwidth) && (entry->wHeight < maxheight))
221 bestEntry = entry;
222 maxwidth = entry->wWidth;
223 maxheight = entry->wHeight;
226 return bestEntry;
230 /**********************************************************************
231 * CURSORICON32_LoadDirEntry
233 * Load the icon/cursor directory for a given resource name and find the
234 * best matching entry.
236 static BOOL CURSORICON32_LoadDirEntry(HINSTANCE32 hInstance, LPCWSTR name,
237 int width, int height, int colors,
238 BOOL fCursor, CURSORICONDIRENTRY32 *dirEntry)
240 HANDLE32 hRsrc;
241 HANDLE32 hMem;
242 CURSORICONDIR32 *dir;
243 CURSORICONDIRENTRY32 *entry = NULL;
245 if (!(hRsrc = FindResource32W( hInstance, name,
246 (LPCWSTR)(fCursor ? RT_GROUP_CURSOR : RT_GROUP_ICON) )))
247 return FALSE;
248 if (!(hMem = LoadResource32( hInstance, hRsrc ))) return FALSE;
249 if ((dir = (CURSORICONDIR32 *)LockResource32( hMem )))
251 if (fCursor)
252 entry = (CURSORICONDIRENTRY32 *)CURSORICON32_FindBestCursor( dir,
253 width, height );
254 else
255 entry = (CURSORICONDIRENTRY32 *)CURSORICON32_FindBestIcon( dir,
256 width, height, colors );
257 if (entry) *dirEntry = *entry;
259 FreeResource32( hMem );
260 return (entry != NULL);
264 /**********************************************************************
265 * CURSORICON32_LoadHandler
267 * Create a cursor or icon from a resource.
269 static HGLOBAL32 CURSORICON32_LoadHandler( HANDLE32 handle,
270 HINSTANCE32 hInstance,
271 BOOL32 fCursor )
273 HBITMAP32 hAndBits, hXorBits;
274 HGLOBAL32 hRes;
275 HDC32 hdc;
276 int size, sizeAnd, sizeXor;
277 POINT16 hotspot = { 0 ,0 };
278 BITMAPOBJ *bmpXor, *bmpAnd;
279 BITMAPINFO *bmi, *pInfo;
280 CURSORICONINFO *info;
281 char *bits;
283 hRes=0;
284 if (fCursor) /* If cursor, get the hotspot */
286 POINT16 *pt = (POINT16 *)LockResource32( handle );
287 hotspot = *pt;
288 bmi = (BITMAPINFO *)(pt + 1);
290 else bmi = (BITMAPINFO *)LockResource32( handle );
292 /* Create a copy of the bitmap header */
294 size = DIB_BitmapInfoSize( bmi, DIB_RGB_COLORS );
295 /* Make sure we have room for the monochrome bitmap later on */
296 size = MAX( size, sizeof(BITMAPINFOHEADER) + 2*sizeof(RGBQUAD) );
297 pInfo = (BITMAPINFO *)xmalloc( size );
298 memcpy( pInfo, bmi, size );
300 if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
302 if (pInfo->bmiHeader.biCompression != BI_RGB)
304 fprintf(stderr,"Unknown size for compressed icon bitmap.\n");
305 free( pInfo );
306 return 0;
308 pInfo->bmiHeader.biHeight /= 2;
310 else if (pInfo->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
312 BITMAPCOREHEADER *core = (BITMAPCOREHEADER *)pInfo;
313 core->bcHeight /= 2;
315 else
317 fprintf( stderr, "CURSORICON32_Load: Unknown bitmap length %ld!\n",
318 pInfo->bmiHeader.biSize );
319 free( pInfo );
320 return 0;
323 /* Create the XOR bitmap */
325 if (!(hdc = GetDC32( 0 )))
327 free( pInfo );
328 return 0;
331 hXorBits = CreateDIBitmap32( hdc, &pInfo->bmiHeader, CBM_INIT,
332 (char*)bmi + size, pInfo, DIB_RGB_COLORS );
334 /* Fix the bitmap header to load the monochrome mask */
336 if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
338 BITMAPINFOHEADER *bih = &pInfo->bmiHeader;
339 RGBQUAD *rgb = pInfo->bmiColors;
340 bits = (char *)bmi + size +
341 DIB_GetImageWidthBytes(bih->biWidth,bih->biBitCount)*bih->biHeight;
342 bih->biBitCount = 1;
343 bih->biClrUsed = bih->biClrImportant = 2;
344 rgb[0].rgbBlue = rgb[0].rgbGreen = rgb[0].rgbRed = 0x00;
345 rgb[1].rgbBlue = rgb[1].rgbGreen = rgb[1].rgbRed = 0xff;
346 rgb[0].rgbReserved = rgb[1].rgbReserved = 0;
348 else
350 BITMAPCOREHEADER *bch = (BITMAPCOREHEADER *)pInfo;
351 RGBTRIPLE *rgb = (RGBTRIPLE *)(bch + 1);
352 bits = (char *)bmi + size +
353 DIB_GetImageWidthBytes(bch->bcWidth,bch->bcBitCount)*bch->bcHeight;
354 bch->bcBitCount = 1;
355 rgb[0].rgbtBlue = rgb[0].rgbtGreen = rgb[0].rgbtRed = 0x00;
356 rgb[1].rgbtBlue = rgb[1].rgbtGreen = rgb[1].rgbtRed = 0xff;
359 /* Create the AND bitmap */
361 hAndBits = CreateDIBitmap32( hdc, &pInfo->bmiHeader, CBM_INIT,
362 bits, pInfo, DIB_RGB_COLORS );
363 ReleaseDC32( 0, hdc );
365 /* Now create the CURSORICONINFO structure */
367 bmpXor = (BITMAPOBJ *) GDI_GetObjPtr( hXorBits, BITMAP_MAGIC );
368 bmpAnd = (BITMAPOBJ *) GDI_GetObjPtr( hAndBits, BITMAP_MAGIC );
369 sizeXor = bmpXor->bitmap.bmHeight * bmpXor->bitmap.bmWidthBytes;
370 sizeAnd = bmpAnd->bitmap.bmHeight * bmpAnd->bitmap.bmWidthBytes;
372 if (!(hRes = GlobalAlloc16( GMEM_MOVEABLE,
373 sizeof(CURSORICONINFO) + sizeXor + sizeAnd)))
375 DeleteObject32( hXorBits );
376 DeleteObject32( hAndBits );
377 return 0;
380 /* Make it owned by the module */
381 if (hInstance) FarSetOwner( hRes, (WORD)(DWORD)GetExePtr(hInstance) );
383 info = (CURSORICONINFO *)GlobalLock16( hRes );
384 info->ptHotSpot.x = hotspot.x;
385 info->ptHotSpot.y = hotspot.y;
386 info->nWidth = bmpXor->bitmap.bmWidth;
387 info->nHeight = bmpXor->bitmap.bmHeight;
388 info->nWidthBytes = bmpXor->bitmap.bmWidthBytes;
389 info->bPlanes = bmpXor->bitmap.bmPlanes;
390 info->bBitsPerPixel = bmpXor->bitmap.bmBitsPixel;
392 /* Transfer the bitmap bits to the CURSORICONINFO structure */
394 GetBitmapBits( hAndBits, sizeAnd, (char *)(info + 1) );
395 GetBitmapBits( hXorBits, sizeXor, (char *)(info + 1) + sizeAnd );
396 DeleteObject32( hXorBits );
397 DeleteObject32( hAndBits );
398 GlobalUnlock16( hRes );
399 return hRes;
402 /**********************************************************************
403 * CURSORICON32_Load
405 * Load a cursor or icon.
407 static HGLOBAL32 CURSORICON32_Load( HINSTANCE32 hInstance, LPCWSTR name,
408 int width, int height, int colors,
409 BOOL fCursor )
411 HANDLE32 handle;
412 HANDLE32 hRet;
413 HANDLE32 hRsrc;
414 CURSORICONDIRENTRY32 dirEntry;
416 if(!hInstance) /* OEM cursor/icon */
418 WORD resid;
419 if(HIWORD(name))
421 LPSTR ansi = HEAP_strdupWtoA(GetProcessHeap(),0,name);
422 if(ansi[0]=='#') /*Check for '#xxx' name */
424 resid = atoi(ansi+1);
425 HeapFree( GetProcessHeap(), 0, ansi );
427 else
429 HeapFree( GetProcessHeap(), 0, ansi );
430 return 0;
433 else resid = LOWORD(name);
434 return OBM_LoadCursorIcon(resid, fCursor);
437 /* Find the best entry in the directory */
439 if (!CURSORICON32_LoadDirEntry( hInstance, name, width, height,
440 colors, fCursor, &dirEntry )) return 0;
442 /* Load the resource */
444 if (!(hRsrc = FindResource32W( hInstance,
445 (LPWSTR) (DWORD) dirEntry.icon.wResId,
446 (LPWSTR) (fCursor ? RT_CURSOR : RT_ICON )))) return 0;
447 if (!(handle = LoadResource32( hInstance, hRsrc ))) return 0;
449 /* Use the resource handle as key to detect multiple loading */
450 if(CURSORICON_lookup(handle,&hRet))
451 return hRet;
453 hRet = CURSORICON32_LoadHandler( handle, hInstance, fCursor );
454 /* Obsolete - FreeResource32(handle);*/
455 CURSORICON_insert(handle,hRet);
456 return hRet;
460 /***********************************************************************
461 * LoadCursorW (USER32.361)
463 HCURSOR32 LoadCursor32W(HINSTANCE32 hInstance,LPCWSTR name)
465 return CURSORICON32_Load( hInstance, name,
466 SYSMETRICS_CXCURSOR, SYSMETRICS_CYCURSOR, 1, TRUE);
469 /***********************************************************************
470 * LoadCursorA (USER32.358)
472 HCURSOR32 LoadCursor32A(HINSTANCE32 hInstance,LPCSTR name)
474 HCURSOR32 res=0;
475 if(!HIWORD(name))
476 return LoadCursor32W(hInstance,(LPCWSTR)name);
477 else
479 LPWSTR uni = HEAP_strdupAtoW( GetProcessHeap(), 0, name );
480 res = LoadCursor32W(hInstance, uni);
481 HeapFree( GetProcessHeap(), 0, uni);
483 return res;
487 /***********************************************************************
488 * LoadIconW (USER32.363)
490 HICON32 LoadIcon32W(HINSTANCE32 hInstance,LPCWSTR name)
492 return CURSORICON32_Load( hInstance, name,
493 SYSMETRICS_CXICON, SYSMETRICS_CYICON,
494 MIN( 16, 1 << screenDepth ), FALSE );
497 /***********************************************************************
498 * LoadIconA (USER32.362)
500 HICON32 LoadIcon32A(HINSTANCE32 hInstance,LPCSTR name)
502 HICON32 res=0;
503 if(!HIWORD(name))
504 return LoadIcon32W(hInstance, (LPCWSTR)name);
505 else
507 LPWSTR uni = HEAP_strdupAtoW( GetProcessHeap(), 0, name );
508 res = LoadIcon32W( hInstance, uni );
509 HeapFree( GetProcessHeap(), 0, uni );
511 return res;