2010年10月16日

ANIファイルの読み込み



ANIファイルはWindowsのマルチメディアアプリケーションを収めたRIFF形式Resource Interchange File Format)という形式をとっております。
ANIファイルの読み込み手順は以下の通りです。

  1. マルチメディアファイル入出力関数を使って、ANIファイルを開きます。
  2. RIFFチャンクに進入して、ACONチャンクを探します。
  3. LISTチャンクに進入して、INFOチャンクをを探します。
  4. INFOチャンクに進入して、INAMチャンクIARTチャンクを読み込みます。
  5. INFOチャンクから退出して、anihチャンクrateチャンクを読み込みます。
  6. ヘッダーフラグがANI_FLAG_SEQUENCEの場合、seqチャンクを読み込みます。
  7. LISTチャンクに進入して、framチャンクを探します。
  8. フレームの数だけiconチャンクを読み込み、アイコンまたはカーソルを作成します。
  9. framチャンクから退出します。
  10. RIFFチャンクから退出します。



マルチメディアファイル入出力関数を使用するために必要なインクルードファイルとライブラリーです。

#include <Mmsystem.h>
#pragma comment (lib, "winmm.lib")


ANIファイルには複数のアイコン/カーソルがグループアイコン形式で格納されており、これらのアイコン/カーソルを読み込むために「アイコンファイルの読み込み」で解説したCGroupIconクラスStreamIn関数を使用するためのインクルード定義が必要です。

//グループアイコンクラスのインクルード
#include "GroupIcon.h"


このGroupIcon.hの中のアイコン/カーソルを読み込み関数では更に「PNGファイルの読み込み」で解説した_PNGStreamIn関数を呼び出しているため、「libpngのインストール」でダウンロードしたlibpngを使用するためのインクルード定義が必要です。

#include "png.h"
#include "PNGFile.h"
#include "DIBFile.h"


上記インクルード定義で必要となるファイルです。

GroupIcon.h GroupIcon.cpp
PNGFile.h PNGFile.cpp
DIBFile.h DIBFile.cpp
Rle.h Rle.cpp
MedianCut.h MedianCut.cpp
Dither.h Dither.cpp


ANIファイルで使用するANIHEADER構造体です。

//ANIHEADER構造体
typedef struct tagANIHEADER {
DWORD dwSize; //構造体のバイト数
DWORD dwFrames; //格納しているアイコン数
DWORD dwSteps; //表示するコマの数
DWORD dwWidth; //画像幅 (生データ時のみ有効)
DWORD dwHeight; //画像高さ (生データ時のみ有効)
DWORD dwBpp; //ビット深度 (生データ時のみ有効)
DWORD dwPlanes; //プレーン数 (生データ時のみ有効)
DWORD dwDefaultRate;//各コマの表示時間のデフォルト値
DWORD dwFlags; //ビット31からビット2まで予約済み(常に0)
//ビット1 TRUE:ファイルはシーケンスデータを含む
//ビット0 TRUE:フレームはアイコン若しくはカーソルデータ
// FALSE:フレームは生データ(詳細は不明。)

} ANIHEADER,*LPANIHEADER;


ANIHEADER構造体dwFlags要素で使用する定数です。

//iconチャンクデータ形式
#define ANI_FLAG_ICON 0x1 //データはアイコン形式である
//seqチャンクの有無

#define ANI_FLAG_SEQUENCE 0x2 //seqチャンクがある


※ANIファイルには複数のアイコン/カーソルがグループアイコン形式で格納されているため、「アイコンファイルの読み込み」で定義したCGroupIconクラスをまとめて統括するCANIFileクラスを独自に定義しました。

//CANIFileクラス

