From 72d634b884900a2cbc4857a5a83e34bfa1f3d030 Mon Sep 17 00:00:00 2001 From: Erich Hoover Date: Sat, 8 May 2010 16:12:37 -0600 Subject: user32: Add support for animated cursors. --- dlls/user32/cursoricon.c | 180 +++++++++++++++++++++++++++++----------------- 1 files changed, 115 insertions(+), 65 deletions(-) diff --git a/dlls/user32/cursoricon.c b/dlls/user32/cursoricon.c index 57d4485..bbd8f62 100644 --- a/dlls/user32/cursoricon.c +++ b/dlls/user32/cursoricon.c @@ -744,22 +744,19 @@ static BOOL stretch_blt_icon(HBITMAP hDest, BITMAPINFO *pDestInfo, BITMAPINFO *p return res; } -static HICON CURSORICON_CreateIconFromBMI( BITMAPINFO *bmi, - POINT16 hotspot, BOOL bIcon, - DWORD dwVersion, - INT width, INT height, - UINT cFlag ) +static int CURSORICON_CreateBitmapsFromBMI( BITMAPINFO *bmi, + POINT16 *hotspot, + INT width, INT height, + HBITMAP *hXorBits, + HBITMAP *hAndBits ) { - HICON hObj; - HBITMAP hAndBits = 0, hXorBits = 0; /* error condition for later */ - ICONINFO iconinfo; + BITMAPINFO *pSrcInfo, *pDestInfo; BOOL do_stretch; INT size; - BITMAPINFO *pSrcInfo, *pDestInfo; - if (dwVersion == 0x00020000) + if( !hXorBits || !hAndBits ) { - FIXME_(cursor)("\t2.xx resources are not supported\n"); + WARN_(cursor)("\tmust have destination for images.\n"); return 0; } @@ -781,10 +778,10 @@ static HICON CURSORICON_CreateIconFromBMI( BITMAPINFO *bmi, (bmi->bmiHeader.biWidth != width); /* Scale the hotspot */ - if (do_stretch && hotspot.x != ICON_HOTSPOT && hotspot.y != ICON_HOTSPOT) + if (do_stretch && hotspot->x != ICON_HOTSPOT && hotspot->y != ICON_HOTSPOT) { - hotspot.x = (hotspot.x * width) / bmi->bmiHeader.biWidth; - hotspot.y = (hotspot.y * height) / (bmi->bmiHeader.biHeight / 2); + hotspot->x = (hotspot->x * width) / bmi->bmiHeader.biWidth; + hotspot->y = (hotspot->y * height) / (bmi->bmiHeader.biHeight / 2); } if (!screen_dc) screen_dc = CreateDCW( DISPLAYW, NULL, NULL, NULL ); @@ -819,14 +816,14 @@ static HICON CURSORICON_CreateIconFromBMI( BITMAPINFO *bmi, if(pSrcInfo->bmiHeader.biBitCount == 32) { void *pDIBBuffer = NULL; - hXorBits = CreateDIBSection(screen_dc, pDestInfo, DIB_RGB_COLORS, &pDIBBuffer, NULL, 0); + *hXorBits = CreateDIBSection(screen_dc, pDestInfo, DIB_RGB_COLORS, &pDIBBuffer, NULL, 0); - if(hXorBits) + if(*hXorBits) { - if (!stretch_blt_icon(hXorBits, pDestInfo, pSrcInfo, (char*)bmi + size)) + if (!stretch_blt_icon(*hXorBits, pDestInfo, pSrcInfo, (char*)bmi + size)) { - DeleteObject(hXorBits); - hXorBits = 0; + DeleteObject(*hXorBits); + *hXorBits = 0; } } } @@ -834,13 +831,13 @@ static HICON CURSORICON_CreateIconFromBMI( BITMAPINFO *bmi, { if (do_stretch) { - hXorBits = CreateCompatibleBitmap(screen_dc, width, height); - if (hXorBits) + *hXorBits = CreateCompatibleBitmap(screen_dc, width, height); + if (*hXorBits) { - if (!stretch_blt_icon(hXorBits, pDestInfo, pSrcInfo, (char*)bmi + size)) + if (!stretch_blt_icon(*hXorBits, pDestInfo, pSrcInfo, (char*)bmi + size)) { - DeleteObject(hXorBits); - hXorBits = 0; + DeleteObject(*hXorBits); + *hXorBits = 0; } } } @@ -848,17 +845,17 @@ static HICON CURSORICON_CreateIconFromBMI( BITMAPINFO *bmi, { if (is_dib_monochrome(bmi)) { - hXorBits = CreateBitmap(width, height, 1, 1, NULL); - SetDIBits(screen_dc, hXorBits, 0, height, + *hXorBits = CreateBitmap(width, height, 1, 1, NULL); + SetDIBits(screen_dc, *hXorBits, 0, height, (char *)bmi + size, pSrcInfo, DIB_RGB_COLORS); } else - hXorBits = CreateDIBitmap(screen_dc, &pSrcInfo->bmiHeader, + *hXorBits = CreateDIBitmap(screen_dc, &pSrcInfo->bmiHeader, CBM_INIT, (char *)bmi + size, pSrcInfo, DIB_RGB_COLORS); } } - if( hXorBits ) + if( *hXorBits ) { char* xbits = (char *)bmi + size + get_dib_width_bytes( bmi->bmiHeader.biWidth, @@ -885,25 +882,25 @@ static HICON CURSORICON_CreateIconFromBMI( BITMAPINFO *bmi, /* Create the AND bitmap */ if (do_stretch) { - hAndBits = CreateBitmap(width, height, 1, 1, NULL); + *hAndBits = CreateBitmap(width, height, 1, 1, NULL); - if (!stretch_blt_icon(hAndBits, pDestInfo, pSrcInfo, xbits)) + if (!stretch_blt_icon(*hAndBits, pDestInfo, pSrcInfo, xbits)) { - DeleteObject(hAndBits); - hAndBits = 0; + DeleteObject(*hAndBits); + *hAndBits = 0; } } else { - hAndBits = CreateBitmap(width, height, 1, 1, NULL); - SetDIBits(screen_dc, hAndBits, 0, height, + *hAndBits = CreateBitmap(width, height, 1, 1, NULL); + SetDIBits(screen_dc, *hAndBits, 0, height, xbits, pSrcInfo, DIB_RGB_COLORS); } - if( !hAndBits ) + if( !(*hAndBits) ) { - DeleteObject( hXorBits ); - hXorBits = 0; + DeleteObject( *hXorBits ); + *hXorBits = 0; } } } @@ -912,12 +909,34 @@ static HICON CURSORICON_CreateIconFromBMI( BITMAPINFO *bmi, HeapFree( GetProcessHeap(), 0, pDestInfo ); } - if( !hXorBits || !hAndBits ) + if( !(*hXorBits) || !(*hAndBits) ) { WARN_(cursor)("\tunable to create an icon bitmap.\n"); return 0; } + return 1; +} + +static HICON CURSORICON_CreateIconFromBMI( BITMAPINFO *bmi, + POINT16 hotspot, BOOL bIcon, + DWORD dwVersion, + INT width, INT height, + UINT cFlag ) +{ + HICON hObj; + ICONINFO iconinfo; + HBITMAP hAndBits = 0, hXorBits = 0; /* error condition for later */ + + if (dwVersion == 0x00020000) + { + FIXME_(cursor)("\t2.xx resources are not supported\n"); + return 0; + } + + if (!CURSORICON_CreateBitmapsFromBMI(bmi, &hotspot, width, height, &hXorBits, &hAndBits)) + return 0; /* Note: Error/Warning already output */ + iconinfo.xHotspot = hotspot.x; iconinfo.yHotspot = hotspot.y; iconinfo.hbmMask = hAndBits; @@ -1044,16 +1063,18 @@ static void riff_find_chunk( DWORD chunk_id, DWORD chunk_type, const riff_chunk_ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size, INT width, INT height, INT depth ) { - HCURSOR cursor; + DWORD nFrames, nMsDelay; ani_header header = {0}; - LPBYTE frame_bits = 0; + int i, error = FALSE; + HCURSOR cursor = 0; + ICONINFO *frames; POINT16 hotspot; - CURSORICONFILEDIRENTRY *entry; riff_chunk_t root_chunk = { bits_size, bits }; riff_chunk_t ACON_chunk = {0}; riff_chunk_t anih_chunk = {0}; riff_chunk_t fram_chunk = {0}; + const unsigned char *icon_chunk; const unsigned char *icon_data; TRACE("bits %p, bits_size %d\n", bits, bits_size); @@ -1083,36 +1104,65 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size, return 0; } - /* FIXME: For now, just load the first frame. Before we can load all the - * frames, we need to write the needed code in wineserver, etc. to handle - * cursors. Once this code is written, we can extend it to support .ani - * cursors and then update user32 and winex11.drv to load all frames. - * - * Hopefully this will at least make some games (C&C3, etc.) more playable - * in the meantime. - */ - FIXME("Loading all frames for .ani cursors not implemented.\n"); - icon_data = fram_chunk.data + (2 * sizeof(DWORD)); + nFrames = header.num_frames; + frames = HeapAlloc( GetProcessHeap(), 0, nFrames*sizeof(ICONINFO) ); + /* The .ANI stores the display rate in 1/60s, we store the delay between frames in ms */ + nMsDelay = (100 * header.display_rate) / 6; - entry = CURSORICON_FindBestIconFile( (CURSORICONFILEDIR *) icon_data, - width, height, depth ); + icon_chunk = fram_chunk.data; + icon_data = fram_chunk.data + (2 * sizeof(DWORD)); + for (i = 0; i < nFrames; i++) + { + DWORD chunk_size = *(DWORD *)(icon_chunk + sizeof(DWORD)); + CURSORICONFILEDIRENTRY *entry; + ICONINFO *frame = &frames[i]; + LPBYTE frame_bits = 0; - frame_bits = HeapAlloc( GetProcessHeap(), 0, entry->dwDIBSize ); - memcpy( frame_bits, icon_data + entry->dwDIBOffset, entry->dwDIBSize ); + entry = CURSORICON_FindBestIconFile( (CURSORICONFILEDIR *) icon_data, + width, height, depth ); - if (!header.width || !header.height) - { - header.width = entry->bWidth; - header.height = entry->bHeight; - } + frame_bits = (LPBYTE) (icon_data + entry->dwDIBOffset); + hotspot.x = entry->xHotspot; + hotspot.y = entry->yHotspot; + if (!header.width || !header.height) + { + header.width = entry->bWidth; + header.height = entry->bHeight; + } - hotspot.x = entry->xHotspot; - hotspot.y = entry->yHotspot; + /* Grab a frame from the animation */ + if (!CURSORICON_CreateBitmapsFromBMI( (BITMAPINFO *) frame_bits, &hotspot, + header.width, header.height, &frame->hbmColor, &frame->hbmMask )) + { + FIXME_(cursor)("\tfailed to convert animated cursor frame.\n"); + error = TRUE; + break; + } + frame->xHotspot = hotspot.x; + frame->yHotspot = hotspot.y; - cursor = CURSORICON_CreateIconFromBMI( (BITMAPINFO *) frame_bits, hotspot, - FALSE, 0x00030000, header.width, header.height, 0 ); + /* Advance to the next chunk */ + icon_chunk += chunk_size + (2 * sizeof(DWORD)); + icon_data = icon_chunk + (2 * sizeof(DWORD)); + } - HeapFree( GetProcessHeap(), 0, frame_bits ); + /* If there was an error but we at least decoded the first frame, so just use that frame */ + if (error) + { + FIXME_(cursor)("Error creating animated cursor, only using first frame!\n"); + for (i=1; ipCreateCursorIcon( cursor, frames, nFrames, nMsDelay ); + HeapFree( GetProcessHeap(), 0, frames ); + if (!cursor) + FIXME_(cursor)("\tfailed to create animated cursor.\n"); return cursor; } -- 1.7.0.4