回 帖 发 新 帖 刷新版面

主题:[原创]ani文件数据结构以及分解ani文件的图像的代码

ani文件数据结构以及分解ani文件的图像的代码

一、ANI文件剖析
ANI 文件是动画光标文件,它在播放时所形成的动画效果,其实就是一幅幅的图标图像按一定的顺序绘制到屏幕上,保留指定的时间,并依序循环显示的结果。
ANI 文件也是RIFF格式文件(多媒体文件),它由“块”构成,块(包括子块)的基本结构见表1。只有标志块(RIFF-ACON)、图像信息块(anih)、数据块(LIST)的某些子块是必需的,其它块(或子块)可有可无,看情况而定。ANI 文件的常见块见表2。

表1 块的基本结构(长度单位:字节)
-------------------------------------------
偏移量 名称 长度 类型 说明
-------------------------------------------
00 块标识 4 字符 标识符的Ascii码
04 块长度 4 长整型
08 块内容 数据
-------------------------------------------
说明:
①块内容中所包含的数据是以字(WORD)为单位排列的,其长度必须为偶数,如果是奇数,则在最后添加一个空字节(NULL)。
②块长度不包括标识符4字节以及块长度项本身所占用的4字节。


表2:构成ANI文件的块
-------------------------------------
名称 字节数 说明
-------------------------------------
标志块 12 RIFF-ACON(文件头)
图像信息块 44 anih
数据块 LIST
时间控制块 rate
帧序控制块 seq
-------------------------------------
说明:
①LIST块只是一个总称,它不能独立存在,而必须由若干子块组成,换句话说,它的块内容就是各个子块的集合。常见的子块见表3。


表3:LIST块的两种常见子块
--------------------------------
子块名称 说明 备注
--------------------------------
存储块 fram
图标块 icon fram的子块
信息块 INFO
文件信息块 INAM INFO的子块
版权信息块 IART INFO的子块
--------------------------------
说明:
①LIST块的两种子块中,只有存储块是必需的,信息块可有可无。
②存储块下至少有2个图标子块,其具体数量保存在图像信息块的 cFrames 项。


表4:ANI文件头的结构(长度单位:字节)
-----------------------------------------------
偏移量 名称 长度 数据类型 内容
-----------------------------------------------
00 RIFF标志 4 字符 “RIFF”的Ascii码
04 文件长度 4 长整形 文件的总字节数
08 ANI标志 4 字符 “ACON”的Ascii码
-----------------------------------------------
说明:
①RIFF是英文Resource Interchange File Format的缩写,它是一种含有嵌套数据结构的二进制文件格式。所谓“嵌套”,是指块中有块(子块)。
②据笔者观察,有的 ani 文件的文件长度项值包括“RIFF”标志符4字节以及文件长度项本身所占用的4字节,有的不包括,笔者也不明白这是什么原因。
③为什么还要有一个“ANI标志”呢?这是因为除了动画光标(.ANI)以外,能以RIFF文件格式存储的数据还包括:音频视频交错格式(AVI)、位图格式(RDI)、MIDI格式(RMI)、调色板格式(PAL)、多媒体电影(RMN)、波形格式(.WAV)、其它RIFF文件(BND)。


表5:图像信息块的结构(长度单位:字节)
----------------------------------------------------
名称 长度 数据类型 说明
----------------------------------------------------
标识符 4 字符型 “anih”的Ascii码
块长度 4  长整形  其值为24(=10进制36)
cbSizeof 4  长整形 块长度,这个值包括项本身4字节
cFrames 4  长整形  图象帧数
cSteps 4  长整形 完成一次动画过程要显示的图象数
cx 4  长整形 图象宽度
cy 4  长整形 图象高度
cBitCount 4  长整形 颜色位数(每象素所占位数)
cPlanes 4  长整形 颜色位面板数
jifRate 4  长整形 jif速率
fl 4  长整形 AF_ICON和AF_SEQUENCE设置标记
----------------------------------------------------
说明:
①如果 fl=1,表示 AF_ICON 标记被设置,则 cFrame=cSteps,cx、cy、cBitCount、cPlanes这四项将被忽略,每一帧的 icon 都各自包含了这四项信息。
②如果 fl=2,表示 AF_SEQUENCE 标记被设置,则 cFrames<cSteps,也说明,在完成一次动画过程时,将会有图象被重复显示,ANI 文件中就必有帧序控制块。
③如果 fl=3,表示 AF_ICON 标记和 AF_SEQUENCE 标记都被设置。
④jif速率是图像显示的时间单位,1jif=1/60秒。如果有时间控制块,那么这个值不起作用。


