ANIファイルはWindowsのマルチメディアアプリケーションを収めたRIFF形式(Resource Interchange File Format)という形式をとっております。
ANIファイルの書き込み手順は以下の通りです。
- マルチメディアファイル入出力関数を使って、ANIファイルを開きます。
- ANIHEADER構造体が正しくない場合、新たに作成し直します。
- RIFF/ACONチャンクを作成して、その中に進入します。
- ファイル名か著者名がある場合は、INFOチャンクを作成してその中に進入します。
- ファイル名がある場合は、INAMチャンクを作成してその中に進入し、ファイル名を書き込んだ後、INAMチャンクから退出します。
- 著者名がある場合は、IARTチャンクを作成してその中に進入し、著者名を書き込んだ後、IARTチャンクから退出します。
- INFOチャンクから退出します。
- anihチャンクを作成してその中に進入し、ANIHEADER構造体を書き込んだ後、anihチャンクから退出します。
- 表示時間配列がある場合は、rateチャンクを作成してその中に進入し、表示時間配列を書き込んだ後、rateチャンクから退出します。
- ヘッダーフラグがANI_FLAG_SEQUENCEの場合、seqチャンクを作成してその中に進入し、シーケンスデータを書き込んだ後、seqチャンクから退出します。
- framチャンクを作成して、その中に進入します。
- iconチャンクを作成してその中に進入し、リストに格納されたCGroupIconクラスをiconチャンクに書き込んだ後、iconチャンクから退出します。この作業をフレームの数だけ繰り返します。
- framチャンクから退出します。
- RIFFチャンクから退出します。
マルチメディアファイル入出力関数を使用するために必要なインクルードファイルとライブラリーです。
#include <Mmsystem.h>
#pragma comment (lib, "winmm.lib")
グループアイコン形式のアイコン/カーソルをANIファイルに書き込むために、「アイコンファイルの書き込み」で解説したCGroupIconクラスのStreamOut関数を使用するためのインクルード定義が必要です。
//グループアイコンクラスのインクルード
#include "GroupIcon.h"
このGroupIcon.hの中のアイコン/カーソルの書き込み関数では更に「PNGファイルの書き込み」で解説した_PNGStreamOut関数を呼び出しているため、「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
まずは指定されたパス名のファイルをマルチメディアファイル入出力関数で開いて、StreamOut関数を呼び出します。
//ANIファイルの書き込み
//bDeleteDIB TRUE:ファイル書き込みに使用したDIBを削除する。
//iPNGMinimumSize 0:PNG圧縮しない。
// 1~256:アイコンサイズが指定されたサイズ以上ならPNG圧縮する。
// それ以外:CIcon::m_bCompressedPNGに従って処理する。
BOOL CANIFile::SaveFile(LPCTSTR pszFileName,BOOL bDeleteDIB,int iPNGMinimumSize)
{
//マルチメディアファイル入出力関数
HMMIO hmmio=::mmioOpen((LPTSTR)pszFileName,NULL,MMIO_CREATE|MMIO_WRITE);
if (!hmmio) return FALSE;
//ANIストリームへの書き込み
BOOL bResult=StreamOut(hmmio,bDeleteDIB,iPNGMinimumSize);
::mmioClose(hmmio,0);
return bResult;
}
リストに格納されたCGroupIconクラスをANIストリームに書き出します。
//ANIストリームへの書き込み
BOOL CANIFile::StreamOut(HMMIO hmmio,BOOL bDeleteDIB,int iPNGMinimumSize)
{
ASSERT(hmmio);
//ANIHEADER構造体が正しくない場合は、新たに作成します。
if (m_AniHeader.dwSize!=sizeof(ANIHEADER)){
::ZeroMemory(&m_AniHeader,sizeof(ANIHEADER));
m_AniHeader.dwSize =sizeof(ANIHEADER);
AfxIsValidAddress(m_pSeq,sizeof(LONG));
m_AniHeader.dwFrames=(DWORD)m_listGrpIcon.GetCount();
m_AniHeader.dwSteps =m_AniHeader.dwFrames;
CIcon* pIcon=GetActiveIcon();
m_AniHeader.dwWidth =pIcon->Width();
m_AniHeader.dwHeight=pIcon->Height();
m_AniHeader.dwBpp =pIcon->BitCount();
m_AniHeader.dwPlanes=1;
m_AniHeader.dwDefaultRate=(m_pRate)? m_pRate[0]:60;
}
//無条件にアイコンデータにします。
m_AniHeader.dwFlags|=ANI_FLAG_ICON;
MMCKINFO mmckRiff,mmckInfo,mmckInam,mmckIart;
MMCKINFO mmckAnih,mmckRate,mmckSeq,mmckFram,mmckIcon;
MMRESULT mmr;
//RIFF/ACONチャンクを作成して、その中に進入します。
mmckRiff.fccType=::mmioStringToFOURCC(_T("ACON"), 0);
::mmioCreateChunk(hmmio,&mmckRiff,MMIO_CREATERIFF);
//ファイル名か著者名がある場合。
if ((!m_strFileName.IsEmpty())||(!m_strAuthorName.IsEmpty())){
//INFOチャンクを作成して、その中に進入します。
mmckInfo.fccType=::mmioStringToFOURCC(_T("INFO"), 0);
::mmioCreateChunk(hmmio,&mmckInfo,MMIO_CREATELIST);
//ファイル名がある場合。
if (!m_strFileName.IsEmpty()){
//INAMチャンクを作成して、その中に進入します。
mmckInam.ckid=::mmioStringToFOURCC(_T("INAM"), 0);
::mmioCreateChunk(hmmio,&mmckInam,0);
//ファイル名を書き込みます。
::mmioWrite(hmmio,(const char*)m_strFileName.GetBuffer(),
(m_strFileName.GetLength()+1)*sizeof(TCHAR));
//INAMチャンクから退出します。
::mmioAscend(hmmio,&mmckInam,0);
}
//著者名がある場合。
if (!m_strAuthorName.IsEmpty()){
//IARTチャンクを作成して、その中に進入します。
mmckIart.ckid=::mmioStringToFOURCC(_T("IART"),0);
::mmioCreateChunk(hmmio,&mmckIart,0);
//著者名を書き込みます。
::mmioWrite(hmmio,(const char*)m_strAuthorName.GetBuffer(),
(m_strAuthorName.GetLength()+1)*sizeof(TCHAR));
//IARTチャンクから退出します。
::mmioAscend(hmmio,&mmckIart,0);
}
//INFOチャンクから退出します。
::mmioAscend(hmmio,&mmckInfo,0);
}
//anihチャンクを作成して、その中に進入します。
mmckAnih.ckid=::mmioStringToFOURCC(_T("anih"),0);
::mmioCreateChunk(hmmio,&mmckAnih,0);
//ANIHEADER構造体を書き込みます。
::mmioWrite(hmmio,(const char*)&m_AniHeader,sizeof(ANIHEADER));
//anihチャンクから退出します。
::mmioAscend(hmmio,&mmckAnih,0);
//表示時間配列m_pRateがある場合。
if (m_pRate){
//rateチャンクを作成して、その中に進入します。
mmckRate.ckid=::mmioStringToFOURCC(_T("rate"),0);
mmr=::mmioCreateChunk(hmmio,&mmckRate,0);
//表示時間配列を書き込みます。
::mmioWrite(hmmio,(const char*)m_pRate,
sizeof(LONG)*m_AniHeader.dwSteps);
//rateチャンクから退出します。
::mmioAscend(hmmio,&mmckRate,0);
}
//ヘッダーフラグがANI_FLAG_SEQUENCEの場合。
if (m_AniHeader.dwFlags&ANI_FLAG_SEQUENCE){
//seqチャンクを作成して、その中に進入します。
mmckSeq.ckid=::mmioStringToFOURCC(_T("seq "),0);
mmr=::mmioCreateChunk(hmmio,&mmckSeq,0);
//シーケンスデータを書き込みます。
::mmioWrite(hmmio,(const char*)m_pSeq,
sizeof(LONG)*m_AniHeader.dwSteps);
//seqチャンクから退出します。
::mmioAscend(hmmio,&mmckSeq,0);
}
//framチャンクを作成して、その中に進入します。
mmckFram.fccType=::mmioStringToFOURCC(_T("fram"), 0);
mmr=::mmioCreateChunk(hmmio,&mmckFram,MMIO_CREATELIST);
for (UINT i=0;i<m_AniHeader.dwFrames;i++){
//iconチャンクを作成して、その中に進入します。
mmckIcon.ckid=::mmioStringToFOURCC(_T("icon"),0);
mmr=::mmioCreateChunk(hmmio,&mmckIcon,0);
//CGroupIconクラスを取り出す。
CGroupIcon* pGrpIcon=(CGroupIcon*)m_listGrpIcon.GetAt(i);
CMemFile outfile;
//リソースタイプの設定
LPCTSTR lpType=(m_lpType==RT_ANIICON)?RT_GROUP_ICON:RT_GROUP_CURSOR;
//グループアイコンを書き込みます。
pGrpIcon->StreamOut(&outfile,lpType,bDeleteDIB,iPNGMinimumSize);
//メモリーファイルからデータアレイを取り出します。
ULONGLONG size=outfile.GetLength();
PBYTE pData=outfile.Detach();
//iconチャンクに書き込みます。
::mmioWrite(hmmio,(const char*)pData,(LONG)size);
//iconチャンクから退出します。
::mmioAscend(hmmio,&mmckIcon,0);
//ファイル書き込みに使用したCMemFileクラスのバッファを削除します。
::free(pData);
}
//framチャンクから退出します。
::mmioAscend(hmmio,&mmckFram,0);
//riffチャンクから退出します。
::mmioAscend(hmmio,&mmckRiff,0);
return TRUE;
}
戻り値がTRUEなら、ANIファイルへの書き込みは成功です。
サンプルプログラム(VisualC++net2003ソリューション)ANIFileTest04.zip
上記のzipファイルをダウンロードしてからWinRAR等で解凍し、ANIFileTest04フォルダー内にある「ANIFileTest.sln」を開きます。「F5」キーを押すと、ビルド確認のダイヤログが表示されるので「Yes」を選択してソリューションをビルドします。
ビルドが終わると、直ちにプログラムが自動起動してウィンドウが表示されます。メニューから「ファイル」/「開く」を選択して、「ファイルを開く」ダイヤログを表示させ、resフォルダー内の「aero_working_xl.ani」を選択し、「開く」ボタンを押して読み込みます。
画像が表示されたところで、下段のダイヤログバーの空白のファイル名の欄に「aero_working_xl」と記入し、カレットのフォーカスを他のコントロールに移動し内容を確定します。
画像を右クリックしてポップアップメニューを表示させ、「名前を付けて保存」で「ファイル名を付けて保存」ダイヤログを表示させ、「aero_working_xl_Copy.ani」と名づけて「保存」ボタンを押します。
ツールバーの「新規」で画像を一旦を消しておいてから、先ほど作成した「aero_working_xl_Copy.ani」を読み込んで、元の画像と同じ画像が表示され、下段のダイヤログバーの空白のファイル名の欄が「aero_working_xl」となっているか確認します。
このサンプルプログラムではANIファイルの他にも、bmp/jpg/png/ico/curなどのファイルの読み/書きや、他のファイルフォーマットへの変更等が出来ますので、お手持ちの画像で動作を確認してみて下さい。
【関連する記事】