diff --git a/dlls/user32/cursoricon.c b/dlls/user32/cursoricon.c index 0d73ecb1351..51eae848ad3 100644 --- a/dlls/user32/cursoricon.c +++ b/dlls/user32/cursoricon.c @@ -83,6 +83,7 @@ static struct list icon_cache = LIST_INIT( icon_cache ); struct cursoricon_frame { + UINT delay; /* frame-specific delay between this frame and the next (in jiffies) */ HBITMAP color; /* color bitmap */ HBITMAP alpha; /* pre-multiplied alpha bitmap for 32-bpp icons */ HBITMAP mask; /* mask bitmap (followed by color for 1-bpp icons) */ @@ -102,7 +103,7 @@ struct cursoricon_object POINT hotspot; UINT num_frames; /* number of frames in the icon/cursor */ UINT num_steps; /* number of sequence steps in the icon/cursor */ - UINT delay; /* delay between frames (in jiffies) */ + UINT delay; /* global delay between frames (in jiffies) */ struct cursoricon_frame frames[1]; /* icon frame information */ }; @@ -850,6 +851,7 @@ static HICON CURSORICON_CreateIconFromBMI( BITMAPINFO *bmi, HMODULE module, LPCW info->hotspot = hotspot; info->width = width; info->height = height; + info->frames[0].delay = ~0; info->frames[0].color = color; info->frames[0].mask = mask; info->frames[0].alpha = alpha; @@ -989,6 +991,7 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size, INT width, INT height, INT depth, UINT loadflags ) { struct cursoricon_object *info; + DWORD *frame_rates = NULL; ani_header header = {0}; HCURSOR cursor = 0; UINT i, error = 0; @@ -1029,8 +1032,10 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size, FIXME("Animated icon/cursor sequence data is not currently supported, frames may appear out of sequence.\n"); riff_find_chunk( ANI_rate_ID, 0, &ACON_chunk, &rate_chunk ); - if (rate_chunk.data) - FIXME("Animated icon/cursor multiple frame-frate data not currently supported.\n"); + if (rate_chunk.data && header.num_steps == header.num_frames) + frame_rates = (DWORD *) rate_chunk.data; + else if (rate_chunk.data && header.num_steps != 1) + FIXME("Animated icon/cursor rate data for sequence-based cursors not supported.\n"); riff_find_chunk( ANI_fram_ID, ANI_LIST_ID, &ACON_chunk, &fram_chunk ); if (!fram_chunk.data) @@ -1075,6 +1080,10 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size, header.width = entry->bWidth; header.height = entry->bHeight; } + if (frame_rates) + frame->delay = frame_rates[i]; + else + frame->delay = ~0; /* Grab a frame from the animation */ if (!create_icon_bitmaps( bmi, header.width, header.height, @@ -1419,6 +1428,7 @@ HICON WINAPI CopyIcon( HICON hIcon ) ptrNew->width = ptrOld->width; ptrNew->height = ptrOld->height; ptrNew->hotspot = ptrOld->hotspot; + ptrNew->frames[0].delay = ptrOld->frames[0].delay; ptrNew->frames[0].mask = copy_bitmap( ptrOld->frames[0].mask ); ptrNew->frames[0].color = copy_bitmap( ptrOld->frames[0].color ); ptrNew->frames[0].alpha = copy_bitmap( ptrOld->frames[0].alpha ); @@ -1752,7 +1762,11 @@ HCURSOR WINAPI GetCursorFrameInfo(HCURSOR hCursor, DWORD unk1, DWORD istep, DWOR *num_steps = ~0; else *num_steps = ptr->num_steps; - *rate_jiffies = ptr->delay; + /* If this specific frame does not have a delay then use the global delay */ + if (ptr->frames[istep].delay == ~0) + *rate_jiffies = ptr->delay; + else + *rate_jiffies = ptr->frames[istep].delay; } } @@ -1957,6 +1971,7 @@ HICON WINAPI CreateIconIndirect(PICONINFO iconinfo) info->is_icon = iconinfo->fIcon; info->width = width; info->height = height; + info->frames[0].delay = ~0; info->frames[0].color = color; info->frames[0].mask = mask; info->frames[0].alpha = create_alpha_bitmap( iconinfo->hbmColor, mask, NULL, NULL ); diff --git a/dlls/user32/tests/cursoricon.c b/dlls/user32/tests/cursoricon.c index 0374d5ec832..1127f1327e9 100644 --- a/dlls/user32/tests/cursoricon.c +++ b/dlls/user32/tests/cursoricon.c @@ -65,6 +65,7 @@ typedef struct #define ANI_seq__ID RIFF_FOURCC('s', 'e', 'q', ' ') #define ANI_fram_ID RIFF_FOURCC('f', 'r', 'a', 'm') #define ANI_icon_ID RIFF_FOURCC('i', 'c', 'o', 'n') +#define ANI_rate_ID RIFF_FOURCC('r', 'a', 't', 'e') #define ANI_FLAG_ICON 0x1 #define ANI_FLAG_SEQUENCE 0x2 @@ -124,9 +125,64 @@ typedef struct { DWORD chunk_type; /* ANI_ACON_ID */ riff_header_t header; /* RIFF animated cursor header */ riff_list_t frame_list; /* RIFF animated cursor frame list info */ - riff_icon32x32x32_t frames[3]; /* array of animated cursor frames */ + riff_icon32x32x32_t frames[3]; /* array of three animated cursor frames */ } riff_cursor3_t; +typedef struct { + DWORD chunk_id; /* ANI_rate_ID */ + DWORD chunk_size; /* actual size of data */ + DWORD rate[3]; /* animated cursor rate data */ +} riff_rate3_t; + +typedef struct { + DWORD chunk_id; /* ANI_RIFF_ID */ + DWORD chunk_size; /* actual size of data */ + DWORD chunk_type; /* ANI_ACON_ID */ + riff_header_t header; /* RIFF animated cursor header */ + riff_rate3_t rates; /* rate data for three cursor frames */ + riff_list_t frame_list; /* RIFF animated cursor frame list info */ + riff_icon32x32x32_t frames[3]; /* array of three animated cursor frames */ +} riff_cursor3_rate_t; + +#define EMPTY_ICON32 \ +{ \ + ANI_icon_ID, \ + sizeof(ani_frame32x32x32), \ + { \ + { \ + 0x0, /* reserved */ \ + 0, /* type: icon(1), cursor(2) */ \ + 1, /* count */ \ + { \ + { \ + 32, /* width */ \ + 32, /* height */ \ + 0, /* color count */ \ + 0x0, /* reserved */ \ + 16, /* x hotspot */ \ + 16, /* y hotspot */ \ + sizeof(ani_data32x32x32), /* DIB size */ \ + sizeof(CURSORICONFILEDIR) /* DIB offset */ \ + } \ + } \ + }, \ + { \ + sizeof(BITMAPINFOHEADER), /* structure for DIB-type data */ \ + 32, /* width */ \ + 32*2, /* actual height times two */ \ + 1, /* planes */ \ + 32, /* bpp */ \ + BI_RGB, /* compression */ \ + 0, /* image size */ \ + 0, /* biXPelsPerMeter */ \ + 0, /* biYPelsPerMeter */ \ + 0, /* biClrUsed */ \ + 0 /* biClrImportant */ \ + }, \ + { /* DIB data: left uninitialized */ } \ + } \ +} + riff_cursor1_t empty_anicursor = { ANI_RIFF_ID, sizeof(empty_anicursor) - sizeof(DWORD)*2, @@ -152,43 +208,7 @@ riff_cursor1_t empty_anicursor = { ANI_fram_ID, }, { - { - ANI_icon_ID, - sizeof(ani_frame32x32x32), - { - { - 0x0, /* reserved */ - 0, /* type: icon(1), cursor(2) */ - 1, /* count */ - { - { - 32, /* width */ - 32, /* height */ - 0, /* color count */ - 0x0, /* reserved */ - 16, /* x hotspot */ - 16, /* y hotspot */ - sizeof(ani_data32x32x32), /* DIB size */ - sizeof(CURSORICONFILEDIR) /* DIB offset */ - } - } - }, - { - sizeof(BITMAPINFOHEADER), /* structure for DIB-type data */ - 32, /* width */ - 32*2, /* actual height times two */ - 1, /* planes */ - 32, /* bpp */ - BI_RGB, /* compression */ - 0, /* image size */ - 0, /* biXPelsPerMeter */ - 0, /* biYPelsPerMeter */ - 0, /* biClrUsed */ - 0 /* biClrImportant */ - }, - { /* DIB data: left uninitialized */ } - } - } + EMPTY_ICON32 } }; @@ -217,117 +237,45 @@ riff_cursor3_t empty_anicursor3 = { ANI_fram_ID, }, { + EMPTY_ICON32, + EMPTY_ICON32, + EMPTY_ICON32 + } +}; + +riff_cursor3_rate_t empty_anicursor3_rate = { + ANI_RIFF_ID, + sizeof(empty_anicursor3_rate) - sizeof(DWORD)*2, + ANI_ACON_ID, + { + ANI_anih_ID, + sizeof(ani_header), { - ANI_icon_ID, - sizeof(ani_frame32x32x32), - { - { - 0x0, /* reserved */ - 0, /* type: icon(1), cursor(2) */ - 1, /* count */ - { - { - 32, /* width */ - 32, /* height */ - 0, /* color count */ - 0x0, /* reserved */ - 16, /* x hotspot */ - 16, /* y hotspot */ - sizeof(ani_data32x32x32), /* DIB size */ - sizeof(CURSORICONFILEDIR) /* DIB offset */ - } - } - }, - { - sizeof(BITMAPINFOHEADER), /* structure for DIB-type data */ - 32, /* width */ - 32*2, /* actual height times two */ - 1, /* planes */ - 32, /* bpp */ - BI_RGB, /* compression */ - 0, /* image size */ - 0, /* biXPelsPerMeter */ - 0, /* biYPelsPerMeter */ - 0, /* biClrUsed */ - 0 /* biClrImportant */ - }, - { /* DIB data: left uninitialized */ } - } - }, - { - ANI_icon_ID, - sizeof(ani_frame32x32x32), - { - { - 0x0, /* reserved */ - 0, /* type: icon(1), cursor(2) */ - 1, /* count */ - { - { - 32, /* width */ - 32, /* height */ - 0, /* color count */ - 0x0, /* reserved */ - 16, /* x hotspot */ - 16, /* y hotspot */ - sizeof(ani_data32x32x32), /* DIB size */ - sizeof(CURSORICONFILEDIR) /* DIB offset */ - } - } - }, - { - sizeof(BITMAPINFOHEADER), /* structure for DIB-type data */ - 32, /* width */ - 32*2, /* actual height times two */ - 1, /* planes */ - 32, /* bpp */ - BI_RGB, /* compression */ - 0, /* image size */ - 0, /* biXPelsPerMeter */ - 0, /* biYPelsPerMeter */ - 0, /* biClrUsed */ - 0 /* biClrImportant */ - }, - { /* DIB data: left uninitialized */ } - } - }, - { - ANI_icon_ID, - sizeof(ani_frame32x32x32), - { - { - 0x0, /* reserved */ - 0, /* type: icon(1), cursor(2) */ - 1, /* count */ - { - { - 32, /* width */ - 32, /* height */ - 0, /* color count */ - 0x0, /* reserved */ - 16, /* x hotspot */ - 16, /* y hotspot */ - sizeof(ani_data32x32x32), /* DIB size */ - sizeof(CURSORICONFILEDIR) /* DIB offset */ - } - } - }, - { - sizeof(BITMAPINFOHEADER), /* structure for DIB-type data */ - 32, /* width */ - 32*2, /* actual height times two */ - 1, /* planes */ - 32, /* bpp */ - BI_RGB, /* compression */ - 0, /* image size */ - 0, /* biXPelsPerMeter */ - 0, /* biYPelsPerMeter */ - 0, /* biClrUsed */ - 0 /* biClrImportant */ - }, - { /* DIB data: left uninitialized */ } - } + sizeof(ani_header), + 3, /* frames */ + 3, /* steps */ + 32, /* width */ + 32, /* height */ + 32, /* depth */ + 1, /* planes */ + 0xbeef, /* display rate in jiffies */ + ANI_FLAG_ICON /* flags */ } + }, + { + ANI_rate_ID, + sizeof(riff_rate3_t) - sizeof(DWORD)*2, + { 0xc0de, 0xcafe, 0xbabe} + }, + { + ANI_LIST_ID, + sizeof(riff_icon32x32x32_t)*(3 /*frames*/) + sizeof(DWORD), + ANI_fram_ID, + }, + { + EMPTY_ICON32, + EMPTY_ICON32, + EMPTY_ICON32 } }; @@ -1633,6 +1581,44 @@ static void test_GetCursorFrameInfo(void) ret = DestroyCursor(h1); ok(ret, "DestroyCursor() failed.\n"); + /* Creating a multi-frame animated cursor with rate data. */ + for (i=0; i