Release 960414
[wine.git] / win32 / cursoricon32.c
blob633eee242c19c575f029dd4c70c51d26d47af4c6
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"
43 /**********************************************************************
44 * CURSORICON32_FindBestIcon
46 * Find the icon closest to the requested size and number of colors.
48 static ICONDIRENTRY32 *CURSORICON32_FindBestIcon( CURSORICONDIR32 *dir,
49 int width, int height, int colors )
51 int i, maxcolors, maxwidth, maxheight;
52 ICONDIRENTRY32 *entry, *bestEntry = NULL;
54 if (dir->idCount < 1)
56 fprintf( stderr, "Icon: empty directory!\n" );
57 return NULL;
59 if (dir->idCount == 1) return &dir->idEntries[0].icon; /* No choice... */
61 /* First find the exact size with less colors */
63 maxcolors = 0;
64 for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
65 if ((entry->bWidth == width) && (entry->bHeight == height) &&
66 (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors))
68 bestEntry = entry;
69 maxcolors = entry->bColorCount;
71 if (bestEntry) return bestEntry;
73 /* First find the exact size with more colors */
75 maxcolors = 255;
76 for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
77 if ((entry->bWidth == width) && (entry->bHeight == height) &&
78 (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors))
80 bestEntry = entry;
81 maxcolors = entry->bColorCount;
83 if (bestEntry) return bestEntry;
85 /* Now find a smaller one with less colors */
87 maxcolors = maxwidth = maxheight = 0;
88 for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
89 if ((entry->bWidth <= width) && (entry->bHeight <= height) &&
90 (entry->bWidth >= maxwidth) && (entry->bHeight >= maxheight) &&
91 (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors))
93 bestEntry = entry;
94 maxwidth = entry->bWidth;
95 maxheight = entry->bHeight;
96 maxcolors = entry->bColorCount;
98 if (bestEntry) return bestEntry;
100 /* Now find a smaller one with more colors */
102 maxcolors = 255;
103 maxwidth = maxheight = 0;
104 for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
105 if ((entry->bWidth <= width) && (entry->bHeight <= height) &&
106 (entry->bWidth >= maxwidth) && (entry->bHeight >= maxheight) &&
107 (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors))
109 bestEntry = entry;
110 maxwidth = entry->bWidth;
111 maxheight = entry->bHeight;
112 maxcolors = entry->bColorCount;
114 if (bestEntry) return bestEntry;
116 /* Now find a larger one with less colors */
118 maxcolors = 0;
119 maxwidth = maxheight = 255;
120 for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
121 if ((entry->bWidth <= maxwidth) && (entry->bHeight <= maxheight) &&
122 (entry->bColorCount <= colors) && (entry->bColorCount > maxcolors))
124 bestEntry = entry;
125 maxwidth = entry->bWidth;
126 maxheight = entry->bHeight;
127 maxcolors = entry->bColorCount;
129 if (bestEntry) return bestEntry;
131 /* Now find a larger one with more colors */
133 maxcolors = maxwidth = maxheight = 255;
134 for (i = 0, entry = &dir->idEntries[0].icon; i < dir->idCount; i++,entry++)
135 if ((entry->bWidth <= maxwidth) && (entry->bHeight <= maxheight) &&
136 (entry->bColorCount > colors) && (entry->bColorCount <= maxcolors))
138 bestEntry = entry;
139 maxwidth = entry->bWidth;
140 maxheight = entry->bHeight;
141 maxcolors = entry->bColorCount;
144 return bestEntry;
148 /**********************************************************************
149 * CURSORICON32_FindBestCursor
151 * Find the cursor closest to the requested size.
153 static CURSORDIRENTRY32 *CURSORICON32_FindBestCursor( CURSORICONDIR32 *dir,
154 int width, int height )
156 int i, maxwidth, maxheight;
157 CURSORDIRENTRY32 *entry, *bestEntry = NULL;
159 if (dir->idCount < 1)
161 fprintf( stderr, "Cursor: empty directory!\n" );
162 return NULL;
164 if (dir->idCount == 1) return &dir->idEntries[0].cursor; /* No choice... */
166 /* First find the largest one smaller than or equal to the requested size*/
168 maxwidth = maxheight = 0;
169 for(i = 0,entry = &dir->idEntries[0].cursor; i < dir->idCount; i++,entry++)
170 if ((entry->wWidth <= width) && (entry->wHeight <= height) &&
171 (entry->wWidth > maxwidth) && (entry->wHeight > maxheight))
173 bestEntry = entry;
174 maxwidth = entry->wWidth;
175 maxheight = entry->wHeight;
177 if (bestEntry) return bestEntry;
179 /* Now find the smallest one larger than the requested size */
181 maxwidth = maxheight = 255;
182 for(i = 0,entry = &dir->idEntries[0].cursor; i < dir->idCount; i++,entry++)
183 if ((entry->wWidth < maxwidth) && (entry->wHeight < maxheight))
185 bestEntry = entry;
186 maxwidth = entry->wWidth;
187 maxheight = entry->wHeight;
190 return bestEntry;
194 /**********************************************************************
195 * CURSORICON32_LoadDirEntry
197 * Load the icon/cursor directory for a given resource name and find the
198 * best matching entry.
200 static BOOL CURSORICON32_LoadDirEntry(HANDLE hInstance, LPCWSTR name,
201 int width, int height, int colors,
202 BOOL fCursor, CURSORICONDIRENTRY32 *dirEntry)
204 HANDLE32 hRsrc;
205 HANDLE32 hMem;
206 CURSORICONDIR32 *dir;
207 CURSORICONDIRENTRY32 *entry = NULL;
209 if (!(hRsrc = FindResource32( hInstance, name,
210 (LPCWSTR)(fCursor ? RT_GROUP_CURSOR : RT_GROUP_ICON) )))
211 return FALSE;
212 if (!(hMem = LoadResource32( hInstance, hRsrc ))) return FALSE;
213 if ((dir = (CURSORICONDIR32 *)LockResource32( hMem )))
215 if (fCursor)
216 entry = (CURSORICONDIRENTRY32 *)CURSORICON32_FindBestCursor( dir,
217 width, height );
218 else
219 entry = (CURSORICONDIRENTRY32 *)CURSORICON32_FindBestIcon( dir,
220 width, height, colors );
221 if (entry) *dirEntry = *entry;
223 FreeResource32( hMem );
224 return (entry != NULL);
228 /**********************************************************************
229 * CURSORICON32_LoadHandler
231 * Create a cursor or icon from a resource.
233 static HANDLE CURSORICON32_LoadHandler( HANDLE32 handle, HINSTANCE hInstance,
234 BOOL fCursor )
236 HANDLE hAndBits, hXorBits, hRes;
237 HDC hdc;
238 int size, sizeAnd, sizeXor;
239 POINT hotspot = { 0 ,0 };
240 BITMAPOBJ *bmpXor, *bmpAnd;
241 BITMAPINFO *bmi, *pInfo;
242 CURSORICONINFO *info;
243 char *bits;
245 hRes=0;
246 if (fCursor) /* If cursor, get the hotspot */
248 POINT *pt = (POINT *)LockResource32( handle );
249 hotspot = *pt;
250 bmi = (BITMAPINFO *)(pt + 1);
252 else bmi = (BITMAPINFO *)LockResource32( handle );
254 /* Create a copy of the bitmap header */
256 size = DIB_BitmapInfoSize( bmi, DIB_RGB_COLORS );
257 /* Make sure we have room for the monochrome bitmap later on */
258 size = MAX( size, sizeof(BITMAPINFOHEADER) + 2*sizeof(RGBQUAD) );
259 pInfo = (BITMAPINFO *)xmalloc( size );
260 memcpy( pInfo, bmi, size );
262 if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
264 if (pInfo->bmiHeader.biCompression != BI_RGB)
266 fprintf(stderr,"Unknown size for compressed icon bitmap.\n");
267 free( pInfo );
268 return 0;
270 pInfo->bmiHeader.biHeight /= 2;
272 else if (pInfo->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
274 BITMAPCOREHEADER *core = (BITMAPCOREHEADER *)pInfo;
275 core->bcHeight /= 2;
277 else
279 fprintf( stderr, "CURSORICON32_Load: Unknown bitmap length %ld!\n",
280 pInfo->bmiHeader.biSize );
281 free( pInfo );
282 return 0;
285 /* Create the XOR bitmap */
287 if (!(hdc = GetDC( 0 )))
289 free( pInfo );
290 return 0;
293 hXorBits = CreateDIBitmap( hdc, &pInfo->bmiHeader, CBM_INIT,
294 (char*)bmi + size, pInfo, DIB_RGB_COLORS );
296 /* Fix the bitmap header to load the monochrome mask */
298 if (pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER))
300 BITMAPINFOHEADER *bih = &pInfo->bmiHeader;
301 RGBQUAD *rgb = pInfo->bmiColors;
302 bits = (char *)bmi + size +
303 DIB_GetImageWidthBytes(bih->biWidth,bih->biBitCount)*bih->biHeight;
304 bih->biBitCount = 1;
305 bih->biClrUsed = bih->biClrImportant = 2;
306 rgb[0].rgbBlue = rgb[0].rgbGreen = rgb[0].rgbRed = 0x00;
307 rgb[1].rgbBlue = rgb[1].rgbGreen = rgb[1].rgbRed = 0xff;
308 rgb[0].rgbReserved = rgb[1].rgbReserved = 0;
310 else
312 BITMAPCOREHEADER *bch = (BITMAPCOREHEADER *)pInfo;
313 RGBTRIPLE *rgb = (RGBTRIPLE *)(bch + 1);
314 bits = (char *)bmi + size +
315 DIB_GetImageWidthBytes(bch->bcWidth,bch->bcBitCount)*bch->bcHeight;
316 bch->bcBitCount = 1;
317 rgb[0].rgbtBlue = rgb[0].rgbtGreen = rgb[0].rgbtRed = 0x00;
318 rgb[1].rgbtBlue = rgb[1].rgbtGreen = rgb[1].rgbtRed = 0xff;
321 /* Create the AND bitmap */
323 hAndBits = CreateDIBitmap( hdc, &pInfo->bmiHeader, CBM_INIT,
324 bits, pInfo, DIB_RGB_COLORS );
325 ReleaseDC( 0, hdc );
327 /* Now create the CURSORICONINFO structure */
329 bmpXor = (BITMAPOBJ *) GDI_GetObjPtr( hXorBits, BITMAP_MAGIC );
330 bmpAnd = (BITMAPOBJ *) GDI_GetObjPtr( hAndBits, BITMAP_MAGIC );
331 sizeXor = bmpXor->bitmap.bmHeight * bmpXor->bitmap.bmWidthBytes;
332 sizeAnd = bmpAnd->bitmap.bmHeight * bmpAnd->bitmap.bmWidthBytes;
334 if (!(hRes = GlobalAlloc( GMEM_MOVEABLE,
335 sizeof(CURSORICONINFO) + sizeXor + sizeAnd)))
337 DeleteObject( hXorBits );
338 DeleteObject( hAndBits );
339 return 0;
342 /* Make it owned by the module */
343 if (hInstance) FarSetOwner( hRes, (WORD)(DWORD)GetExePtr(hInstance) );
345 info = (CURSORICONINFO *)GlobalLock( hRes );
346 info->ptHotSpot.x = hotspot.x;
347 info->ptHotSpot.y = hotspot.y;
348 info->nWidth = bmpXor->bitmap.bmWidth;
349 info->nHeight = bmpXor->bitmap.bmHeight;
350 info->nWidthBytes = bmpXor->bitmap.bmWidthBytes;
351 info->bPlanes = bmpXor->bitmap.bmPlanes;
352 info->bBitsPerPixel = bmpXor->bitmap.bmBitsPixel;
354 /* Transfer the bitmap bits to the CURSORICONINFO structure */
356 GetBitmapBits( hAndBits, sizeAnd, (char *)(info + 1) );
357 GetBitmapBits( hXorBits, sizeXor, (char *)(info + 1) + sizeAnd );
358 DeleteObject( hXorBits );
359 DeleteObject( hAndBits );
360 GlobalUnlock( hRes );
361 return hRes;
364 /**********************************************************************
365 * CURSORICON32_Load
367 * Load a cursor or icon.
369 static HANDLE CURSORICON32_Load( HANDLE hInstance, LPCWSTR name, int width,
370 int height, int colors, BOOL fCursor )
372 HANDLE32 handle;
373 HANDLE hRet;
374 HANDLE32 hRsrc;
375 CURSORICONDIRENTRY32 dirEntry;
377 if(!hInstance) /* OEM cursor/icon */
379 WORD resid;
380 if(HIWORD(name))
382 LPSTR ansi;
383 ansi=STRING32_DupUniToAnsi(name);
384 if(ansi[0]=='#') /*Check for '#xxx' name */
386 resid=atoi(ansi+1);
387 free(ansi);
388 }else{
389 free(ansi);
390 return 0;
393 else
394 resid=(WORD)(int)name;
395 return OBM_LoadCursorIcon(resid, fCursor);
398 /* Find the best entry in the directory */
400 if (!CURSORICON32_LoadDirEntry( hInstance, name, width, height,
401 colors, fCursor, &dirEntry )) return 0;
403 /* Load the resource */
405 if (!(hRsrc = FindResource32( hInstance,
406 (LPWSTR) (DWORD) dirEntry.icon.wResId,
407 (LPWSTR) (fCursor ? RT_CURSOR : RT_ICON )))) return 0;
408 if (!(handle = LoadResource32( hInstance, hRsrc ))) return 0;
410 hRet = CURSORICON32_LoadHandler( handle, hInstance, fCursor );
411 FreeResource32(handle);
412 return hRet;
416 /***********************************************************************
417 * LoadCursor
419 HCURSOR WIN32_LoadCursorW( HANDLE hInstance, LPCWSTR name )
421 return CURSORICON32_Load( hInstance, name,
422 SYSMETRICS_CXCURSOR, SYSMETRICS_CYCURSOR, 1, TRUE);
425 HCURSOR WIN32_LoadCursorA(HANDLE hInstance, LPCSTR name)
427 HCURSOR res=0;
428 if(!HIWORD(name))
429 return WIN32_LoadCursorW(hInstance, name);
430 else {
431 LPWSTR uni = STRING32_DupAnsiToUni(name);
432 res = WIN32_LoadCursorW(hInstance, uni);
433 free(uni);
435 return res;
439 /***********************************************************************
440 * LoadIcon
442 HICON WIN32_LoadIconW( HANDLE hInstance, LPCWSTR name )
444 return CURSORICON32_Load( hInstance, name,
445 SYSMETRICS_CXICON, SYSMETRICS_CYICON,
446 MIN( 16, 1 << screenDepth ), FALSE );
449 HICON WIN32_LoadIconA( HANDLE hInstance, LPCSTR name)
451 HICON res=0;
452 if(!HIWORD(name))
453 return WIN32_LoadIconW(hInstance, name);
454 else {
455 LPWSTR uni = STRING32_DupAnsiToUni(name);
456 res = WIN32_LoadIconW(hInstance, uni);
457 free(uni);
459 return res;