Commit 274e5db7 authored by Erich Hoover's avatar Erich Hoover Committed by Alexandre Julliard

user32: Add support for sequence-based animated cursors.

parent 7b333de8
...@@ -119,13 +119,13 @@ struct animated_cursoricon_object ...@@ -119,13 +119,13 @@ struct animated_cursoricon_object
HICON frames[1]; /* list of animated cursor frames */ HICON frames[1]; /* list of animated cursor frames */
}; };
static HICON alloc_icon_handle( BOOL is_ani, UINT num_frames ) static HICON alloc_icon_handle( BOOL is_ani, UINT num_steps )
{ {
struct cursoricon_object *obj; struct cursoricon_object *obj;
int icon_size; int icon_size;
if (is_ani) if (is_ani)
icon_size = FIELD_OFFSET( struct animated_cursoricon_object, frames[num_frames] ); icon_size = FIELD_OFFSET( struct animated_cursoricon_object, frames[num_steps] );
else else
icon_size = sizeof( struct static_cursoricon_object ); icon_size = sizeof( struct static_cursoricon_object );
obj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, icon_size ); obj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, icon_size );
...@@ -136,8 +136,8 @@ static HICON alloc_icon_handle( BOOL is_ani, UINT num_frames ) ...@@ -136,8 +136,8 @@ static HICON alloc_icon_handle( BOOL is_ani, UINT num_frames )
{ {
struct animated_cursoricon_object *ani_icon_data = (struct animated_cursoricon_object *) obj; struct animated_cursoricon_object *ani_icon_data = (struct animated_cursoricon_object *) obj;
ani_icon_data->num_steps = num_frames; /* changed later for some animated cursors */ ani_icon_data->num_steps = num_steps;
ani_icon_data->num_frames = num_frames; ani_icon_data->num_frames = num_steps; /* changed later for some animated cursors */
} }
return alloc_user_handle( &obj->obj, USER_ICON ); return alloc_user_handle( &obj->obj, USER_ICON );
} }
...@@ -228,8 +228,22 @@ static BOOL free_icon_handle( HICON handle ) ...@@ -228,8 +228,22 @@ static BOOL free_icon_handle( HICON handle )
{ {
struct animated_cursoricon_object *ani_icon_data = (struct animated_cursoricon_object *) obj; struct animated_cursoricon_object *ani_icon_data = (struct animated_cursoricon_object *) obj;
for (i=0; i<ani_icon_data->num_frames; i++) for (i=0; i<ani_icon_data->num_steps; i++)
{
HICON hFrame = ani_icon_data->frames[i];
if (hFrame)
{
UINT j;
free_icon_handle( ani_icon_data->frames[i] ); free_icon_handle( ani_icon_data->frames[i] );
for (j=0; j<ani_icon_data->num_steps; j++)
{
if (ani_icon_data->frames[j] == hFrame)
ani_icon_data->frames[j] = 0;
}
}
}
} }
if (!IS_INTRESOURCE( obj->resname )) HeapFree( GetProcessHeap(), 0, obj->resname ); if (!IS_INTRESOURCE( obj->resname )) HeapFree( GetProcessHeap(), 0, obj->resname );
HeapFree( GetProcessHeap(), 0, obj ); HeapFree( GetProcessHeap(), 0, obj );
...@@ -1070,15 +1084,19 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size, ...@@ -1070,15 +1084,19 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size,
struct animated_cursoricon_object *ani_icon_data; struct animated_cursoricon_object *ani_icon_data;
struct cursoricon_object *info; struct cursoricon_object *info;
DWORD *frame_rates = NULL; DWORD *frame_rates = NULL;
DWORD *frame_seq = NULL;
ani_header header = {0}; ani_header header = {0};
BOOL use_seq = FALSE;
HCURSOR cursor = 0; HCURSOR cursor = 0;
UINT i, error = 0; UINT i, error = 0;
HICON *frames;
riff_chunk_t root_chunk = { bits_size, bits }; riff_chunk_t root_chunk = { bits_size, bits };
riff_chunk_t ACON_chunk = {0}; riff_chunk_t ACON_chunk = {0};
riff_chunk_t anih_chunk = {0}; riff_chunk_t anih_chunk = {0};
riff_chunk_t fram_chunk = {0}; riff_chunk_t fram_chunk = {0};
riff_chunk_t rate_chunk = {0}; riff_chunk_t rate_chunk = {0};
riff_chunk_t seq_chunk = {0};
const unsigned char *icon_chunk; const unsigned char *icon_chunk;
const unsigned char *icon_data; const unsigned char *icon_data;
...@@ -1107,13 +1125,23 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size, ...@@ -1107,13 +1125,23 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size,
} }
if (header.flags & ANI_FLAG_SEQUENCE) if (header.flags & ANI_FLAG_SEQUENCE)
FIXME("Animated icon/cursor sequence data is not currently supported, frames may appear out of sequence.\n"); {
riff_find_chunk( ANI_seq__ID, 0, &ACON_chunk, &seq_chunk );
if (seq_chunk.data)
{
frame_seq = (DWORD *) seq_chunk.data;
use_seq = TRUE;
}
else
{
FIXME("Sequence data expected but not found, assuming steps == frames.\n");
header.num_steps = header.num_frames;
}
}
riff_find_chunk( ANI_rate_ID, 0, &ACON_chunk, &rate_chunk ); riff_find_chunk( ANI_rate_ID, 0, &ACON_chunk, &rate_chunk );
if (rate_chunk.data && header.num_steps == header.num_frames) if (rate_chunk.data)
frame_rates = (DWORD *) rate_chunk.data; 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 ); riff_find_chunk( ANI_fram_ID, ANI_LIST_ID, &ACON_chunk, &fram_chunk );
if (!fram_chunk.data) if (!fram_chunk.data)
...@@ -1122,19 +1150,19 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size, ...@@ -1122,19 +1150,19 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size,
return 0; return 0;
} }
cursor = alloc_icon_handle( TRUE, header.num_frames ); cursor = alloc_icon_handle( TRUE, header.num_steps );
if (!cursor) return 0; if (!cursor) return 0;
frames = HeapAlloc( GetProcessHeap(), 0, sizeof(DWORD)*header.num_frames );
if (!frames)
{
free_icon_handle( cursor );
return 0;
}
info = get_icon_ptr( cursor ); info = get_icon_ptr( cursor );
ani_icon_data = (struct animated_cursoricon_object *) info; ani_icon_data = (struct animated_cursoricon_object *) info;
info->is_icon = FALSE; info->is_icon = FALSE;
if (header.num_steps > header.num_frames) ani_icon_data->num_frames = header.num_frames;
{
FIXME("More steps than frames and sequence-based cursors not yet supported.\n");
ani_icon_data->num_steps = header.num_frames;
}
else
ani_icon_data->num_steps = header.num_steps;
/* The .ANI stores the display rate in jiffies (1/60s) */ /* The .ANI stores the display rate in jiffies (1/60s) */
info->delay = header.display_rate; info->delay = header.display_rate;
...@@ -1145,10 +1173,8 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size, ...@@ -1145,10 +1173,8 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size,
{ {
const DWORD chunk_size = *(const DWORD *)(icon_chunk + sizeof(DWORD)); const DWORD chunk_size = *(const DWORD *)(icon_chunk + sizeof(DWORD));
const CURSORICONFILEDIRENTRY *entry; const CURSORICONFILEDIRENTRY *entry;
struct cursoricon_frame *frame;
INT frameWidth, frameHeight; INT frameWidth, frameHeight;
const BITMAPINFO *bmi; const BITMAPINFO *bmi;
HICON hIconFrame;
entry = CURSORICON_FindBestIconFile((const CURSORICONFILEDIR *) icon_data, entry = CURSORICON_FindBestIconFile((const CURSORICONFILEDIR *) icon_data,
width, height, depth, loadflags ); width, height, depth, loadflags );
...@@ -1168,9 +1194,9 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size, ...@@ -1168,9 +1194,9 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size,
} }
/* Grab a frame from the animation */ /* Grab a frame from the animation */
hIconFrame = create_icon_from_bmi( (BITMAPINFO *)bmi, NULL, NULL, NULL, info->hotspot, frames[i] = create_icon_from_bmi( (BITMAPINFO *)bmi, NULL, NULL, NULL, info->hotspot,
FALSE, frameWidth, frameHeight, loadflags ); FALSE, frameWidth, frameHeight, loadflags );
if (!hIconFrame) if (!frames[i])
{ {
FIXME_(cursor)("failed to convert animated cursor frame.\n"); FIXME_(cursor)("failed to convert animated cursor frame.\n");
error = TRUE; error = TRUE;
...@@ -1180,20 +1206,12 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size, ...@@ -1180,20 +1206,12 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size,
ani_icon_data->num_frames = 0; ani_icon_data->num_frames = 0;
release_icon_ptr( cursor, info ); release_icon_ptr( cursor, info );
free_icon_handle( cursor ); free_icon_handle( cursor );
HeapFree( GetProcessHeap(), 0, frames );
return 0; return 0;
} }
break; break;
} }
/* Setup the animated frame */
ani_icon_data->frames[i] = hIconFrame;
frame = get_icon_frame( info, i );
if (frame_rates)
frame->delay = frame_rates[i];
else
frame->delay = ~0;
release_icon_frame( info, i, frame );
/* Advance to the next chunk */ /* Advance to the next chunk */
icon_chunk += chunk_size + (2 * sizeof(DWORD)); icon_chunk += chunk_size + (2 * sizeof(DWORD));
icon_data = icon_chunk + (2 * sizeof(DWORD)); icon_data = icon_chunk + (2 * sizeof(DWORD));
...@@ -1205,10 +1223,33 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size, ...@@ -1205,10 +1223,33 @@ static HCURSOR CURSORICON_CreateIconFromANI( const LPBYTE bits, DWORD bits_size,
FIXME_(cursor)("Error creating animated cursor, only using first frame!\n"); FIXME_(cursor)("Error creating animated cursor, only using first frame!\n");
for (i=1; i<ani_icon_data->num_frames; i++) for (i=1; i<ani_icon_data->num_frames; i++)
free_icon_handle( ani_icon_data->frames[i] ); free_icon_handle( ani_icon_data->frames[i] );
use_seq = FALSE;
info->delay = 0; info->delay = 0;
ani_icon_data->num_steps = 1; ani_icon_data->num_steps = 1;
ani_icon_data->num_frames = 1; ani_icon_data->num_frames = 1;
} }
/* Setup the animated frames in the correct sequence */
for (i=0; i<ani_icon_data->num_steps; i++)
{
DWORD frame_id = use_seq ? frame_seq[i] : i;
struct cursoricon_frame *frame;
if (frame_id >= ani_icon_data->num_frames)
{
frame_id = ani_icon_data->num_frames-1;
ERR_(cursor)("Sequence indicates frame past end of list, corrupt?\n");
}
ani_icon_data->frames[i] = frames[frame_id];
frame = get_icon_frame( info, i );
if (frame_rates)
frame->delay = frame_rates[i];
else
frame->delay = ~0;
release_icon_frame( info, i, frame );
}
HeapFree( GetProcessHeap(), 0, frames );
release_icon_ptr( cursor, info ); release_icon_ptr( cursor, info );
return cursor; return cursor;
...@@ -1851,8 +1892,6 @@ HCURSOR WINAPI GetCursorFrameInfo(HCURSOR hCursor, DWORD unk1, DWORD istep, DWOR ...@@ -1851,8 +1892,6 @@ HCURSOR WINAPI GetCursorFrameInfo(HCURSOR hCursor, DWORD unk1, DWORD istep, DWOR
FIXME("semi-stub! %p => %d %d %p %p\n", hCursor, unk1, istep, rate_jiffies, num_steps); FIXME("semi-stub! %p => %d %d %p %p\n", hCursor, unk1, istep, rate_jiffies, num_steps);
icon_steps = get_icon_steps(ptr); icon_steps = get_icon_steps(ptr);
/* Important Note: Sequences are not currently supported, so this implementation
* will not properly handle all cases. */
if (istep < icon_steps || !ptr->is_ani) if (istep < icon_steps || !ptr->is_ani)
{ {
struct animated_cursoricon_object *ani_icon_data = (struct animated_cursoricon_object *) ptr; struct animated_cursoricon_object *ani_icon_data = (struct animated_cursoricon_object *) ptr;
......
...@@ -135,14 +135,21 @@ typedef struct { ...@@ -135,14 +135,21 @@ typedef struct {
} riff_rate3_t; } riff_rate3_t;
typedef struct { typedef struct {
DWORD chunk_id; /* ANI_seq__ID */
DWORD chunk_size; /* actual size of data */
DWORD order[3]; /* animated cursor sequence data */
} riff_seq3_t;
typedef struct {
DWORD chunk_id; /* ANI_RIFF_ID */ DWORD chunk_id; /* ANI_RIFF_ID */
DWORD chunk_size; /* actual size of data */ DWORD chunk_size; /* actual size of data */
DWORD chunk_type; /* ANI_ACON_ID */ DWORD chunk_type; /* ANI_ACON_ID */
riff_header_t header; /* RIFF animated cursor header */ riff_header_t header; /* RIFF animated cursor header */
riff_seq3_t seq; /* sequence data for three cursor frames */
riff_rate3_t rates; /* rate data for three cursor frames */ riff_rate3_t rates; /* rate data for three cursor frames */
riff_list_t frame_list; /* RIFF animated cursor frame list info */ riff_list_t frame_list; /* RIFF animated cursor frame list info */
riff_icon32x32x32_t frames[3]; /* array of three animated cursor frames */ riff_icon32x32x32_t frames[3]; /* array of three animated cursor frames */
} riff_cursor3_rate_t; } riff_cursor3_seq_t;
#define EMPTY_ICON32 \ #define EMPTY_ICON32 \
{ \ { \
...@@ -243,9 +250,9 @@ riff_cursor3_t empty_anicursor3 = { ...@@ -243,9 +250,9 @@ riff_cursor3_t empty_anicursor3 = {
} }
}; };
riff_cursor3_rate_t empty_anicursor3_rate = { riff_cursor3_seq_t empty_anicursor3_seq = {
ANI_RIFF_ID, ANI_RIFF_ID,
sizeof(empty_anicursor3_rate) - sizeof(DWORD)*2, sizeof(empty_anicursor3_seq) - sizeof(DWORD)*2,
ANI_ACON_ID, ANI_ACON_ID,
{ {
ANI_anih_ID, ANI_anih_ID,
...@@ -259,10 +266,15 @@ riff_cursor3_rate_t empty_anicursor3_rate = { ...@@ -259,10 +266,15 @@ riff_cursor3_rate_t empty_anicursor3_rate = {
32, /* depth */ 32, /* depth */
1, /* planes */ 1, /* planes */
0xbeef, /* display rate in jiffies */ 0xbeef, /* display rate in jiffies */
ANI_FLAG_ICON /* flags */ ANI_FLAG_ICON|ANI_FLAG_SEQUENCE /* flags */
} }
}, },
{ {
ANI_seq__ID,
sizeof(riff_seq3_t) - sizeof(DWORD)*2,
{ 2, 0, 1} /* show frames in a uniquely identifiable order */
},
{
ANI_rate_ID, ANI_rate_ID,
sizeof(riff_rate3_t) - sizeof(DWORD)*2, sizeof(riff_rate3_t) - sizeof(DWORD)*2,
{ 0xc0de, 0xcafe, 0xbabe} { 0xc0de, 0xcafe, 0xbabe}
...@@ -1639,41 +1651,43 @@ static void test_GetCursorFrameInfo(void) ...@@ -1639,41 +1651,43 @@ static void test_GetCursorFrameInfo(void)
ok(ret, "DestroyCursor() failed (error = %d).\n", GetLastError()); ok(ret, "DestroyCursor() failed (error = %d).\n", GetLastError());
/* Creating a multi-frame animated cursor with rate data. */ /* Creating a multi-frame animated cursor with rate data. */
for (i=0; i<empty_anicursor3_rate.header.header.num_frames; i++) for (i=0; i<empty_anicursor3_seq.header.header.num_frames; i++)
{ {
empty_anicursor3_rate.frames[i].data.icon_info.idType = 2; /* type: cursor */ empty_anicursor3_seq.frames[i].data.icon_info.idType = 2; /* type: cursor */
empty_anicursor3_rate.frames[i].data.icon_info.idEntries[0].xHotspot = 3; empty_anicursor3_seq.frames[i].data.icon_info.idEntries[0].xHotspot = 3;
empty_anicursor3_rate.frames[i].data.icon_info.idEntries[0].yHotspot = 3; empty_anicursor3_seq.frames[i].data.icon_info.idEntries[0].yHotspot = 3;
memcpy( &empty_anicursor3_rate.frames[i].data.bmi_data.data[0], &frame_identifier[i], sizeof(DWORD) ); memcpy( &empty_anicursor3_seq.frames[i].data.bmi_data.data[0], &frame_identifier[i], sizeof(DWORD) );
} }
SetLastError(0xdeadbeef); SetLastError(0xdeadbeef);
h1 = CreateIconFromResource((PBYTE) &empty_anicursor3_rate, sizeof(empty_anicursor3_rate), FALSE, 0x00030000); h1 = CreateIconFromResource((PBYTE) &empty_anicursor3_seq, sizeof(empty_anicursor3_seq), FALSE, 0x00030000);
ok(h1 != NULL, "Create cursor failed (error = %x).\n", GetLastError()); ok(h1 != NULL, "Create cursor failed (error = %x).\n", GetLastError());
/* Check number of steps in multi-frame animated cursor with rate data */ /* Check number of steps in multi-frame animated cursor with rate data */
i=0; i=0;
while (DrawIconEx(hdc, 0, 0, h1, 32, 32, i, NULL, DI_NORMAL)) while (DrawIconEx(hdc, 0, 0, h1, 32, 32, i, NULL, DI_NORMAL))
i++; i++;
ok(i == empty_anicursor3_rate.header.header.num_steps, ok(i == empty_anicursor3_seq.header.header.num_steps,
"Unexpected number of steps in cursor (%d != %d)\n", "Unexpected number of steps in cursor (%d != %d)\n",
i, empty_anicursor3_rate.header.header.num_steps); i, empty_anicursor3_seq.header.header.num_steps);
/* Check GetCursorFrameInfo behavior on a multi-frame animated cursor with rate data */ /* Check GetCursorFrameInfo behavior on a multi-frame animated cursor with rate data */
for (i=0; i<empty_anicursor3_rate.header.header.num_frames; i++) for (i=0; i<empty_anicursor3_seq.header.header.num_frames; i++)
{ {
int frame_id = empty_anicursor3_seq.seq.order[i];
unk1 = unk2 = unk3 = unk4 = 0xdead; unk1 = unk2 = unk3 = unk4 = 0xdead;
h2 = pGetCursorFrameInfo(h1, &unk1, (VOID*)i, &unk3, &unk4); h2 = pGetCursorFrameInfo(h1, &unk1, (VOID*)i, &unk3, &unk4);
ok(h1 != h2 && h2 != 0, "GetCursorFrameInfo() failed for cursor %p: (%p, %p).\n", h1, h1, h2); ok(h1 != h2 && h2 != 0, "GetCursorFrameInfo() failed for cursor %p: (%p, %p).\n", h1, h1, h2);
ret = check_cursor_data( hdc, h2, &frame_identifier[i], sizeof(DWORD) ); ret = check_cursor_data( hdc, h2, &frame_identifier[frame_id], sizeof(DWORD) );
ok(ret, "GetCursorFrameInfo() returned wrong cursor data for frame %d.\n", i); ok(ret, "GetCursorFrameInfo() returned wrong cursor data for frame %d.\n", i);
ok(unk1 == 0xdead, "GetCursorFrameInfo() unexpected param 2 value (0x%x != 0xdead).\n", unk1); ok(unk1 == 0xdead, "GetCursorFrameInfo() unexpected param 2 value (0x%x != 0xdead).\n", unk1);
ok(unk2 == 0xdead, "GetCursorFrameInfo() unexpected param 3 value (0x%x != 0xdead).\n", unk2); ok(unk2 == 0xdead, "GetCursorFrameInfo() unexpected param 3 value (0x%x != 0xdead).\n", unk2);
ok(unk3 == empty_anicursor3_rate.rates.rate[i], ok(unk3 == empty_anicursor3_seq.rates.rate[i],
"GetCursorFrameInfo() unexpected param 4 value (0x%x != 0x%x).\n", "GetCursorFrameInfo() unexpected param 4 value (0x%x != 0x%x).\n",
unk3, empty_anicursor3_rate.rates.rate[i]); unk3, empty_anicursor3_seq.rates.rate[i]);
ok(unk4 == empty_anicursor3_rate.header.header.num_steps, ok(unk4 == empty_anicursor3_seq.header.header.num_steps,
"GetCursorFrameInfo() unexpected param 5 value (%d != %d).\n", "GetCursorFrameInfo() unexpected param 5 value (%d != %d).\n",
unk4, empty_anicursor3_rate.header.header.num_steps); unk4, empty_anicursor3_seq.header.header.num_steps);
} }
/* Clean up multi-frame animated cursor with rate data. */ /* Clean up multi-frame animated cursor with rate data. */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment