2010年11月13日

ANIファイルの書き込み



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

  1. マルチメディアファイル入出力関数を使って、ANIファイルを開きます。
  2. ANIHEADER構造体が正しくない場合、新たに作成し直します。
  3. RIFF/ACONチャンクを作成して、その中に進入します。
  4. ファイル名か著者名がある場合は、INFOチャンクを作成してその中に進入します。
  5. ファイル名がある場合は、INAMチャンクを作成してその中に進入し、ファイル名を書き込んだ後、INAMチャンクから退出します。
  6. 著者名がある場合は、IARTチャンクを作成してその中に進入し、著者名を書き込んだ後、IARTチャンクから退出します。
  7. INFOチャンクから退出します。
  8. anihチャンクを作成してその中に進入し、ANIHEADER構造体を書き込んだ後、anihチャンクから退出します。
  9. 表示時間配列がある場合は、rateチャンクを作成してその中に進入し、表示時間配列を書き込んだ後、rateチャンクから退出します。
  10. ヘッダーフラグがANI_FLAG_SEQUENCEの場合、seqチャンクを作成してその中に進入し、シーケンスデータを書き込んだ後、seqチャンクから退出します。
  11. framチャンクを作成して、その中に進入します。
  12. iconチャンクを作成してその中に進入し、リストに格納されたCGroupIconクラスiconチャンクに書き込んだ後、iconチャンクから退出します。この作業をフレームの数だけ繰り返します。
  13. framチャンクから退出します。
  14. 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などのファイルの読み/書きや、他のファイルフォーマットへの変更等が出来ますので、お手持ちの画像で動作を確認してみて下さい。



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

メールアドレス:

ホームページアドレス:

コメント:

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


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

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