[9/10] user: Add support for .ANI cursors.

H. Verbeet hverbeet at gmail.com
Sat Aug 5 16:40:41 CDT 2006


This adds support for .ANI cursors, including a small helper function
for reading RIFFs, required to load them.
-------------- next part --------------
diff --git a/dlls/user/cursoricon.c b/dlls/user/cursoricon.c
index 6fe12b0..6ade21a 100644
--- a/dlls/user/cursoricon.c
+++ b/dlls/user/cursoricon.c
@@ -42,8 +42,6 @@
  * the X client instead of in the server like other bitmaps; however,
  * some programs (notably Paint Brush) expect to be able to manipulate
  * the bits directly :-(
- *
- * FIXME: what are we going to do with animation and color (bpp > 1) cursors ?!
  */
 
 #include "config.h"
@@ -1194,6 +1192,247 @@ static BOOL load_cursor_frame( LPBYTE bi
 }
 
 /**********************************************************************
+ *          .ANI cursor support
+ */
+#define riffFOURCC( c0, c1, c2, c3 ) \
+        ( (DWORD)(BYTE)(c0) | ( (DWORD)(BYTE)(c1) << 8 ) | \
+        ( (DWORD)(BYTE)(c2) << 16 ) | ( (DWORD)(BYTE)(c3) << 24 ) )
+
+#define ANI_RIFF_ID riffFOURCC('R', 'I', 'F', 'F')
+#define ANI_LIST_ID riffFOURCC('L', 'I', 'S', 'T')
+#define ANI_ACON_ID riffFOURCC('A', 'C', 'O', 'N')
+#define ANI_anih_ID riffFOURCC('a', 'n', 'i', 'h')
+#define ANI_seq__ID riffFOURCC('s', 'e', 'q', ' ')
+#define ANI_fram_ID riffFOURCC('f', 'r', 'a', 'm')
+
+#define ANI_FLAG_ICON       0x1
+#define ANI_FLAG_SEQUENCE   0x2
+
+typedef struct {
+    DWORD header_size;
+    DWORD num_frames;
+    DWORD num_steps;
+    DWORD width;
+    DWORD height;
+    DWORD bpp;
+    DWORD num_planes;
+    DWORD display_rate;
+    DWORD flags;
+} ani_header;
+
+typedef struct {
+    DWORD           data_size;
+    const unsigned char   *data;
+} riff_chunk_t;
+
+static void dump_ani_header( const ani_header *header )
+{
+    TRACE("     header size: %ld\n", header->header_size);
+    TRACE("          frames: %ld\n", header->num_frames);
+    TRACE("           steps: %ld\n", header->num_steps);
+    TRACE("           width: %ld\n", header->width);
+    TRACE("          height: %ld\n", header->height);
+    TRACE("             bpp: %ld\n", header->bpp);
+    TRACE("          planes: %ld\n", header->num_planes);
+    TRACE("    display rate: %ld\n", header->display_rate);
+    TRACE("           flags: 0x%08lx\n", header->flags);
+}
+
+
+/*
+ * RIFF:
+ * DWORD "RIFF"
+ * DWORD size
+ * DWORD riff_id
+ * BYTE[] data
+ *
+ * LIST:
+ * DWORD "LIST"
+ * DWORD size
+ * DWORD list_id
+ * BYTE[] data
+ *
+ * CHUNK:
+ * DWORD chunk_id
+ * DWORD size
+ * BYTE[] data
+ */
+static void riff_find_chunk( DWORD chunk_id, DWORD chunk_type, const riff_chunk_t *parent_chunk, riff_chunk_t *chunk )
+{
+    const unsigned char *ptr = parent_chunk->data;
+    const unsigned char *end = parent_chunk->data + (parent_chunk->data_size - (2 * sizeof(DWORD)));
+
+    if (chunk_type == ANI_LIST_ID || chunk_type == ANI_RIFF_ID) end -= sizeof(DWORD);
+
+    while (ptr < end)
+    {
+        if ((!chunk_type && *(DWORD *)ptr == chunk_id )
+                || (chunk_type && *(DWORD *)ptr == chunk_type && *((DWORD *)ptr + 2) == chunk_id ))
+        {
+            ptr += sizeof(DWORD);
+            chunk->data_size = *(DWORD *)ptr;
+            ptr += sizeof(DWORD);
+            if (chunk_type == ANI_LIST_ID || chunk_type == ANI_RIFF_ID) ptr += sizeof(DWORD);
+            chunk->data = ptr;
+
+            return;
+        }
+
+        ptr += sizeof(DWORD);
+        ptr += *(DWORD *)ptr;
+        ptr += sizeof(DWORD);
+    }
+}
+
+
+/*
+ * .ANI layout:
+ *
+ * RIFF:'ACON'                  RIFF chunk
+ *     |- CHUNK:'anih'          Header
+ *     |- CHUNK:'seq '          Sequence information (optional)
+ *     \- LIST:'fram'           Frame list
+ *            |- CHUNK:icon     Cursor frames
+ *            |- CHUNK:icon
+ *            |- ...
+ *            \- CHUNK:icon
+ */
+static HCURSOR load_ani( const LPBYTE bits, DWORD bits_size, INT width, INT height )
+{
+    int i;
+    WORD max_count = 0;
+    HCURSOR cursor;
+    CURSORICONFILEDIR *dir = 0;
+    ani_header header = {0};
+    DWORD *frame_seq = 0;
+    cursor_frame_t *frames;
+    unsigned int frame_bits_size = 0;
+    LPBYTE frame_bits = 0;
+    POINT16 hotspot;
+
+    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 %ld\n", bits, bits_size);
+
+    if (!bits) return 0;
+
+    riff_find_chunk( ANI_ACON_ID, ANI_RIFF_ID, &root_chunk, &ACON_chunk );
+    if (!ACON_chunk.data)
+    {
+        ERR("Failed to get root chunk.\n");
+        return 0;
+    }
+
+    riff_find_chunk( ANI_anih_ID, 0, &ACON_chunk, &anih_chunk );
+    if (!anih_chunk.data)
+    {
+        ERR("Failed to get 'anih' chunk.\n");
+        return 0;
+    }
+    memcpy( &header, anih_chunk.data, sizeof(header) );
+    dump_ani_header( &header );
+
+    if (header.flags & ANI_FLAG_SEQUENCE)
+    {
+        riff_chunk_t seq_chunk = {0};
+
+        TRACE("Loading sequence data.\n");
+        riff_find_chunk( ANI_seq__ID, 0, &ACON_chunk, &seq_chunk );
+        if (!seq_chunk.data)
+        {
+            ERR("Failed to get 'seq ' chunk\n");
+            return 0;
+        }
+        frame_seq = HeapAlloc( GetProcessHeap(), 0, sizeof(DWORD) * header.num_steps );
+        memcpy( frame_seq, seq_chunk.data, sizeof(DWORD) * header.num_steps );
+    }
+
+    riff_find_chunk( ANI_fram_ID, ANI_LIST_ID, &ACON_chunk, &fram_chunk );
+    if (!fram_chunk.data)
+    {
+        ERR("Failed to get icon list\n");
+        return 0;
+    }
+
+    icon_chunk = fram_chunk.data;
+    icon_data = icon_chunk + (2 * sizeof(DWORD));
+    /* The .ANI stores the display rate in 1/60s, we store the delay between frames in ms */
+    cursor = create_cursor( header.num_steps, (100 * header.display_rate) / 6 );
+    frames = HeapAlloc( GetProcessHeap(), 0, header.num_frames * sizeof(cursor_frame_t) );
+
+    for (i = 0; i < header.num_frames; ++i)
+    {
+        WORD count;
+        CURSORICONFILEDIRENTRY *entry;
+        DWORD chunk_size = *(DWORD *)(icon_chunk + sizeof(DWORD));
+
+        /* Read icon count, skip magic */
+        memcpy( &count, icon_data + sizeof(DWORD), sizeof(WORD) );
+
+        /* There's a decent chance the amount of entries will be the same for each icon */
+        if (count > max_count)
+        {
+            HeapFree( GetProcessHeap(), 0, dir );
+            /* sizeof(CURSORICONFILEDIRENTRY) for each entry, +6 for magic & count */
+            dir = HeapAlloc( GetProcessHeap(), 0, (count * sizeof(CURSORICONFILEDIRENTRY)) + 6 );
+            max_count = count;
+        }
+
+        /* sizeof(CURSORICONFILEDIRENTRY) for each entry, +6 for magic & count */
+        memcpy( dir, icon_data, (count * sizeof(CURSORICONFILEDIRENTRY)) + 6 );
+        entry = CURSORICON_FindBestCursorFile( dir, width, height, 1 );
+
+        if (frame_bits_size < entry->dwDIBSize)
+        {
+            frame_bits_size = entry->dwDIBSize;
+            HeapFree( GetProcessHeap(), 0, frame_bits );
+            frame_bits = HeapAlloc( GetProcessHeap(), 0, frame_bits_size );
+        }
+
+        if (!header.width || !header.height)
+        {
+            header.width = entry->bWidth;
+            header.height = entry->bHeight;
+        }
+
+        hotspot.x = entry->xHotspot;
+        hotspot.y = entry->yHotspot;
+
+        memcpy( frame_bits, icon_data + entry->dwDIBOffset, entry->dwDIBSize );
+
+        load_cursor_frame( frame_bits, entry->dwDIBSize, hotspot, 0x00030000, header.width, header.height, 0, &frames[i] );
+
+        /* Advance to the next chunk */
+        icon_chunk += chunk_size + (2 * sizeof(DWORD));
+        icon_data = icon_chunk + (2 * sizeof(DWORD));
+    }
+    HeapFree( GetProcessHeap(), 0, dir );
+
+    /* Set the frames in the correct sequence */
+    for (i = 0; i < header.num_steps; ++i)
+    {
+        int frame_idx = (frame_seq ? frame_seq[i] : i);
+        set_cursor_frame( cursor, i, &frames[frame_idx] );
+    }
+
+    /* Cleanup */
+    for (i = 0; i < header.num_frames; ++i)
+    {
+        HeapFree( GetProcessHeap(), 0, frames[i].bits );
+    }
+    HeapFree( GetProcessHeap(), 0, frame_seq );
+    HeapFree( GetProcessHeap(), 0, frames );
+
+    return cursor;
+}
+
+
+/**********************************************************************
  *		CreateIconFromResourceEx (USER32.@)
  *
  * FIXME: Convert to mono when cFlag is LR_MONOCHROME. Do something
@@ -1268,6 +1507,11 @@ static HICON CURSORICON_LoadFromFile( LP
     if (!memcmp( bits, "\x00\x00\x01\x00", 4 )) fCursor = FALSE;
     /* Same thing for .CUR */
     else if (!memcmp( bits, "\x00\x00\x02\x00", 4 )) fCursor = TRUE;
+    else if (!memcmp( bits, "RIFF", 4 ))
+    {
+        hIcon = load_ani( bits, filesize, width, height );
+        goto end;
+    }
 
     dir = (CURSORICONFILEDIR*) bits;
     if ( filesize < sizeof(*dir) )


More information about the wine-patches mailing list