Release 970824
[wine/multimedia.git] / win32 / cursoricon32.c
blobb5516630e953a2d3556c59d1f373b8455e80a503
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
44 DWORD key;
45 HANDLE32 value;
48 struct MAP_DWORD_DWORD *CURSORICON_map;
49 int CURSORICON_count;
51 BOOL32 CURSORICON_lookup(DWORD key,HANDLE32 *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 BOOL32 CURSORICON32_LoadDirEntry(HINSTANCE32 hInstance, LPCWSTR name,
238 int width, int height, int colors,
239 BOOL32 fCursor, CURSORICONDIRENTRY32 *dirEntry)
241 HANDLE32 hRsrc;
242 HANDLE32 hMem;
243 CURSORICONDIR32 *dir;
244 CURSORICONDIRENTRY32 *entry = NULL;
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 )))
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 HGLOBAL32 CURSORICON32_LoadHandler( HANDLE32 handle,
271 HINSTANCE32 hInstance,
272 BOOL32 fCursor )
274 HBITMAP32 hAndBits, hXorBits;
275 HGLOBAL32 hRes;
276 HDC32 hdc;
277 int size, sizeAnd, sizeXor;
278 POINT16 hotspot = { 0 ,0 };
279 BITMAPOBJ *bmpXor, *bmpAnd;
280 BITMAPINFO *bmi, *pInfo;
281 CURSORICONINFO *info;
282 char *bits;
284 hRes=0;
285 if (fCursor) /* If cursor, get the hotspot */
287 POINT16 *pt = (POINT16 *)LockResource32( handle );
288 hotspot = *pt;
289 bmi = (BITMAPINFO *)(pt + 1);
291 else bmi = (BITMAPINFO *)LockResource32( handle );
293 /* Create a copy of the bitmap header */
295 size = DIB_BitmapInfoSize( bmi, DIB_RGB_COLORS );
296 /* Make sure we have room for the monochrome bitmap later on */
297 size = MAX( size, sizeof(BITMAPINFOHEADER) + 2*sizeof(RGBQUAD) );
298 pInfo = (BITMAPINFO *)xmalloc( size );
299 memcpy( pInfo, bmi, size );
301 if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
303 if (pInfo->bmiHeader.biCompression != BI_RGB)
305 fprintf(stderr,"Unknown size for compressed icon bitmap.\n");
306 free( pInfo );
307 return 0;
309 pInfo->bmiHeader.biHeight /= 2;
311 else if (pInfo->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
313 BITMAPCOREHEADER *core = (BITMAPCOREHEADER *)pInfo;
314 core->bcHeight /= 2;
316 else
318 fprintf( stderr, "CURSORICON32_Load: Unknown bitmap length %ld!\n",
319 pInfo->bmiHeader.biSize );
320 free( pInfo );
321 return 0;
324 /* Create the XOR bitmap */
326 if (!(hdc = GetDC32( 0 )))
328 free( pInfo );
329 return 0;
332 hXorBits = CreateDIBitmap32( hdc, &pInfo->bmiHeader, CBM_INIT,
333 (char*)bmi + size, pInfo, DIB_RGB_COLORS );
335 /* Fix the bitmap header to load the monochrome mask */
337 if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
339 BITMAPINFOHEADER *bih = &pInfo->bmiHeader;
340 RGBQUAD *rgb = pInfo->bmiColors;
341 bits = (char *)bmi + size +
342 DIB_GetDIBWidthBytes(bih->biWidth,bih->biBitCount)*bih->biHeight;
343 bih->biBitCount = 1;
344 bih->biClrUsed = bih->biClrImportant = 2;
345 rgb[0].rgbBlue = rgb[0].rgbGreen = rgb[0].rgbRed = 0x00;
346 rgb[1].rgbBlue = rgb[1].rgbGreen = rgb[1].rgbRed = 0xff;
347 rgb[0].rgbReserved = rgb[1].rgbReserved = 0;
349 else
351 BITMAPCOREHEADER *bch = (BITMAPCOREHEADER *)pInfo;
352 RGBTRIPLE *rgb = (RGBTRIPLE *)(bch + 1);
353 bits = (char *)bmi + size +
354 DIB_GetDIBWidthBytes(bch->bcWidth,bch->bcBitCount)*bch->bcHeight;
355 bch->bcBitCount = 1;
356 rgb[0].rgbtBlue = rgb[0].rgbtGreen = rgb[0].rgbtRed = 0x00;
357 rgb[1].rgbtBlue = rgb[1].rgbtGreen = rgb[1].rgbtRed = 0xff;
360 /* Create the AND bitmap */
362 hAndBits = CreateDIBitmap32( hdc, &pInfo->bmiHeader, CBM_INIT,
363 bits, pInfo, DIB_RGB_COLORS );
364 ReleaseDC32( 0, hdc );
366 /* Now create the CURSORICONINFO structure */
368 bmpXor = (BITMAPOBJ *) GDI_GetObjPtr( hXorBits, BITMAP_MAGIC );
369 bmpAnd = (BITMAPOBJ *) GDI_GetObjPtr( hAndBits, BITMAP_MAGIC );
370 sizeXor = bmpXor->bitmap.bmHeight * bmpXor->bitmap.bmWidthBytes;
371 sizeAnd = bmpAnd->bitmap.bmHeight * bmpAnd->bitmap.bmWidthBytes;
373 if (!(hRes = GlobalAlloc16( GMEM_MOVEABLE,
374 sizeof(CURSORICONINFO) + sizeXor + sizeAnd)))
376 DeleteObject32( hXorBits );
377 DeleteObject32( hAndBits );
378 return 0;
381 /* Make it owned by the module */
382 if (hInstance) FarSetOwner( hRes, (WORD)(DWORD)GetExePtr(hInstance) );
384 info = (CURSORICONINFO *)GlobalLock16( hRes );
385 info->ptHotSpot.x = hotspot.x;
386 info->ptHotSpot.y = hotspot.y;
387 info->nWidth = bmpXor->bitmap.bmWidth;
388 info->nHeight = bmpXor->bitmap.bmHeight;
389 info->nWidthBytes = bmpXor->bitmap.bmWidthBytes;
390 info->bPlanes = bmpXor->bitmap.bmPlanes;
391 info->bBitsPerPixel = bmpXor->bitmap.bmBitsPixel;
393 /* Transfer the bitmap bits to the CURSORICONINFO structure */
395 GetBitmapBits32( hAndBits, sizeAnd, (char *)(info + 1) );
396 GetBitmapBits32( hXorBits, sizeXor, (char *)(info + 1) + sizeAnd );
397 DeleteObject32( hXorBits );
398 DeleteObject32( hAndBits );
399 GlobalUnlock16( hRes );
400 return hRes;
403 /**********************************************************************
404 * CURSORICON32_Load
406 * Load a cursor or icon.
408 static HGLOBAL32 CURSORICON32_Load( HINSTANCE32 hInstance, LPCWSTR name,
409 int width, int height, int colors,
410 BOOL32 fCursor )
412 HANDLE32 handle;
413 HANDLE32 hRet;
414 HANDLE32 hRsrc;
415 CURSORICONDIRENTRY32 dirEntry;
417 if(!hInstance) /* OEM cursor/icon */
419 WORD resid;
420 if(HIWORD(name))
422 LPSTR ansi = HEAP_strdupWtoA(GetProcessHeap(),0,name);
423 if(ansi[0]=='#') /*Check for '#xxx' name */
425 resid = atoi(ansi+1);
426 HeapFree( GetProcessHeap(), 0, ansi );
428 else
430 HeapFree( GetProcessHeap(), 0, ansi );
431 return 0;
434 else resid = LOWORD(name);
435 return OBM_LoadCursorIcon(resid, fCursor);
438 /* Find the best entry in the directory */
440 if (!CURSORICON32_LoadDirEntry( hInstance, name, width, height,
441 colors, fCursor, &dirEntry )) return 0;
443 /* Load the resource */
445 if (!(hRsrc = FindResource32W( hInstance,
446 (LPWSTR) (DWORD) dirEntry.icon.wResId,
447 (LPWSTR) (fCursor ? RT_CURSOR : RT_ICON )))) return 0;
448 if (!(handle = LoadResource32( hInstance, hRsrc ))) return 0;
450 /* Use the resource handle as key to detect multiple loading */
451 if(CURSORICON_lookup(handle,&hRet))
452 return hRet;
454 hRet = CURSORICON32_LoadHandler( handle, hInstance, fCursor );
455 /* Obsolete - FreeResource32(handle);*/
456 CURSORICON_insert(handle,hRet);
457 return hRet;
461 /***********************************************************************
462 * LoadCursorW (USER32.361)
464 HCURSOR32 WINAPI LoadCursor32W(HINSTANCE32 hInstance,LPCWSTR name)
466 return CURSORICON32_Load( hInstance, name,
467 SYSMETRICS_CXCURSOR, SYSMETRICS_CYCURSOR, 1, TRUE);
470 /***********************************************************************
471 * LoadCursorA (USER32.358)
473 HCURSOR32 WINAPI LoadCursor32A(HINSTANCE32 hInstance,LPCSTR name)
475 HCURSOR32 res=0;
476 if(!HIWORD(name))
477 return LoadCursor32W(hInstance,(LPCWSTR)name);
478 else
480 LPWSTR uni = HEAP_strdupAtoW( GetProcessHeap(), 0, name );
481 res = LoadCursor32W(hInstance, uni);
482 HeapFree( GetProcessHeap(), 0, uni);
484 return res;
488 /***********************************************************************
489 * LoadIconW (USER32.363)
491 HICON32 WINAPI LoadIcon32W(HINSTANCE32 hInstance,LPCWSTR name)
493 return CURSORICON32_Load( hInstance, name,
494 SYSMETRICS_CXICON, SYSMETRICS_CYICON,
495 MIN( 16, 1 << screenDepth ), FALSE );
498 /***********************************************************************
499 * LoadIconA (USER32.362)
501 HICON32 WINAPI LoadIcon32A(HINSTANCE32 hInstance,LPCSTR name)
503 HICON32 res=0;
504 if(!HIWORD(name))
505 return LoadIcon32W(hInstance, (LPCWSTR)name);
506 else
508 LPWSTR uni = HEAP_strdupAtoW( GetProcessHeap(), 0, name );
509 res = LoadIcon32W( hInstance, uni );
510 HeapFree( GetProcessHeap(), 0, uni );
512 return res;