class CANIFile : public CObject
{
public:
CANIFile(){Init();};
virtual ~CANIFile(){Delete();};

void Init(); //メンバー変数の初期化
void Delete(); //メンバー変数の削除

//ANIファイルの読み込み
virtual BOOL LoadFile(LPCTSTR pszFileName,BOOL bDeleteDIB=TRUE);

//リソースを格納しているファイル(DLL、EXEファイル)を開いて
//リソースハンドルを取得し、リソースを読み込みます。

BOOL LoadResource(LPCTSTR lpszExeName,LPCTSTR lpName,LPCTSTR lpType);
//リソースからの読み込み
BOOL LoadResource(HMODULE hModule,LPCTSTR lpName,LPCTSTR lpType);
//メモリーからの読み込み
BOOL LoadFromMem(HPSTR pchBuffer,LONG memSize);

//ANIストリームからの読み込み
BOOL StreamIn(HMMIO hmmio,BOOL bDeleteDIB);

//指定コマの表示時間の取得
LONG GetRate(UINT nStep);
//指定コマのアイコン番号の取得
LONG GetSeq(UINT nStep);
//指定コマの表示時間の設定
void SetRate(UINT nStep,LONG nRate);
//指定コマのアイコン番号の設定
void SetSeq(UINT nStep,LONG nFrame);
//指定コマのCGroupIconクラスの取得
CGroupIcon* GetGrpIcon(UINT nStep);
//コマ数の取得
DWORD GetCount();

ANIHEADER m_AniHeader; //ヘッダー
LONG* m_pRate; //表示時間配列
LONG* m_pSeq; //シーケンスデータ
CString m_strFileName; //ファイル名
CString m_strAuthorName;//著者名
CPtrArray m_listGrpIcon; //CGroupIconクラスのリスト
LPCTSTR m_lpType; //リソースタイプ(RT_ANICURSOR/RT_ANIICON)
};


CANIFileクラスのインライン関数です。


//指定コマの表示時間の取得
inline LONG CANIFile::GetRate(UINT nStep)
{
ASSERT(nStep<m_AniHeader.dwSteps);
if (!m_pRate) return m_AniHeader.dwDefaultRate;
return m_pRate[nStep];
}

//指定コマのアイコン番号の取得
inline LONG CANIFile::GetSeq(UINT nStep)
{
ASSERT(nStep<m_AniHeader.dwSteps);
if (!m_pSeq) return nStep;
return m_pSeq[nStep];
}

//指定コマの表示時間の設定
inline void CANIFile::SetRate(UINT nStep,LONG nRate)
{
ASSERT((m_AniHeader.dwSize==sizeof(ANIHEADER))&&(nStep<m_AniHeader.dwSteps));
if (!m_pRate) {
m_pRate=new LONG[m_AniHeader.dwSteps];
for(UINT i=0;i<m_AniHeader.dwSteps;i++)
m_pRate[i]=m_AniHeader.dwDefaultRate;
}
m_pRate[nStep]=nRate;
}

//指定コマのアイコン番号の設定
inline void CANIFile::SetSeq(UINT nStep,LONG nFrame)
{
ASSERT((m_AniHeader.dwSize==sizeof(ANIHEADER))&&
(nStep<m_AniHeader.dwSteps)&&((UINT)nFrame<m_AniHeader.dwFrames));
if (!m_pSeq) {
m_AniHeader.dwFlags|=ANI_FLAG_SEQUENCE;
m_pSeq=new LONG[m_AniHeader.dwSteps];
for(UINT i=0;i<m_AniHeader.dwSteps;i++)
m_pSeq[i]=i;
}
m_pSeq[nStep]=nFrame;
}

//指定コマのCGroupIconクラスの取得
inline CGroupIcon* CANIFile::GetGrpIcon(UINT nStep)
{
int index=GetSeq(nStep);
return (CGroupIcon*)m_listGrpIcon.GetAt(index);
}

//コマ数の取得
inline DWORD CANIFile::GetCount()
{
if (m_AniHeader.dwSize!=sizeof(ANIHEADER)) return FALSE;
return m_AniHeader.dwSteps;
}


CANIFileクラスのメンバー変数です。

