2010年12月05日

GIFファイルの読み込み 第3部



GIFファイルの読み込み 第3部」では、MFCを使った用例とサンプルプログラムを紹介します。



次にMFCにおいてこのCGIFFileクラスを使用して実際に画像を表示する方法を解説します。

まずはCGIFFileクラスを定義しているMFCドキュメントクラスのヘッダーファイルの一部分です。

// 生成された、メッセージ割り当て関数
protected:
DECLARE_MESSAGE_MAP()
public:
virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);
virtual void DeleteContents();

HBITMAP m_hbmBackground; //ビュー画面に表示する背景画像です。
BOOL m_bDisposeToViewBrush; //ビュー画面の背景色でディスポースする場合にTRUEになります。
COLORREF m_colorBackgroundView; //ビュー画面の背景色です。
CGIFFile* m_pGif; //CGIFFileクラスへのポインタです。

afx_msg void OnUpdateViewDisposetoview(CCmdUI *pCmdUI);
afx_msg void OnViewDisposetoview();
afx_msg void OnUpdateControlPlay(CCmdUI *pCmdUI);
afx_msg void OnControlPlay();
afx_msg void OnUpdateControlStop(CCmdUI *pCmdUI);
afx_msg void OnControlStop();
afx_msg void OnUpdateControlPause(CCmdUI *pCmdUI);
afx_msg void OnControlPause();
afx_msg void OnUpdateControlNext(CCmdUI *pCmdUI);
afx_msg void OnControlNext();
afx_msg void OnUpdateControlPrev(CCmdUI *pCmdUI);
afx_msg void OnControlPrev();
};


MFCドキュメントクラスのCPPファイル内のOnNewDocument関数、OnOpenDocument関数、DeleteContents関数、実際にボタンコントロールイベントを処理して画像の動作をコントロールしているイベント関数群です。

BOOL CGIFFileTestDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;

// TODO: この位置に再初期化処理を追加してください。
// (SDI ドキュメントはこのドキュメントを再利用します。)
//ビュークラスへのポインタを取得します。

POSITION pos=GetFirstViewPosition();
CView* pView=GetNextView(pos);
//CGIFFileクラスがまだないなら新たに作成します。
if ((pView)&&(!m_pGif)) m_pGif=new CGIFFile(pView);
//背景用の画像を読み込みます。
if (!m_hbmBackground) m_hbmBackground=_LoadGIF("res\\背景256.gif");
return TRUE;
}

BOOL CGIFFileTestDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
if (!CDocument::OnOpenDocument(lpszPathName))
return FALSE;

// TODO: Add your specialized creation code here
//ビュークラスへのポインタを取得します。

POSITION pos=GetFirstViewPosition();
CView* pView=GetNextView(pos);
//CGIFFileクラスがまだないなら新たに作成します。
if (!m_pGif) m_pGif=new CGIFFile(pView);

//GIFファイルを読み込みます。
//ブラウザの背景色にディスポースする場合は、
//第2引数をビュークラスの背景色にし、
//それ以外は-1を設定します。

BOOL bResult=m_pGif->LoadFile(lpszPathName,(m_bDisposeToViewBrush)?m_colorBackgroundView:-1);
//読み込みに成功したら、再生します。
if (bResult) m_pGif->Play();
return bResult;
}

void CGIFFileTestDoc::DeleteContents()
{
// TODO: Add your specialized code here and/or call the base class
if (m_pGif) m_pGif->Delete();

CDocument::DeleteContents();
}

//ブラウザの背景色にディスポースするか、
//背景画像をそのまま透過するか選択します。

void CGIFFileTestDoc::OnUpdateViewDisposetoview(CCmdUI *pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable();
int nCheck=(m_bDisposeToViewBrush)?BST_CHECKED:BST_UNCHECKED;
pCmdUI->SetCheck(nCheck);
}

void CGIFFileTestDoc::OnUpdateControlPlay(CCmdUI *pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable((m_pGif)&&(m_pGif->IsEnabled())&&(!m_pGif->IsActivate()));
}

//GIF動画を再生します。
void CGIFFileTestDoc::OnControlPlay()
{
// TODO: Add your command handler code here
if ((m_pGif)&&(m_pGif->IsEnabled())&&(!m_pGif->IsActivate()))
m_pGif->Play();
}

void CGIFFileTestDoc::OnUpdateControlStop(CCmdUI *pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable((m_pGif)&&(m_pGif->IsActivate()));
}

//GIF動画を停止します。
void CGIFFileTestDoc::OnControlStop()
{
// TODO: Add your command handler code here
if ((m_pGif)&&(m_pGif->IsActivate()))
m_pGif->Stop();
}

void CGIFFileTestDoc::OnUpdateControlPause(CCmdUI *pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable((m_pGif)&&((m_pGif->IsActivate())||(m_pGif->IsPaused()())));
}

//GIF動画を一時停止します。
void CGIFFileTestDoc::OnControlPause()
{
// TODO: Add your command handler code here
if ((m_pGif)&&((m_pGif->IsActivate())||(m_pGif->IsPaused()())))
m_pGif->Pause();
}