表6:LIST-fram块的结构(长度单位:字节)
-----------------------------------------------
名称  长度 数据类型 说明
-----------------------------------------------
标识符 4 字符型 “LIST”的Ascii码
块长度 4 长整形
子块标识符 4 字符型 “fram”的Ascii码
子块标识符 4 字符型 “icon”的Ascii码
子块长度 4 长整形
子块内容 4 图像1的数据
子块标识符 4 字符型 “icon”的Ascii码
子块长度 4 长整形
子块内容 4 图像2的数据
……
-----------------------------------------------
说明:
①注意 fram 子块标识符的后面没有块长度这一项!
②图像数据实际上是一个完整的 ico 图标结构,一帧图像就是一个图标(一般都是调色板模式的图标)。
③我们在资源管理器中看见的 ani 文件的图像,是第一个 icon 子块的图像。
④关于图标的数据结构,请参阅笔者的《Ico文件数据结构》一文。


表7:时间控制块的结构(长度单位:字节)
---------------------------------------------------------
名称  长度 数据类型 说明
---------------------------------------------------------
标识符 4 字符型 “rate”的Ascii码
块长度 4  长整形 rate块的大小
块内容 块内容由若干帧显示时间项组成,要显
示的图象有几帧就有几个对应的时间项
帧显示时间 4  长整形 第一帧图像在屏幕上的显示时间
帧显示时间 4  长整形 第二帧图像在屏幕上的显示时间
……
帧显示时间 4  长整形 第N帧图像在屏幕上的显示时间
---------------------------------------------------------
说明:
①帧显示时间项的数量由图像信息块中的 cSteps 项值决定。
②帧显示时间项值表示该帧要显示几个时间单位。
③如果每幅图象的显示时间完全一样,ANI 文件就没有必要保存多个相同的时间值,而是把这个时间值保存在图像信息块的 jifRate 项里,这样,ANI 文件里就没有时间控制块。
④如果每幅图象的显示时间不一样,那就必须有时间控制块。这时,图像信息块中的 jifRate 项仍然存在,只是没有作用。


表8:帧序控制块的结构(长度单位:字节)
---------------------------------------------------------
名称  长度 数据类型 说明
---------------------------------------------------------
标识符 4 字符型 “seq ”的Ascii码
块长度 4  长整形 seq块的大小
块内容 块内容由若干帧显示序号项组成,要显
示的图象有几帧就有几个对应的序号项
帧显示序号 4  长整形 第一帧图像在屏幕上的显示序号
帧显示序号 4  长整形 第二帧图像在屏幕上的显示序号
……
帧显示序号 4  长整形 第N帧图像在屏幕上的显示序号
---------------------------------------------------------
说明:
①序号是各个图象(即icon子块)在 ANI 文件中存放的先后顺序,第一个图象的序号定义为0,第二个图象的序号定义为1,依次类推。
②显示顺序并不一定等于图象存放的先后顺序。
③如果没有帧序控制块,ANI 文件中保存的各幅图象数据均不相同,完成一次完整的动画过程要显示的图象数=文件保存的图象数。显示时,从第一幅图象开始,按文件中保存的图象的先后顺序,依次显示,一直到最后一幅图象显示完毕,然后重新从第一幅图象开始下一次的动画过程,循环往复。
④如果有帧序控制块, 说明ANI文件中的图象帧数<完成一次动画过程要显示的图象帧数(这时图像信息块中的 AF_SEQUENCE 标记应该已被设置),必定有的图像要显示的次数>1。例如一个ANI文件中有三个 icon 子块,分别为icon0、icon1、icon2,而一次完整的动画过程要显示四幅图象,其中icon1要显示两次,即:icon0-icon1-icon2-icon1,在这种情况下,ANI文件没有必要把 icon1 的数据保存两次,而只是保存一次,但增加了帧序控制块,在帧序控制块中保存了完成一次完整的动画过程所要显示的图象的序号,这样就有效地减小了文件的大小,避免了冗余数据的保存和处理。


