33#pragma warning(disable:4018)
44} CURSORICONFILEDIRENTRY;
51 CURSORICONFILEDIRENTRY idEntries[1];
85 CURSORICONDIRENTRY idEntries[1];
94static const WCHAR DISPLAYW[] = {
'D',
'I',
'S',
'P',
'L',
'A',
'Y',0};
101static int get_dib_image_size(
int width,
int height,
int depth )
103 return (((width * depth + 31) / 8) & ~3) * abs( height );
112static int bitmap_info_size(
const BITMAPINFO * info, WORD coloruse )
114 unsigned int colors, size, masks = 0;
116 if (info->bmiHeader.biSize ==
sizeof(BITMAPCOREHEADER))
118 const BITMAPCOREHEADER *core = (
const BITMAPCOREHEADER *)info;
119 colors = (core->bcBitCount <= 8) ? 1 << core->bcBitCount : 0;
120 return sizeof(BITMAPCOREHEADER) + colors *
121 ((coloruse == DIB_RGB_COLORS) ?
sizeof(RGBTRIPLE) :
sizeof(WORD));
125 colors = info->bmiHeader.biClrUsed;
128 if (!colors && (info->bmiHeader.biBitCount <= 8))
129 colors = 1 << info->bmiHeader.biBitCount;
130 if (info->bmiHeader.biCompression == BI_BITFIELDS) masks = 3;
131 size = (std::max)((
int) info->bmiHeader.biSize, (
int)(
sizeof(BITMAPINFOHEADER) + masks *
sizeof(DWORD) ));
132 return size + colors * ((coloruse == DIB_RGB_COLORS) ?
sizeof(RGBQUAD) :
sizeof(WORD));
148static BOOL is_dib_monochrome(
const BITMAPINFO* info )
150 if (info->bmiHeader.biBitCount != 1)
return FALSE;
152 if (info->bmiHeader.biSize ==
sizeof(BITMAPCOREHEADER))
154 const RGBTRIPLE *rgb = ((
const BITMAPCOREINFO*)info)->bmciColors;
157 if ((rgb->rgbtRed == 0) && (rgb->rgbtGreen == 0) && (rgb->rgbtBlue == 0))
162 return ((rgb->rgbtRed == 0xff) && (rgb->rgbtGreen == 0xff)
163 && (rgb->rgbtBlue == 0xff));
169 const RGBQUAD *rgb = info->bmiColors;
172 if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) &&
173 (rgb->rgbBlue == 0) && (rgb->rgbReserved == 0))
178 return ((rgb->rgbRed == 0xff) && (rgb->rgbGreen == 0xff)
179 && (rgb->rgbBlue == 0xff) && (rgb->rgbReserved == 0));
191static int DIB_GetBitmapInfo(
const BITMAPINFOHEADER *header, LONG *width,
192 LONG *height, WORD *bpp, DWORD *compr )
194 if (header->biSize ==
sizeof(BITMAPCOREHEADER))
196 const BITMAPCOREHEADER *core = (
const BITMAPCOREHEADER *)header;
197 *width = core->bcWidth;
198 *height = core->bcHeight;
199 *bpp = core->bcBitCount;
203 else if (header->biSize ==
sizeof(BITMAPINFOHEADER) ||
204 header->biSize ==
sizeof(BITMAPV4HEADER) ||
205 header->biSize ==
sizeof(BITMAPV5HEADER))
207 *width = header->biWidth;
208 *height = header->biHeight;
209 *bpp = header->biBitCount;
210 *compr = header->biCompression;
220typedef BOOL (*fnGetCIEntry)( LPCVOID dir, DWORD size,
int n,
221 int *width,
int *height,
int *bits );
228static int CURSORICON_FindBestIcon( LPCVOID dir, DWORD size, fnGetCIEntry get_entry,
229 int width,
int height,
int depth, UINT loadflags )
231 int i, cx, cy, bits, bestEntry = -1;
232 UINT iTotalDiff, iXDiff=0, iYDiff=0, iColorDiff;
233 UINT iTempXDiff, iTempYDiff, iTempColorDiff;
236 iTotalDiff = 0xFFFFFFFF;
237 iColorDiff = 0xFFFFFFFF;
239 if (loadflags & LR_DEFAULTSIZE)
241 if (!width) width = GetSystemMetrics( SM_CXICON );
242 if (!height) height = GetSystemMetrics( SM_CYICON );
244 else if (!width && !height)
247 if (!get_entry( dir, size, 0, &width, &height, &bits ))
return -1;
251 for ( i = 0; iTotalDiff && get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
253 iTempXDiff = abs(width - cx);
254 iTempYDiff = abs(height - cy);
256 if(iTotalDiff > (iTempXDiff + iTempYDiff))
260 iTotalDiff = iXDiff + iYDiff;
265 for ( i = 0; get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
267 if(abs(width - cx) == (
int)iXDiff && abs(height - cy) == (
int)iYDiff)
269 iTempColorDiff = abs(depth - bits);
270 if(iColorDiff > iTempColorDiff)
273 iColorDiff = iTempColorDiff;
281static BOOL CURSORICON_GetResIconEntry( LPCVOID dir, DWORD size,
int n,
282 int *width,
int *height,
int *bits )
284 const CURSORICONDIR *resdir = (
const CURSORICONDIR *)dir;
285 const ICONRESDIR *icon;
287 if ( resdir->idCount <= n )
289 if ((
const char *)&resdir->idEntries[n + 1] - (
const char *)dir > size)
291 icon = &resdir->idEntries[n].ResInfo.icon;
292 *width = icon->bWidth;
293 *height = icon->bHeight;
294 *bits = resdir->idEntries[n].wBitCount;
305static int CURSORICON_FindBestCursor( LPCVOID dir, DWORD size, fnGetCIEntry get_entry,
306 int width,
int height,
int depth, UINT loadflags )
308 int i, maxwidth, maxheight, cx, cy, bits, bestEntry = -1;
310 if (loadflags & LR_DEFAULTSIZE)
312 if (!width) width = GetSystemMetrics( SM_CXCURSOR );
313 if (!height) height = GetSystemMetrics( SM_CYCURSOR );
315 else if (!width && !height)
318 if (!get_entry( dir, size, 0, &width, &height, &bits ))
return -1;
328 maxwidth = maxheight = 0;
329 for ( i = 0; get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
331 if ((cx <= width) && (cy <= height) &&
332 (cx > maxwidth) && (cy > maxheight))
339 if (bestEntry != -1)
return bestEntry;
343 maxwidth = maxheight = 255;
344 for ( i = 0; get_entry( dir, size, i, &cx, &cy, &bits ); i++ )
346 if (((cx < maxwidth) && (cy < maxheight)) || (bestEntry == -1))
357static BOOL CURSORICON_GetResCursorEntry( LPCVOID dir, DWORD size,
int n,
358 int *width,
int *height,
int *bits )
360 const CURSORICONDIR *resdir = (
const CURSORICONDIR *)dir;
361 const CURSORDIR *cursor;
363 if ( resdir->idCount <= n )
365 if ((
const char *)&resdir->idEntries[n + 1] - (
const char *)dir > size)
367 cursor = &resdir->idEntries[n].ResInfo.cursor;
368 *width = cursor->wWidth;
369 *height = cursor->wHeight;
370 *bits = resdir->idEntries[n].wBitCount;
374static const CURSORICONDIRENTRY *CURSORICON_FindBestIconRes(
const CURSORICONDIR * dir, DWORD size,
375 int width,
int height,
int depth,
380 n = CURSORICON_FindBestIcon( dir, size, CURSORICON_GetResIconEntry,
381 width, height, depth, loadflags );
384 return &dir->idEntries[n];
387static const CURSORICONDIRENTRY *CURSORICON_FindBestCursorRes(
const CURSORICONDIR *dir, DWORD size,
388 int width,
int height,
int depth,
391 int n = CURSORICON_FindBestCursor( dir, size, CURSORICON_GetResCursorEntry,
392 width, height, depth, loadflags );
395 return &dir->idEntries[n];
398static BOOL CURSORICON_GetFileEntry( LPCVOID dir, DWORD size,
int n,
399 int *width,
int *height,
int *bits )
401 const CURSORICONFILEDIR *filedir = (
const CURSORICONFILEDIR *)dir;
402 const CURSORICONFILEDIRENTRY *entry;
403 const BITMAPINFOHEADER *info;
405 if ( filedir->idCount <= n )
407 if ((
const char *)&filedir->idEntries[n + 1] - (
const char *)dir > size)
409 entry = &filedir->idEntries[n];
410 info = (
const BITMAPINFOHEADER *)((
const char *)dir + entry->dwDIBOffset);
411 if ((
const char *)(info + 1) - (
const char *)dir > size)
return FALSE;
412 *width = entry->bWidth;
413 *height = entry->bHeight;
414 *bits = info->biBitCount;
418static const CURSORICONFILEDIRENTRY *CURSORICON_FindBestCursorFile(
const CURSORICONFILEDIR *dir, DWORD size,
419 int width,
int height,
int depth,
422 int n = CURSORICON_FindBestCursor( dir, size, CURSORICON_GetFileEntry,
423 width, height, depth, loadflags );
426 return &dir->idEntries[n];
429static const CURSORICONFILEDIRENTRY *CURSORICON_FindBestIconFile(
const CURSORICONFILEDIR *dir, DWORD size,
430 int width,
int height,
int depth,
433 int n = CURSORICON_FindBestIcon( dir, size, CURSORICON_GetFileEntry,
434 width, height, depth, loadflags );
437 return &dir->idEntries[n];
443static BOOL bmi_has_alpha(
const BITMAPINFO *info,
const void *bits )
446 BOOL has_alpha = FALSE;
447 const unsigned char *ptr = (
const unsigned char *)bits;
449 if (info->bmiHeader.biBitCount != 32)
return FALSE;
450 for (i = 0; i < info->bmiHeader.biWidth * abs(info->bmiHeader.biHeight); i++, ptr += 4)
452 has_alpha = (ptr[3] != 0);
453 if ((has_alpha))
break;
464static HBITMAP create_alpha_bitmap(
const BITMAPINFO *src_info,
const void *color_bits,
int bmWidth,
int bmHeight )
467 BITMAPINFO *info = NULL;
473 hdc = CreateCompatibleDC(0);
474 if (!(hdc))
return 0;
475 info = (BITMAPINFO *)HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(BITMAPINFO, bmiColors[256]));
476 if (!(info))
goto done;
477 info->bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
478 info->bmiHeader.biWidth = bmWidth;
479 info->bmiHeader.biHeight = -bmHeight;
480 info->bmiHeader.biPlanes = 1;
481 info->bmiHeader.biBitCount = 32;
482 info->bmiHeader.biCompression = BI_RGB;
483 info->bmiHeader.biSizeImage = bmWidth * bmHeight * 4;
484 info->bmiHeader.biXPelsPerMeter = 0;
485 info->bmiHeader.biYPelsPerMeter = 0;
486 info->bmiHeader.biClrUsed = 0;
487 info->bmiHeader.biClrImportant = 0;
488 alpha = CreateDIBSection(hdc, info, DIB_RGB_COLORS, &bits, NULL, 0);
489 if (!(alpha))
goto done;
491 SelectObject( hdc, alpha );
492 StretchDIBits( hdc, 0, 0,bmWidth, bmHeight,
493 0, 0, src_info->bmiHeader.biWidth, src_info->bmiHeader.biHeight,
494 color_bits, src_info, DIB_RGB_COLORS, SRCCOPY );
498 for (i = 0, ptr = (
unsigned char *)bits; i <bmWidth * bmHeight; i++, ptr += 4)
500 unsigned int alpha = ptr[3];
501 ptr[0] = ptr[0] * alpha / 255;
502 ptr[1] = ptr[1] * alpha / 255;
503 ptr[2] = ptr[2] * alpha / 255;
508 HeapFree( GetProcessHeap(), 0, info );
518static HICON create_icon_from_bmi(
const BITMAPINFO *bmi, DWORD maxsize, HMODULE module, LPCWSTR resname,
519 HRSRC rsrc, POINT hotspot, BOOL bIcon, INT width, INT height,
522 DWORD size, color_size, mask_size;
523 HBITMAP color = 0, mask = 0, alpha = 0;
524 const void *color_bits, *mask_bits;
525 BITMAPINFO *bmi_copy;
533 if (maxsize <
sizeof(BITMAPCOREHEADER))
537 if (maxsize < bmi->bmiHeader.biSize)
539 WARN(
"invalid header size %u\n", bmi->bmiHeader.biSize );
542 if ( (bmi->bmiHeader.biSize !=
sizeof(BITMAPCOREHEADER)) &&
543 (bmi->bmiHeader.biSize !=
sizeof(BITMAPINFOHEADER) ||
544 (bmi->bmiHeader.biCompression != BI_RGB &&
545 bmi->bmiHeader.biCompression != BI_BITFIELDS)) )
547 WARN(
"invalid bitmap header %u\n", bmi->bmiHeader.biSize );
551 size = bitmap_info_size( bmi, DIB_RGB_COLORS );
552 color_size = get_dib_image_size( bmi->bmiHeader.biWidth, bmi->bmiHeader.biHeight / 2,
553 bmi->bmiHeader.biBitCount );
554 mask_size = get_dib_image_size( bmi->bmiHeader.biWidth, bmi->bmiHeader.biHeight / 2, 1 );
555 if (size > maxsize || color_size > maxsize - size)
557 WARN(
"truncated file %u < %u+%u+%u\n", maxsize, size, color_size, mask_size );
560 if (mask_size > maxsize - size - color_size) mask_size = 0;
562 if (cFlag & LR_DEFAULTSIZE)
564 if (!width) width = GetSystemMetrics( bIcon ? SM_CXICON : SM_CXCURSOR );
565 if (!height) height = GetSystemMetrics( bIcon ? SM_CYICON : SM_CYCURSOR );
569 if (!width) width = bmi->bmiHeader.biWidth;
570 if (!height) height = bmi->bmiHeader.biHeight/2;
572 do_stretch = (bmi->bmiHeader.biHeight/2 != height) ||
573 (bmi->bmiHeader.biWidth != width);
578 hotspot.x = width / 2;
579 hotspot.y = height / 2;
583 hotspot.x = (hotspot.x * width) / bmi->bmiHeader.biWidth;
584 hotspot.y = (hotspot.y * height) / (bmi->bmiHeader.biHeight / 2);
587 if (!screen_dc) screen_dc = CreateDCW( DISPLAYW, NULL, NULL, NULL );
588 if (!screen_dc)
return 0;
590 bmi_copy = (BITMAPINFO*)HeapAlloc(GetProcessHeap(), 0, (std::max)((
int)size, (
int)FIELD_OFFSET(BITMAPINFO, bmiColors[2])));
594 hdc = CreateCompatibleDC(0);
595 if (!(hdc))
goto done;
597 memcpy( bmi_copy, bmi, size );
598 bmi_copy->bmiHeader.biHeight /= 2;
600 color_bits = (
const char*)bmi + size;
601 mask_bits = (
const char*)color_bits + color_size;
604 if (is_dib_monochrome( bmi ))
606 mask = CreateBitmap(width, height * 2, 1, 1, NULL);
607 if (!(mask))
goto done;
611 SelectObject( hdc, mask );
612 StretchDIBits( hdc, 0, height, width, height,
613 0, 0, bmi_copy->bmiHeader.biWidth, bmi_copy->bmiHeader.biHeight,
614 color_bits, bmi_copy, DIB_RGB_COLORS, SRCCOPY );
618 mask = CreateBitmap(width, height, 1, 1, NULL);
619 if (!(mask))
goto done;
620 color = CreateBitmap(width, height, GetDeviceCaps(screen_dc, PLANES),
621 GetDeviceCaps(screen_dc, BITSPIXEL), NULL);
624 DeleteObject( mask );
627 SelectObject( hdc, color );
628 StretchDIBits( hdc, 0, 0, width, height,
629 0, 0, bmi_copy->bmiHeader.biWidth, bmi_copy->bmiHeader.biHeight,
630 color_bits, bmi_copy, DIB_RGB_COLORS, SRCCOPY );
632 if (bmi_has_alpha( bmi_copy, color_bits ))
633 alpha = create_alpha_bitmap(bmi_copy, color_bits ,width,height);
636 bmi_copy->bmiHeader.biBitCount = 1;
637 if (bmi_copy->bmiHeader.biSize !=
sizeof(BITMAPCOREHEADER))
639 RGBQUAD *rgb = bmi_copy->bmiColors;
641 bmi_copy->bmiHeader.biClrUsed = bmi_copy->bmiHeader.biClrImportant = 2;
642 rgb[0].rgbBlue = rgb[0].rgbGreen = rgb[0].rgbRed = 0x00;
643 rgb[1].rgbBlue = rgb[1].rgbGreen = rgb[1].rgbRed = 0xff;
644 rgb[0].rgbReserved = rgb[1].rgbReserved = 0;
648 RGBTRIPLE *rgb = (RGBTRIPLE *)(((BITMAPCOREHEADER *)bmi_copy) + 1);
650 rgb[0].rgbtBlue = rgb[0].rgbtGreen = rgb[0].rgbtRed = 0x00;
651 rgb[1].rgbtBlue = rgb[1].rgbtGreen = rgb[1].rgbtRed = 0xff;
657 SelectObject( hdc, mask );
658 StretchDIBits( hdc, 0, 0, width, height,
659 0, 0, bmi_copy->bmiHeader.biWidth, bmi_copy->bmiHeader.biHeight,
660 mask_bits, bmi_copy, DIB_RGB_COLORS, SRCCOPY );
666 HeapFree( GetProcessHeap(), 0, bmi_copy );
669 ICONINFO iconInfo={0};
670 iconInfo.fIcon=bIcon;
671 iconInfo.xHotspot=hotspot.x;
672 iconInfo.yHotspot=hotspot.y;
675 iconInfo.hbmColor=alpha;
676 iconInfo.hbmMask=mask;
680 iconInfo.hbmColor=color;
681 iconInfo.hbmMask=mask;
684 hObj=CreateIconIndirect(&iconInfo);
685 if(color) DeleteObject( color );
686 if(alpha) DeleteObject( alpha );
687 if(mask) DeleteObject( mask );
692#define MYFIELD_OFFSET(type, field) ((LONG)(LONG_PTR)&(((type *)0)->field))
694HANDLE WINAPI LoadImageBuf(
const void* bits, UINT filesize, UINT type,
695 INT width, INT height, UINT loadflags){
696 const CURSORICONFILEDIRENTRY *entry;
697 const CURSORICONFILEDIR *dir;
700 if(type == IMAGE_BITMAP)
702 BOOL fCursor = type == IMAGE_CURSOR?TRUE:FALSE;
704 if (memcmp( bits,
"RIFF", 4 ) == 0)
706 return (HCURSOR)CreateIconFromResource((PBYTE)bits,filesize,FALSE,0x00030000);
709 dir = (
const CURSORICONFILEDIR*) bits;
710 if ( filesize < MYFIELD_OFFSET( CURSORICONFILEDIR, idEntries[dir->idCount] ))
713 if(!(loadflags & LR_MONOCHROME))
716 depth=GetDeviceCaps(hdc,BITSPIXEL);
720 entry = CURSORICON_FindBestCursorFile( dir, filesize, width, height, depth, loadflags );
722 entry = CURSORICON_FindBestIconFile( dir, filesize, width, height, depth, loadflags );
725 if ( !entry || entry->dwDIBOffset > filesize || entry->dwDIBOffset + entry->dwDIBSize > filesize )
728 hotspot.x = entry->xHotspot;
729 hotspot.y = entry->yHotspot;
730 return (HANDLE)create_icon_from_bmi( (
const BITMAPINFO *)&((LPBYTE)bits)[entry->dwDIBOffset], filesize - entry->dwDIBOffset,
731 NULL, NULL, NULL, hotspot, !fCursor, width, height, loadflags );