//メンバー変数の初期化
void CANIFile::Init()
{
::ZeroMemory(&m_AniHeader,sizeof(ANIHEADER));
m_pRate=m_pSeq=0;
m_strFileName.Empty();
m_strAuthorName.Empty();
m_listGrpIcon.RemoveAll();
m_lpType=RT_ANICURSOR;
}

//メンバー変数の削除
void CANIFile::Delete()
{
if (m_pRate) delete[]m_pRate;
if (m_pSeq) delete[]m_pSeq;

for(int i=0;i<m_listGrpIcon.GetCount();i++)
delete ((CGroupIcon*)m_listGrpIcon.GetAt(i));
Init();
}


まずは指定されたパス名のファイルをmmioOpen関数で開いて、CANIFileクラスStreamIn関数を呼び出します。

//ANIファイルの読み込み
BOOL CANIFile::LoadFile(LPCTSTR pszFileName,BOOL bDeleteDIB)
{
//マルチメディアファイル入出力関数でファイルを開く。
HMMIO hmmio=::mmioOpen((LPTSTR)pszFileName,NULL,MMIO_READ);
if (!hmmio) return FALSE;
Delete();
//ANIストリームからの読み込み
BOOL bResult=StreamIn(hmmio,bDeleteDIB);
::mmioClose(hmmio,0);
return bResult;
}


ANIストリームを読み込んでCGroupIconクラスを作成し、リストに格納します。

//ANIストリームからの読み込み
BOOL CANIFile::StreamIn(HMMIO hmmio,BOOL bDeleteDIB)
{
ASSERT(hmmio);

MMCKINFO mmckRiff,mmckInfo,mmckInam,mmckIart;
MMCKINFO mmckAnih,mmckRate,mmckSeq,mmckIcon,mmckFram;

MMRESULT mmr;
LONG mmioPos;
LONG size;

//RIFFチャンクに進入してACONチャンクを探します。
mmckRiff.fccType=::mmioStringToFOURCC(_T("ACON"), 0);
mmr=::mmioDescend(hmmio,&mmckRiff,NULL,MMIO_FINDRIFF);
//ACONチャンクがなければFALSEを返して終了する。
if (mmr!=MMSYSERR_NOERROR) return FALSE;

//現在のファイルポインタの位置を保存する。
mmioPos=::mmioSeek(hmmio,0,SEEK_CUR);

//LISTチャンクに進入してINFOチャンクをを探します。
mmckInfo.fccType=::mmioStringToFOURCC(_T("INFO"), 0);
mmr=::mmioDescend(hmmio,&mmckInfo,NULL,MMIO_FINDLIST);
//INFOチャンクがあった場合。
if (mmr == MMSYSERR_NOERROR){
//現在のファイルポインタの位置を保存する。
mmioPos=::mmioSeek(hmmio,0,SEEK_CUR);
//INAMチャンクを探す。
mmckInam.ckid=::mmioStringToFOURCC(_T("INAM"),0);
mmr=::mmioDescend(hmmio,&mmckInam,NULL,MMIO_FINDCHUNK);
//INAMチャンクがあった場合。
if (mmr==MMSYSERR_NOERROR) {
//ファイル名を読み込む
LPTSTR lpsz=m_strFileName.GetBufferSetLength(mmckInam.cksize+sizeof(TCHAR));
::ZeroMemory(lpsz,mmckInam.cksize+sizeof(TCHAR));
size=::mmioRead(hmmio,(HPSTR)lpsz,mmckInam.cksize);
if (size!=mmckInam.cksize) return FALSE;
//INAMチャンクから退出する。
if (::mmioAscend(hmmio,&mmckInam,0)!=MMSYSERR_NOERROR) return FALSE;
}
//INFOチャンクの終端に到達したので、元に戻す。
else ::mmioSeek(hmmio,mmioPos,SEEK_SET);

//IARTチャンクを探す。
mmckIart.ckid=::mmioStringToFOURCC(_T("IART"),0);
mmr=::mmioDescend(hmmio,&mmckIart,NULL,MMIO_FINDCHUNK);
//IARTチャンクがあった場合。
if (mmr==MMSYSERR_NOERROR) {
//著者名を読み込む。
LPTSTR lpsz=m_strAuthorName.GetBufferSetLength(mmckIart.cksize+sizeof(TCHAR));
::ZeroMemory(lpsz,mmckIart.cksize+sizeof(TCHAR));
size=::mmioRead(hmmio,(HPSTR)lpsz,mmckIart.cksize);
if (size!=mmckIart.cksize) return FALSE;
//IARTチャンクから退出する。
if (::mmioAscend(hmmio,&mmckIart,0)!=MMSYSERR_NOERROR) return FALSE;
}
//INFOチャンクの終端に到達したので、INFOチャンクを退出する。
else if (::mmioAscend(hmmio,&mmckInfo,0)!=MMSYSERR_NOERROR) return FALSE;
}
//LISTチャンクの終端に到達したので、元に戻す。
else ::mmioSeek(hmmio,mmioPos,SEEK_SET);

//anihチャンクを探す。
mmckAnih.ckid=::mmioStringToFOURCC(_T("anih"), 0);
mmr=::mmioDescend(hmmio,&mmckAnih,NULL,MMIO_FINDCHUNK);
//anihチャンクがなければFALSEを返して終了する。
if (mmr!=MMSYSERR_NOERROR) return FALSE;
//ANIHEADER構造体を読み込む。
size=::mmioRead(hmmio,(HPSTR)&m_AniHeader,mmckAnih.cksize);
if (size!=mmckAnih.cksize) return FALSE;
//anihチャンクから退出する。
if (::mmioAscend(hmmio,&mmckAnih,0)!=MMSYSERR_NOERROR) return FALSE;
//iconチャンクがアイコンデータではない場合は、FALSEを返して終了する。
if (!(m_AniHeader.dwFlags&ANI_FLAG_ICON)) return FALSE;
//フレームがなければ、FALSEを返して終了する。
if (!m_AniHeader.dwFrames) return FALSE;

//現在のファイルポインタの位置を保存する。
mmioPos=::mmioSeek(hmmio,0,SEEK_CUR);
//rateチャンクを探す。
mmckRate.ckid=::mmioStringToFOURCC(_T("rate"), 0);
mmr=::mmioDescend(hmmio,&mmckRate,NULL,MMIO_FINDCHUNK);
if (mmr==MMSYSERR_NOERROR) {
//rateチャンクを読み込む。
mmioPos=::mmioSeek(hmmio,0,SEEK_CUR);
m_pRate=new LONG[m_AniHeader.dwSteps];
for(UINT i=0;i<m_AniHeader.dwSteps;i++) m_pRate[i]=m_AniHeader.dwDefaultRate;
size=::mmioRead(hmmio,(HPSTR)m_pRate,mmckRate.cksize);
if (size!=mmckRate.cksize) return FALSE;
//rateチャンクから退出する
if (::mmioAscend(hmmio,&mmckRate,0)!=MMSYSERR_NOERROR) return FALSE;
}
//aconチャンクの終端に到達したので、元に戻す。
else ::mmioSeek(hmmio,mmioPos,SEEK_SET);

//ヘッダーフラグがANI_FLAG_SEQUENCEの場合。
if (m_AniHeader.dwFlags&ANI_FLAG_SEQUENCE){
//seqチャンクを探す。
mmckSeq.ckid=::mmioStringToFOURCC(_T("seq "),0);
mmr=::mmioDescend(hmmio,&mmckSeq,NULL,MMIO_FINDCHUNK);
if (mmr==MMSYSERR_NOERROR) {
//seqチャンクを読み込む。
mmioPos=::mmioSeek(hmmio,0,SEEK_CUR);
m_pSeq=new LONG[m_AniHeader.dwSteps];
for(UINT i=0;i<m_AniHeader.dwSteps;i++) m_pSeq[i]=i;
size=::mmioRead(hmmio,(HPSTR)m_pSeq,mmckSeq.cksize);
if (size!=mmckSeq.cksize) return FALSE;
//seqチャンクから退出する。
if (::mmioAscend(hmmio,&mmckSeq,0)!=MMSYSERR_NOERROR) return FALSE;
}
//seqチャンクがなければFALSEを返して終了する。
else return FALSE;
}

//LISTチャンクに進入して、framチャンクを探す。
mmckFram.fccType=::mmioStringToFOURCC(_T("fram"), 0);
mmr=::mmioDescend(hmmio,&mmckFram,NULL,MMIO_FINDLIST);
//framチャンクがなければ、FALSEを返して終了する。
if (mmr!=MMSYSERR_NOERROR) return FALSE;

//Iconの読み込む。
for(UINT i=0;i<m_AniHeader.dwFrames;i++){
//iconチャンクを探す。
mmckIcon.ckid=::mmioStringToFOURCC(TEXT("icon"),0);
mmr=::mmioDescend(hmmio,&mmckIcon,NULL,MMIO_FINDCHUNK);
if (mmr==MMSYSERR_NOERROR) {
//iconチャンクを読み込む。
PBYTE pData=new BYTE[mmckIcon.cksize];
size=::mmioRead(hmmio,(HPSTR)pData,mmckIcon.cksize);
//読み込みに失敗したか、iconチャンクからの退出に失敗したらFALSEを返して終了する。
if ((size!=mmckIcon.cksize)||(::mmioAscend(hmmio,&mmckIcon,0)!=MMSYSERR_NOERROR)){
delete[]pData;
return FALSE;
}
//CGroupIconクラスを作成する。
CGroupIcon* pGrpIcon=new CGroupIcon;
//iconチャンクからCGroupIconクラスを作成する。
CMemFile infile(pData,mmckIcon.cksize);
BOOL bResult=pGrpIcon->StreamIn(&infile,bDeleteDIB);
infile.Close();
//iconチャンクバッファの削除
delete[]pData;
if (!bResult){
delete pGrpIcon;
return FALSE;
}
//グループアイコンをリストに追加する。
m_listGrpIcon.Add(pGrpIcon);
//一コマ目がRT_GROUP_ICONならリソース形式をRT_ANIICON、それ以外はRT_ANICURSORとする。
if (i==0) m_lpType=(pGrpIcon->GetType()==RT_GROUP_ICON)? RT_ANIICON:RT_ANICURSOR;

}
else {//framチャンクの終端に到達した。
break;
}
}
//framチャンクから退出する。
if (::mmioAscend(hmmio,&mmckFram,0)!=MMSYSERR_NOERROR) return FALSE;
//riffチャンクから退出する。
//リソースからの読み込んだ場合、シークエラーになることがあるので
//敢えてエラーチェックを外しました。

::mmioAscend(hmmio,&mmckRiff,0);
}


サンプルプログラム(VisualC++net2003ソリューション)ANIFileTest01B.zip

上記のzipファイルをダウンロードしてからWinRAR等で解凍し、ANIFileTest01Bフォルダー内にある「ANIFileTest.sln」を開きます。「F5」キーを押すと、ビルド確認のダイヤログが表示されるので「Yes」を選択してソリューションをビルドします。




ビルドが終わると、直ちにプログラムが自動起動してウィンドウが表示されます。メニューから「ファイル」/「開く」を選択して、「ファイルを開く」ダイヤログを表示させます。



resフォルダー内の「aero_working_xl.ani」を選択し、「開く」ボタンを押して読み込みます。画像が表示されたところで、コマの選択ダイヤログとアイコンの選択コンボで複数のグループアイコンとサイズの違うカーソルが2種類読み込まれているか確認してください。



上記と同じ要領で「res」フォルダー内にある後二つのANIファイルについても、内容が正しく読み込めるか確かめてみてください。




posted by ひろし at 17:48| Comment(0) | ANIファイルフォーマット | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。