表9:LIST-INFO块的结构(长度单位:字节)
---------------------------------------------------------
名称   长度 数据类型 说明
---------------------------------------------------------
标识符 4 字符型 “LIST”的Ascii码
块长度 4 长整形
子块标识符 4 字符型 “INFO”的Ascii码,信息子块
子块标识符 4 字符型 “INAM”的Ascii码,文件信息块
子块长度 4 长整形
子块内容 4 字符型 INAM块的内容
子块标识符 4 字符型 “IART”的Ascii码,版权信息块
子块长度 4 长整形
子块内容 4 字符型 IART块的内容
---------------------------------------------------------
说明:
①注意 INFO 子块标识符的后面没有块长度这一项!
②INFO 子块又有2个子块:文件信息块、版权信息块。

回复列表 (共13个回复)

沙发

二、实例
  1.以下是 banana.ani 文件的前301字节:
------------------------------------------------------
0000: 52 49 46 46 78 2E 00 00 41 43 4F 4E 4C 49 53 54
0010: 4A 00 00 00 49 4E 46 4F 49 4E 41 4D 0F 00 00 00
0020: 50 65 65 6C 69 6E 67 20 42 61 6E 61 6E 61 00 00
0030: 49 41 52 54 26 00 00 00 4D 69 63 72 6F 73 6F 66
0040: 74 20 43 6F 72 70 6F 72 61 74 69 6F 6E 2C 20 43
0050: 6F 70 79 72 69 67 68 74 20 31 39 39 33 00 61 6E
0060: 69 68 24 00 00 00 24 00 00 00 0F 00 00 00 10 00
0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0080: 00 00 0F 00 00 00 03 00 00 00 72 61 74 65 40 00
0090: 00 00 0F 00 00 00 0F 00 00 00 0F 00 00 00 0F 00
00A0: 00 00 0F 00 00 00 0F 00 00 00 0F 00 00 00 0F 00
00B0: 00 00 0F 00 00 00 0F 00 00 00 0F 00 00 00 1E 00
00C0: 00 00 14 00 00 00 14 00 00 00 32 00 00 00 1E 00
00D0: 00 00 73 65 71 20 40 00 00 00 00 00 00 00 01 00
00E0: 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00
00F0: 00 00 06 00 00 00 07 00 00 00 08 00 00 00 09 00
0100: 00 00 0A 00 00 00 0B 00 00 00 0C 00 00 00 0D 00
0110: 00 00 0E 00 00 00 00 00 00 00 4C 49 53 54 5E 2D
0120: 00 00 66 72 61 6D 69 63 6F 6E FE 02 00 00
-------------------------------------------------------

  2.简要分析:
0000-000B:文件头
 0000-0003:=“RIFF”,标识符
 0004-0007:=78 2E 00 00,文件大小为 11896+8 字节
 0008-000B:=“ACON”,标识符
000C-005D:LIST-INFO块
 000C-000F:=“LIST”,标识符
 0010-0013:=4A 00 00 00,块长度为74字节
  0014-0017:=“INFO”,信息子块标识符
   0018-001B:=“INAM”,文件信息子块标识符
   001C-001F:=0F 00 00 00,文件信息子块长度为15字节
   0020-0023:=“Peeling Banana ”,块内容
   0030-0033:=“IART”,版权信息子块标识符
   0034-0037:=26 00 00 00,版权信息子块长度为38字节
   0038-005D:=“Microsoft Corporation, Copyright 1993 ”,块内容
