Release 960717
[wine/multimedia.git] / win32 / cursoricon32.c
blob0e256a45b3ceedcbf4c27dd4ff9fe1c3b1cd445d
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 "stddebug.h"
37 #include "debug.h"
38 #include "xmalloc.h"
39 #include "task.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(HANDLE 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 HANDLE CURSORICON32_LoadHandler( HANDLE32 handle, HINSTANCE hInstance,
270 BOOL fCursor )
272 HANDLE hAndBits, hXorBits, hRes;
273 HDC hdc;
274 int size, sizeAnd, sizeXor;
275 POINT16 hotspot = { 0 ,0 };
276 BITMAPOBJ *bmpXor, *bmpAnd;
277 BITMAPINFO *bmi, *pInfo;
278 CURSORICONINFO *info;
279 char *bits;
281 hRes=0;
282 if (fCursor) /* If cursor, get the hotspot */
284 POINT16 *pt = (POINT16 *)LockResource32( handle );
285 hotspot = *pt;
286 bmi = (BITMAPINFO *)(pt + 1);
288 else bmi = (BITMAPINFO *)LockResource32( handle );
290 /* Create a copy of the bitmap header */
292 size = DIB_BitmapInfoSize( bmi, DIB_RGB_COLORS );
293 /* Make sure we have room for the monochrome bitmap later on */
294 size = MAX( size, sizeof(BITMAPINFOHEADER) + 2*sizeof(RGBQUAD) );
295 pInfo = (BITMAPINFO *)xmalloc( size );
296 memcpy( pInfo, bmi, size );
298 if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
300 if (pInfo->bmiHeader.biCompression != BI_RGB)
302 fprintf(stderr,"Unknown size for compressed icon bitmap.\n");
303 free( pInfo );
304 return 0;
306 pInfo->bmiHeader.biHeight /= 2;
308 else if (pInfo->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
310 BITMAPCOREHEADER *core = (BITMAPCOREHEADER *)pInfo;
311 core->bcHeight /= 2;
313 else
315 fprintf( stderr, "CURSORICON32_Load: Unknown bitmap length %ld!\n",
316 pInfo->bmiHeader.biSize );
317 free( pInfo );
318 return 0;
321 /* Create the XOR bitmap */
323 if (!(hdc = GetDC( 0 )))
325 free( pInfo );
326 return 0;
329 hXorBits = CreateDIBitmap( hdc, &pInfo->bmiHeader, CBM_INIT,
330 (char*)bmi + size, pInfo, DIB_RGB_COLORS );
332 /* Fix the bitmap header to load the monochrome mask */
334 if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
336 BITMAPINFOHEADER *bih = &pInfo->bmiHeader;
337 RGBQUAD *rgb = pInfo->bmiColors;
338 bits = (char *)bmi + size +
339 DIB_GetImageWidthBytes(bih->biWidth,bih->biBitCount)*bih->biHeight;
340 bih->biBitCount = 1;
341 bih->biClrUsed = bih->biClrImportant = 2;
342 rgb[0].rgbBlue = rgb[0].rgbGreen = rgb[0].rgbRed = 0x00;
343 rgb[1].rgbBlue = rgb[1].rgbGreen = rgb[1].rgbRed = 0xff;
344 rgb[0].rgbReserved = rgb[1].rgbReserved = 0;
346 else
348 BITMAPCOREHEADER *bch = (BITMAPCOREHEADER *)pInfo;
349 RGBTRIPLE *rgb = (RGBTRIPLE *)(bch + 1);
350 bits = (char *)bmi + size +
351 DIB_GetImageWidthBytes(bch->bcWidth,bch->bcBitCount)*bch->bcHeight;
352 bch->bcBitCount = 1;
353 rgb[0].rgbtBlue = rgb[0].rgbtGreen = rgb[0].rgbtRed = 0x00;
354 rgb[1].rgbtBlue = rgb[1].rgbtGreen = rgb[1].rgbtRed = 0xff;
357 /* Create the AND bitmap */
359 hAndBits = CreateDIBitmap( hdc, &pInfo->bmiHeader, CBM_INIT,
360 bits, pInfo, DIB_RGB_COLORS );
361 ReleaseDC( 0, hdc );
363 /* Now create the CURSORICONINFO structure */
365 bmpXor = (BITMAPOBJ *) GDI_GetObjPtr( hXorBits, BITMAP_MAGIC );
366 bmpAnd = (BITMAPOBJ *) GDI_GetObjPtr( hAndBits, BITMAP_MAGIC );
367 sizeXor = bmpXor->bitmap.bmHeight * bmpXor->bitmap.bmWidthBytes;
368 sizeAnd = bmpAnd->bitmap.bmHeight * bmpAnd->bitmap.bmWidthBytes;
370 if (!(hRes = GlobalAlloc16( GMEM_MOVEABLE,
371 sizeof(CURSORICONINFO) + sizeXor + sizeAnd)))
373 DeleteObject( hXorBits );
374 DeleteObject( hAndBits );
375 return 0;
378 /* Make it owned by the module */
379 if (hInstance) FarSetOwner( hRes, (WORD)(DWORD)GetExePtr(hInstance) );
381 info = (CURSORICONINFO *)GlobalLock16( hRes );
382 info->ptHotSpot.x = hotspot.x;
383 info->ptHotSpot.y = hotspot.y;
384 info->nWidth = bmpXor->bitmap.bmWidth;
385 info->nHeight = bmpXor->bitmap.bmHeight;
386 info->nWidthBytes = bmpXor->bitmap.bmWidthBytes;
387 info->bPlanes = bmpXor->bitmap.bmPlanes;
388 info->bBitsPerPixel = bmpXor->bitmap.bmBitsPixel;
390 /* Transfer the bitmap bits to the CURSORICONINFO structure */
392 GetBitmapBits( hAndBits, sizeAnd, (char *)(info + 1) );
393 GetBitmapBits( hXorBits, sizeXor, (char *)(info + 1) + sizeAnd );
394 DeleteObject( hXorBits );
395 DeleteObject( hAndBits );
396 GlobalUnlock16( hRes );
397 return hRes;
400 /**********************************************************************
401 * CURSORICON32_Load
403 * Load a cursor or icon.
405 static HANDLE CURSORICON32_Load( HANDLE hInstance, LPCWSTR name, int width,
406 int height, int colors, BOOL fCursor )
408 HANDLE32 handle;
409 HANDLE hRet;
410 HANDLE32 hRsrc;
411 CURSORICONDIRENTRY32 dirEntry;
413 if(!hInstance) /* OEM cursor/icon */
415 WORD resid;
416 if(HIWORD(name))
418 LPSTR ansi;
419 ansi=STRING32_DupUniToAnsi(name);
420 if(ansi[0]=='#') /*Check for '#xxx' name */
422 resid=atoi(ansi+1);
423 free(ansi);
424 }else{
425 free(ansi);
426 return 0;
429 else
430 resid=(WORD)(int)name;
431 return OBM_LoadCursorIcon(resid, fCursor);
434 /* Find the best entry in the directory */
436 if (!CURSORICON32_LoadDirEntry( hInstance, name, width, height,
437 colors, fCursor, &dirEntry )) return 0;
439 /* Load the resource */
441 if (!(hRsrc = FindResource32W( hInstance,
442 (LPWSTR) (DWORD) dirEntry.icon.wResId,
443 (LPWSTR) (fCursor ? RT_CURSOR : RT_ICON )))) return 0;
444 if (!(handle = LoadResource32( hInstance, hRsrc ))) return 0;
446 /* Use the resource handle as key to detect multiple loading */
447 if(CURSORICON_lookup(handle,&hRet))
448 return hRet;
450 hRet = CURSORICON32_LoadHandler( handle, hInstance, fCursor );
451 /* Obsolete - FreeResource32(handle);*/
452 CURSORICON_insert(handle,hRet);
453 return hRet;
457 /***********************************************************************
458 * LoadCursorW (USER32.361)
460 HCURSOR32 LoadCursor32W(HINSTANCE32 hInstance,LPCWSTR name)
462 return CURSORICON32_Load( hInstance, name,
463 SYSMETRICS_CXCURSOR, SYSMETRICS_CYCURSOR, 1, TRUE);
466 /***********************************************************************
467 * LoadCursorA (USER32.358)
469 HCURSOR32 LoadCursor32A(HINSTANCE32 hInstance,LPCSTR name)
471 HCURSOR32 res=0;
472 if(!HIWORD(name))
473 return LoadCursor32W(hInstance,(LPCWSTR)name);
474 else {
475 LPWSTR uni = STRING32_DupAnsiToUni(name);
476 res = LoadCursor32W(hInstance, uni);
477 free(uni);
479 return res;
483 /***********************************************************************
484 * LoadIconW (USER32.363)
486 HICON32 LoadIcon32W(HINSTANCE32 hInstance,LPCWSTR name)
488 return CURSORICON32_Load( hInstance, name,
489 SYSMETRICS_CXICON, SYSMETRICS_CYICON,
490 MIN( 16, 1 << screenDepth ), FALSE );
493 /***********************************************************************
494 * LoadIconA (USER32.362)
496 HICON32 LoadIcon32A(HINSTANCE32 hInstance,LPCSTR name)
498 HICON32 res=0;
499 if(!HIWORD(name))
500 return LoadIcon32W(hInstance, (LPCWSTR)name);
501 else {
502 LPWSTR uni = STRING32_DupAnsiToUni(name);
503 res = LoadIcon32W(hInstance, uni);
504 free(uni);
506 return res;