void CGIFFileTestDoc::OnUpdateControlNext(CCmdUI *pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable((m_pGif)&&(m_pGif->IsEnabled())&&(!m_pGif->IsActivate()));
}

//GIF動画をコマ送りします。
void CGIFFileTestDoc::OnControlNext()
{
// TODO: Add your command handler code here
if ((m_pGif)&&(m_pGif->IsEnabled())&&(!m_pGif->IsActivate()))
m_pGif->Next();
}

void CGIFFileTestDoc::OnUpdateControlPrev(CCmdUI *pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable((m_pGif)&&(m_pGif->IsEnabled())&&(!m_pGif->IsActivate()));
}

//GIF動画をコマ戻しします。
void CGIFFileTestDoc::OnControlPrev()
{
// TODO: Add your command handler code here
if ((m_pGif)&&(m_pGif->IsEnabled())&&(!m_pGif->IsActivate()))
m_pGif->Prev();
}

MFCビュークラス(CGIFFileTestView)に追加するメンバー変数です。(GIFFileTestView.h)

// 生成された、メッセージ割り当て関数
protected:
DECLARE_MESSAGE_MAP()
public:
CRect m_rectDraw; //画像を表示する領域
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
};


画像を実際に表示するCGIFFileTestViewクラスのコンストラクタOnDraw関数と画像表示を準備するOnInitialUpdate関数です。

// CGIFFileTestView コンストラクション/デストラクション

CGIFFileTestView::CGIFFileTestView()
{
// TODO: 構築コードをここに追加します。
m_rectDraw.SetRect(100,100,200,200);
}

// CGIFFileTestView 描画

void CGIFFileTestView::OnDraw(CDC* pDC)
{
CGIFFileTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;

// TODO: この場所にネイティブ データ用の描画コードを追加します。
//ブラウザの背景色にディスポースする場合。

if (pDoc->m_bDisposeToViewBrush){
//CGIFFileクラスへのポインタを取得します。
CGIFFile* pGif=pDoc->m_pGif;
//GIF画像があれば表示します。
if ((pGif)&&(pGif->IsEnabled()))
pGif->OnDraw(pDC,m_rectDraw.left,m_rectDraw.top,m_rectDraw.Width(),m_rectDraw.Height());
}

//背景画像にディスポースする場合。
else{
//裏メモリーDCを作成します。
CDC MemDC;
MemDC.CreateCompatibleDC(pDC);
HGDIOBJ hGdiObj;
UINT width,height;
HBITMAP hBitmap;
//CGIFFileクラスへのポインタを取得します。
CGIFFile* pGif=pDoc->m_pGif;
//用意した背景画像があれば、コピーを作成して裏DCに設定します。
BITMAP bm;
if (::GetObject(pDoc->m_hbmBackground,sizeof(BITMAP),&bm)){
width=bm.bmWidth;
height=bm.bmHeight;
hBitmap=(HBITMAP)::CopyImage(pDoc->m_hbmBackground,IMAGE_BITMAP,0,0,0);
hGdiObj=MemDC.SelectObject(hBitmap);
}
//背景画像はないがGIF画像がある場合は、
//空のDDBを作成して裏DCに設定し、白色で塗りつぶしておきます。

else if ((pGif)&&(pGif->IsEnabled())){
width =m_rectDraw.right;
height=m_rectDraw.bottom;
hBitmap=::CreateCompatibleBitmap(pDC->GetSafeHdc(),width,height);
hGdiObj=MemDC.SelectObject(hBitmap);
MemDC.PatBlt(0,0,width,height,WHITENESS);
}
//上記以外はこの関数を終了します。
else {
MemDC.DeleteDC();
return;
}
//GIF画像を裏DCに書き込みます。
pGif->OnDraw(&MemDC,m_rectDraw.left,m_rectDraw.top,m_rectDraw.Width(),m_rectDraw.Height());
//裏DCの内容をViewクラスのDCに書き込みます。
pDC->BitBlt(0,0,width,height,&MemDC,0,0,SRCCOPY);

//後始末
MemDC.SelectObject(hGdiObj);
::DeleteObject(hBitmap);
MemDC.DeleteDC();
}

//ダイヤログバーの内容を更新します。
CMainFrame* pFrame=(CMainFrame*)AfxGetMainWnd();
pFrame->m_dlgBarGifCtrl.Update();

}


void CGIFFileTestView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
CSize sizeTotal;
// TODO: このビューのサイズの合計を計算します。
CGIFFileTestDoc* pDoc = GetDocument();
if (pDoc){
//CGIFFileクラスへのポインタを取得します。
CGIFFile* pGif=pDoc->m_pGif;
//GIF画像が有る場合。
if ((pGif)&&(pGif->IsEnabled()))
{
int cx=pGif->Width();
int cy=pGif->Height();
//CViewクラスの描画領域に設定します。
m_rectDraw.right=m_rectDraw.left+cx;
m_rectDraw.bottom=m_rectDraw.top+cy;
//CGIFFileクラスの描画領域ポインタに設定します。
pGif->m_lpRect=&m_rectDraw;
}
}
sizeTotal.cx=m_rectDraw.right;
sizeTotal.cy=m_rectDraw.bottom;