005E-0089:图像信息块
 005E-0061:=“anih”,标识符
 0062-0065:=24 00 00 00,块长度为36字节
 0066-0069:同上
 006A-006D:=0F 00 00 00,保存了15帧图像,序号0—14
 006E-0071:=10 00 00 00,完成一次动画过程需要显示16帧图像
 0072-0075:图像宽度,由于 AF_ICON 标记被设置,该值无效
 0076-0079:图像高度,同上
 007A-007D:每象素占位,同上
 007E-0081:位面板数,同上
 0082-0085:jif速率,同上
 0086-0089:=03 00 00 00, AF_ICON 标记和 AF_SEQUENCE 标记均被设置
008A-00D1:时间控制块
 008A-008D:=“rate”,标识符
 008E-0091:=40 00 00 00,块长度为60字节
0092-0095:=0F 00 00 00,第1帧图像显示时间为15个jif,=1/60×15=0.25秒
0096-0099:=0F 00 00 00,第2帧图像显示时间为15个jif,同上
 ……
00BE-00C1:=1E 00 00 00,第12帧图像显示时间为30个jif,=1/60×30=0.5秒
00C2-00C5:=14 00 00 00,第13帧图像显示时间为20个jif,=1/60×20=0.33秒
00C6-00C9:=14 00 00 00,第14帧图像显示时间为20个jif,=1/60×20=0.33秒
00CA-00CD:=32 00 00 00,第15帧图像显示时间为50个jif,=1/60×50=0.83秒
00CE-00D1:=1E 00 00 00,第16帧图像显示时间为30个jif,=1/60×30=0.5秒
00D2-0119:帧序控制块
 00D2-00D5:="seq ",标识符
 00D6-00D9:=40 00 00 00,块长度为60字节
 00DA-00DD:=00 00 00 00,第1帧显示序号为0的图像
 00DE-00E1:=01 00 00 00,第2帧显示序号为1的图像
 00E2-00E5:=02 00 00 00,第3帧显示序号为2的图像
 ……
 0112-0115:=0E 00 00 00,第15帧显示序号为14的图像
 0116-0119:=00 00 00 00,第16帧显示序号为0的图像
011A-2E7F:LIST-fram块
 011A-011D:=“LIST”,LIST块标识符
 011E-0121:=5E 2D 00 00,块长度为11614字节
  0122-0125:=“fram”,fram子块标识符
   0126-042B:第1个icon子块,其图像序号为0
    0126-0129:=“icon”,标识符
    012A-012D:=FE 02 00 00,icon子块长度为766字节
    012E-042B:icon子块内容,是一个标准的16色32×32图标数据
   042C-0731:第2个icon子块,其图像序号为1
   ……
   2B7A-2E7F:第15个icon子块,其图像序号为14


三、分解 ani 文件的图像的代码,见附件。
  分解出来的图像数据可以分别保存为 ico 图标文件,代码请自行编写,就作为家庭作业吧。
  另外,根据本文的讲解,你应该能够轻松地编写出制作 ani 动画光标的程序,动手吧!

板凳

不知道为什么我编排得很整齐的文本,显示出来却挤成了一团?

3 楼

更正:
附件代码的Command1_Click过程中,ReDim bytData(LOF(1) - 1)应为ReDim bytData(LOF(fn) - 1)

4 楼

顶一下。

5 楼

看帖是学习,回帖更是礼貌!!!

6 楼

说的很详尽,谢谢! 对于ani光标文件,网上的源码真的很难找到!

7 楼

怎么提取ani中的图标数据呢,求教!

8 楼

我把提取ani中的图标做成了一个类,代码如下:

Option Explicit

Public fileSize As Long     '文件大小
Public frameCount As Long   '总帧数
Public aniWidth As Long     '图像宽
Public aniHeight As Long    '图像高
Public BitCount As Integer  '颜色位数:1=单色;4=16色;8=256色;24=24位真彩;32=32位真彩
Public aniJif As Long       '速率

Dim aniName As String
Dim bytData() As Byte       '文件数据
Dim ico() As Byte           '图标数据
Dim pNum As Integer         '图像输出计数
Dim iPos As Long            'ico数据位置

Public Function Picture(num As Integer) As IPicture '图像输出:参数-图像序号
pNum = num
getACONicon
Set Picture = getPictureFromByteStream(ico())
End Function

Public Sub OutData(num As Integer, iName As String) '数据输出:参数-图像序号
pNum = num
getACONicon
Open iName For Binary As #1
Put #1, , ico
Close #1
End Sub

Public Sub LoadANI(iName As String) '装入ANI
Dim dat() As Byte, i As Long, j As Long
aniName = iName: pNum = 0

fileSize = FileLen(aniName)
ReDim bytData(fileSize - 1)
Open aniName For Binary As #1
Get #1, , bytData
Close #1

dat = StrConv("anih", vbFromUnicode)
i = InStrB(bytData, dat) + 11: If i = 11 Then Exit Sub
frameCount = bytData(i)     '图象帧数
aniJif = bytData(i + 24) * 1000 / 60

dat = StrConv("icon", vbFromUnicode)
i = InStrB(bytData, dat): If i = 0 Then Exit Sub
aniWidth = bytData(i + 13): aniHeight = bytData(i + 14) '图像尺寸

Exit Sub
ReadErr:
Close
End Sub

Private Sub getACONicon()
Dim i As Long, k As Long, dat() As Byte
dat = StrConv("icon", vbFromUnicode)
iPos = 1
For i = 0 To pNum: iPos = InStrB(iPos, bytData, dat) + 7: Next
BitCount = bytData(iPos + 36)
If BitCount = 32 Then
  transform32
Else
  transform
End If
ico(2) = 1 '有的图像“资源类型”项=2,这样在图片框显示时为单色
End Sub

Private Sub transform()
Dim i As Long
ReDim ico(bytData(iPos - 4) + bytData(iPos - 3) * 256 - 1)
iPos = iPos - 1
For i = 0 To UBound(ico): iPos = iPos + 1: ico(i) = bytData(iPos): Next
End Sub

Private Sub transform32()
On Error GoTo 100
Dim aSize As Long 'AND位图长度
Dim iSize As Long '图像数据块长度
Dim i As Long, j As Long, k As Long, w As Integer, h As Integer
Dim n As Long, n1 As Long, n2 As Long, n3 As Long

iSize = bytData(iPos + 16) * 65536 + bytData(iPos + 15) * 256 + bytData(iPos + 14) '获取源图像数据块长度
w = bytData(iPos + 6): h = bytData(iPos + 7)   '获取图像宽高
k = w * h                                      '计算应去掉的XOR位图长度
aSize = h * 4 * (w \ 32 + Abs(w Mod 32 > 0))   '计算AND位图长度

If w Mod 4 = 0 Then '如果是标准扫描线
  ReDim ico(iSize + 22 - k - 1)
  For i = 0 To 61: ico(i) = bytData(iPos + i): Next '复制ico文件头、图像信息块、BMP信息头
  k = i: j = i
  For i = 1 To iSize - aSize - 40 '复制XOR位图数据
    If i Mod 4 <> 0 Then ico(j) = bytData(iPos + k): j = j + 1
    k = k + 1
  Next
  j = j - 1
  For i = k To k + aSize - 1: j = j + 1: ico(j) = bytData(iPos + i): Next '复制AND位图数据
  iSize = h * w * 3 + aSize + 40 '计算新图像数据块长度