SetScrollSizes(MM_TEXT, sizeTotal);
}



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

上記のzipファイルをダウンロードして、「giflibのインストール」で作成した「giflib-4.1.6」フォルダーと「libgif」フォルダーがある同じフォルダー内にWinRAR等を使って解凍すると、「GIFFileTest02A」と言う名のフォルダーが作成されます。



GIFFileTest02A」フォルダー内にある「GIFFileTest.sln」を開いてから、ソリューションエクスプローラで「libgif」プロジェクトを右クリックしてポップアップメニューを表示させ、「プロパティ」を選択すると「libgifプロパティページ」ダイヤログが表示されます。



ダイヤログの左にあるツリービューで「コンフィギュレーションプロパティ」/「C/C++」/「コード生成」を選択し、「Runtime library」の欄を「Multi-threaded Debug (/MTd)」から「Multi-threaded (Debug) DLL (/MDd)」に変更してから「OK」ボタンを押してください。

リリースビルドについても同様に、コンフィギュレーションコンボボックスを「Release」に切り替えてから、「Multi-threaded (/ML)」から「Multi-threaded DLL (/MD)」に変更してから「OK」ボタンを押して下さい。



引き続いて、ダイヤログの左にあるツリービューで「コンフィギュレーションプロパティ」/「C/C++」/「コマンドライン」を選択し、「オプションの追加」エディットボックスに以下のコマンドラインをコピー&ペーストして、「OK」ボタンを押して下さい。

リリースビルドについても同様に、下記のコマンドオプションを追加して下さい。

/D "_OPEN_BINARY" /D "HAVE_IO_H" /D "HAVE_FCNTL_H" /D "HAVE_STDARG_H" /D "HAVE_BASETSD_H"


libgif」プロジェクト内にある以下の三つのファイルについて、下記の通りに修正してあるか確認して下さい。
「dgif_lib.c」

357行目if分の後に続く一つ目の条件文のすぐ後ろに赤字部分を追加して、後方の分岐条件をコメントアウトして下さい。


/*** FIXME: Why do we check both of these in order to do this?
* Why do we have both Image and SavedImages? */

if (GifFile->Image.ColorMap)// && GifFile->SavedImages == NULL) ←コメントアウトする部分。
FreeMapObject(GifFile->Image.ColorMap);



「egif_lib.c」

367行目のif文のすぐ上に赤字部分の2行を追加して下さい。


GifFile->Image.Left = Left;
GifFile->Image.Top = Top;
GifFile->Image.Width = Width;
GifFile->Image.Height = Height;
GifFile->Image.Interlace = Interlace;

if (GifFile->Image.ColorMap) //←追加する行。
FreeMapObject(GifFile->Image.ColorMap); //←追加する行。


if (ColorMap) {
GifFile->Image.ColorMap = MakeMapObject(ColorMap->ColorCount,
ColorMap->Colors);
if (GifFile->Image.ColorMap == NULL) {
_GifError = E_GIF_ERR_NOT_ENOUGH_MEM;
return GIF_ERROR;
}



「gif_hash.c」

51行目の「#include "gif_lib.h"」のすぐ上に、malloc.hをインクルードする行を追加して下さい。


#include <stdio.h>
#include <string.h>
#include <malloc.h> //←追加する行。
#include "gif_lib.h"
#include "gif_hash.h"
#include "gif_lib_private.h"



「F5」キーを押すと、ビルド確認のダイヤログが表示されるので「Yes」を選択してソリューションをビルドします。ビルドエラーが出てどうしても「libgif」のインストールが上手くいかない方は、「GIFFileTest」プロジェクト内にある「GIFFile.cpp」ファイルを開いて、下記の赤字の行にあるコメントアウトを外して、フォルダー内にあるライブラリーファイルを直接読み込むようにします。修正後、再度「F5」キーでビルドしてみて下さい。

extern "C"
{
#include "gif_lib.h"
}

//ソリューションにlibgifプロジェクトがない場合は、下のコメントアウトを外して下さい。
///* ←コメントアウトを外してください。
#ifdef _DEBUG
#pragma comment(lib,"libgifd")
#else//_DEBUG
#pragma comment(lib,"libgif")
#endif//_DEBUG
//*/ ←コメントアウトを外してください。


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



「res」フォルダー内の「ディスポース2.gif」を選択し、「開く」ボタンを押して読み込みます。動画が表示されたところで、背景画像が透過されているか確認してください。


sideback
メニュー



次にメニューから「表示」/「ビューの背景色にディスポース」にチェックを入れてみて、背景がビューウィンドウの背景色で塗りつぶされているか確認してください。


sideback
メニュー



残りの「ディスポース1.gif」/「ディスポース3.gif」/「ディスポース4.gif」についても同様に正しく動画表示されるか確認して下さい。

<<GIFファイルの読み込み 第2部ページの先頭GIFファイルの書き込み 第1部>>




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

メールアドレス:

ホームページアドレス:

コメント:

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


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

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