Else
  n1 = 4 * (w * 3 \ 4 + Abs((w * 3 Mod 4) > 0)) '计算标准24位真彩扫描线长度
  n2 = w * 3        '当前扫描线长度
  n3 = Abs(n1 - n2) '每条扫描线要增加的空白字节
  ReDim ico(iSize + 22 - k - 1 + n3 * h)
  For i = 0 To 61: ico(i) = bytData(iPos + i): Next '复制ico文件头
  k = i: j = i: n = 0
  For i = 1 To iSize - aSize - 40 '复制XOR位图数据
    If i Mod 4 <> 0 Then ico(j) = bytData(iPos + k): n = n + 1: j = j + IIf(n Mod n2 = 0, n3 + 1, 1)
    k = k + 1
  Next
  j = j - 1
  For i = k To k + aSize - 1: j = j + 1: ico(j) = bytData(iPos + i): Next '复制AND位图数据
  iSize = h * n1 + aSize + 40
End If

ico(16) = iSize \ 65536: ico(15) = (iSize Mod 65536) \ 256: ico(14) = iSize Mod 65536 Mod 256 '设置新图像信息块中的图像数据块长度
iSize = iSize - 40
ico(44) = iSize \ 65536: ico(43) = (iSize Mod 65536) \ 256: ico(42) = iSize Mod 65536 Mod 256 '设置新图像BMP信息头中的位图点阵长度
ico(36) = 24: BitCount = 24 '设置为24位真彩
100
End Sub

9 楼

ani是什么文件啊?

10 楼

一江秋水 您好·

我想写一个ani动画编辑器,写一个能支持我现有的ani动画的格式,动画开头跟你发布的有些不同,麻烦你帮我看一下·能写的话重谢!我的QQ:244414498

00000000h: 52 49 00 00 14 00 00 00 0B 00 64 00 40 01 00 00 ; RI........d.@...
00000010h: F0 00 00 00 10 00 00 00 24 3A 01 00 78 9C EC BD ; ?......$:..x滌?
00000020h: 5F 70 23 F7 75 26 BA 45 80 23 DA 89 6D CE 46 F2 ; _p#鱱&篍€#趬m蜦?
00000030h: 92 F7 89 20 40 8A 2D C7 D6 B4 56 96 0D BE E1 0F ; 掲?@?侵碫?踞.
00000040h: 29 35 E4 AC 05 78 2D 17 A0 27 74 83 33 02 E8 E4 ; )5洮.x-.?t?.桎
00000050h: 9A 60 32 4A 40 3F A1 C1 91 44 8E BD EB E1 AC A5 ; 歚2J@?×慏幗脶
00000060h: 2D CE 13 BA 31 92 63 CA A9 D8 9C 6B A9 4A CC 03 ; -??抍施販k㎎?
00000070h: 1B A0 65 AF 87 DE B5 6C 96 75 1F 74 AB D8 E0 48 ; .爀瘒薜l杣.t郒
00000080h: 76 62 D9 89 9C 54 F9 3E DD F3 9D F3 6B FC 21 47 ; vb賶淭?蒹濗k?G
00000090h: D9 E7 5B 95 EA E2 3F 10 04 01 7C 7D CE F9 CE 77 ; 夔[曣?...|}矽蝫
000000a0h: FE 74 7D BC 4E 47 73 DC E1 AF F5 71 13 1F 63 7C ; }糔Gs茚q..c|
000000b0h: F0 ED CD 71 6F 62 9F 8F E3 89 26 FF 5C EF 1F 63 ; 痦蛁ob煆銐&\?c

我分析的是

00000000h: 52 49 00 00 14 00 00 00 0B 00 64 00 40 01 00 00 ; RI........d.@...
00000010h: F0 00 00 00   0B为文件11张图片组成的意思,4001为像素320,F000为像素240

这个ani动画由11张图片组成的图片像素240*320

像这种ani文件格式网上没找到能打的开的。

 

我来回复

您尚未登录,请登录后再回复。点此登录或注册