FC2ブログ

ホーム > GIFファイルフォーマット > GIFファイルの読み込み 第2部

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



GIFファイルの読み込み 第2部」では、GIFファイルを開いてから各GIFブロックを読み込み、指定されたDCに画像を表示するまでを解説します。

GIFファイルの読み込み手順は以下の通りです。

  1. GIFファイルを開いて、ファイルストリームを取得します。
  2. 取得したファイルストリームを使ってGIFライブラリを開き、グローバルデータを保存します。
  3. ローカルデータを順に読み込んでリストに格納します。
  4. ターミネータに突き当たったら読み込みを終了して、GIFライブラリを閉じます。
  5. GIFファイルを閉じた後は、リストに格納されたローカルデータ(CGIFLocalクラス)を元に動画を表示します。





GIFライブラリ「gif-4.1.6」を使用するためのインクルード定義です。

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


「giflibのインストール」でダウンロードした「gif-4.1.6\lib」フォルダーから、「gif_lib.h」を、今回のプログラム本体を格納した「GIFFile.cpp」ファイルのあるフォルダーにコピーしておきます。

GIFファイルの書き込みには、24ビットフルカラー画像32ビットアルファ透過画像減色処理するために、以下のインクルード定義が必要となります。

#include <shlwapi.h>
#include "DIBFile.h"
#include "MedianCut.h"
#include "Dither.h"


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

DIBFile.h DIBFile.cpp
Rle.h Rle.cpp
MedianCut.h MedianCut.cpp
Dither.h Dither.cpp


GIFファイルの書き込みで使用するスタンダードコール形式のサブルーチン関数です。

//32ビットピクセルデータからカラーテーブルを作成します。
UINT __stdcall _CountPixelColor32(PBYTE pBits32,DWORD dwSizeImage,UINT cEntries,RGBQUAD* prgbq)
{
ASSERT(AfxIsValidAddress(pBits32,dwSizeImage,0));
ASSERT(AfxIsValidAddress(prgbq,sizeof(RGBQUAD)*cEntries));

UINT nCount=0; //色数カウントを0にする。
DWORD* src=(DWORD*)pBits32; //ピクセルビットへのポインタを取得する。
::ZeroMemory(prgbq,sizeof(RGBQUAD)*cEntries);
int indexTransparent=-1;

UINT nPixels=dwSizeImage>>2;
//すべてのピクセルの色を読み込んでカラーテーブルを作成します。
for(UINT i=0;i<nPixels;i++){
DWORD bgra=*src++;
DWORD* dst=(DWORD*)prgbq; //カラーテーブルへのポインタを取得する。
BOOL bFind=FALSE; //カラーテーブルに同じ色があればTRUE
//カラーテーブルにある色を読み込み、ピクセルの色と比較する。
for (UINT j=0;j<nCount;j++){
//同じ色ならbFindをTRUEにしてループを抜ける。
if (*dst++==bgra){
bFind=TRUE;
break;
}
}
if (!bFind){//色が見つからなかった。
//カラーテーブルが一杯で色を追加できない場合は
//FALSEを戻して終了します。

if (nCount>=cEntries) return FALSE;
//カラーテーブルに色を加える。
*((DWORD*)(&prgbq[nCount++]))=bgra;
}
}
return nCount;
}

//入力されたカラーテーブルから指定色の色番号を取得します。
//見つからなかった場合は-1を返します。

inline int __stdcall _GetFixedColorIndex(UINT nStart,UINT cEntries,RGBQUAD* prgbq,DWORD bgra)
{
ASSERT(AfxIsValidAddress(prgbq,sizeof(RGBQUAD)*cEntries,0));

DWORD* src=(DWORD*)prgbq;
for(UINT i=nStart;i<cEntries;i++)
if (bgra==*src++) return i;
return -1;
}

//マスクカラーテーブル(_GetMaskColor関数で使用します。)
#define MAX_COLOR_MASK 119
static COLORREF g_colorMask[MAX_COLOR_MASK]={
0x0000FF,0x00FF00,0x00FFFF,0xFF0000,0xFF00FF,0xFFFF00,0xFFFFFF,
0x0000c0,0x00c000,0x00c0c0,0xc00000,0xc000c0,0xc0c000,0xc0c0c0,
0x000080,0x008000,0x008080,0x800000,0x800080,0x808000,0x808080,
0x000040,0x004000,0x004040,0x400000,0x400040,0x404000,0x404040,
0xccccFF,0xccFFcc,0xccFFFF,0xFFcccc,0xFFccFF,0xFFFFcc,0xcccccc,
0x000010,0x001000,0x001010,0x100000,0x100010,0x101000,0x101010,
0x000020,0x002000,0x002020,0x200000,0x200020,0x202000,0x202020,
0x000030,0x003000,0x003030,0x300000,0x300030,0x303000,0x303030,
0x000050,0x005000,0x005050,0x500000,0x500050,0x505000,0x505050,
0x000060,0x006000,0x006060,0x600000,0x600060,0x606000,0x606060,
0x000070,0x007000,0x007070,0x700000,0x700070,0x707000,0x707070,
0x000090,0x009000,0x009090,0x900000,0x900090,0x909000,0x909090,
0x0000a0,0x00a000,0x00a0a0,0xa00000,0xa000a0,0xa0a000,0xa0a0a0,
0x0000b0,0x00b000,0x00b0b0,0xb00000,0xb000b0,0xb0b000,0xb0b0b0,
0x0000d0,0x00d000,0x00d0d0,0xd00000,0xd000d0,0xd0d000,0xd0d0d0,
0x0000e0,0x00e000,0x00e0e0,0xe00000,0xe000e0,0xe0e000,0xe0e0e0,
0x0000f0,0x00f000,0x00f0f0,0xf00000,0xf000f0,0xf0f000,0xf0f0f0,
};

//与えられたカラーテーブルにない色(b,g,r,aの順)を取得します。
//エラー時は-1を返します。

DWORD __stdcall _GetMaskColor(UINT cEntries,RGBQUAD* prgbq)
{
for(int i=0;i<MAX_COLOR_MASK;i++){
BOOL bFind=FALSE;
//マスクカラーテーブルから色を取得します。
DWORD bgrMask=g_colorMask[i];
//カラーテーブルの先頭アドレスを、読み込みポインタに設定します。
DWORD* src=(DWORD*)prgbq;
for(UINT j=0;j<cEntries;j++){
//カラーテーブルの色と一致した場合はループを抜けます。
if (*src++==bgrMask){
bFind=TRUE;
break;
}
}
//カラーテーブルには一致する色がなかったので、
//このマスク色を返して、正常終了します。

if (!bFind) return bgrMask;
}
//全てのマスク色とカラーテーブルの色が一致したので、
//-1を返してエラー終了します。

return -1;
}


GIFファイルDIBカラー/マスク画像を、一コマ分だけ書き込みます。

//GIFファイルDIB画像を書き込みます。
BOOL __stdcall _SaveGIF(LPCTSTR lpszFileName,HBITMAP hbmColor,HBITMAP hbmMask,
COLORREF rgbBackground)
{
CGIFFile gif(0);
CGIFLocal* pLocal=new CGIFLocal(hbmColor,hbmMask);
gif.AddLocal(pLocal);
return gif.SaveFile(lpszFileName,rgbBackground);
}


CGIFFileクラスGIFファイルに書き込みます。

//GIFファイルを開いて、CGIFFileクラスのStreamIn関数を呼び出します。
BOOL CGIFFile::SaveFile(LPCTSTR lpszFileName,COLORREF rgbBackground)
{
CFile outfile;
if (!outfile.Open(lpszFileName,CFile::modeCreate|CFile::modeWrite)) return FALSE;
BOOL bResult=StreamOut(&outfile,rgbBackground);
outfile.Close();
return bResult;
}


32ビットアルファDIBを減色する際に必要なアルファブレンド演算マクロです。

//アルファブレンド演算マクロ
#ifndef ALPHA_BLEND
#define ALPHA_BLEND(composite,fg,alpha,bg){\
WORD temp=((WORD)(fg)*(WORD)(alpha)+(WORD)(bg)*(WORD)(255-(WORD)(alpha))+(WORD)128);\
(composite)=(BYTE)((temp+(temp>>8))>>8);\
}
#endif//ALPHA_BLEND


ファイルに書き込むためのユーザー定義関数です。

//GIFストリーム書き込み関数
int _EGifOutputFunc(GifFileType* pGifInfo,const GifByteType* buf, int size)
{
CFile* outfile=(CFile*)pGifInfo->UserData;
outfile->Write(buf,(UINT)size); //CFileクラスを使ってファイルに書き込む
return size;
}


CGIFFileクラスのリストに格納されたCGIFLocalクラスの内容を元に、GIFストリームに書き出します。

//GIFストリームへの書き込み
//この関数を呼び出す前に、CGIFLocalクラス
//カラー画像マスク画像遅延時間ディスポサル方法インターレースの有無
//を設定し、AddLocal関数CGIFLocalクラスを追加しておく必要があります。

BOOL CGIFFile::StreamOut(CFile* outfile,COLORREF rgbBackground)
{
ASSERT(m_pGifInfo==NULL);

if (!AfxIsValidAddress(outfile,sizeof(CFile),0)) return FALSE;
if (!IsEnabled()) return FALSE;

//GIF出力ストリームを作成します。
m_pGifInfo=::EGifOpen((void*)outfile,_EGifOutputFunc);
if (!m_pGifInfo) return FALSE;

//引数の背景色が有効なら、背景色を設定します。
if (!(rgbBackground&0xFF000000)){
m_rgbBackground=rgbBackground;
m_bBackground=TRUE;
}

//アプリケーションブロックの有無を決定します。
if ((!m_bNetScapeExt)&&(m_nImageCount>1))
m_bNetScapeExt=TRUE;

//グラフィックブロックの有無を決定します。
if (!m_bGraphicBlock){
for(UINT i=0;i<m_nImageCount;i++){
CGIFLocal* pLocal=GetLocal(i);
if ((pLocal->m_delay)||
(pLocal->m_disposalMethod)||
(pLocal->m_indexTransparent!=-1))
{
m_bGraphicBlock=TRUE;
break;
}
}
}

RGBQUAD rgbqLct[256];

//現在の画像から32ビット画像を作成してリストに収めます。--------------------------------
BITMAPINFOHEADER bh={0};
bh.biSize=sizeof(BITMAPINFOHEADER);
bh.biWidth=m_LSWidth;
bh.biHeight=m_LSHeight;
bh.biBitCount=32;
bh.biPlanes=1;
UINT widthBytes=WIDTHBYTES(m_LSWidth*32); //ビットマップバイト幅
bh.biSizeImage=widthBytes*m_LSHeight;
UINT nPixels=bh.biSizeImage>>2; //ピクセル総数の取得

//ビットマップ画像転送用のメモリーDCを作成します。
HDC hDstDC=::CreateCompatibleDC(0);
HDC hSrcDC=::CreateCompatibleDC(0);
HGDIOBJ hDstObj=0;
HGDIOBJ hSrcObj=0;

//カラー画像をコピーする32ビットDIBを作成します。
LPVOID pvBitsColor32;
HBITMAP hbmColor32=::CreateDIBSection(0,(BITMAPINFO*)&bh,DIB_RGB_COLORS,&pvBitsColor32,0,0);
if (!hbmColor32) return FALSE;
//マスク画像をコピーする32ビットDIBを作成します。
LPVOID pvBitsMask32;
HBITMAP hbmMask32 =::CreateDIBSection(0,(BITMAPINFO*)&bh,DIB_RGB_COLORS,&pvBitsMask32,0,0);
ASSERT(hbmMask32);

BITMAP bm;
for(UINT i=0;i<m_nImageCount;i++){

//CGIFLocalクラスへのポインタを取得します。
CGIFLocal* pLocal=GetLocal(i);
ASSERT(pLocal);

//BITMAP構造体を取得しておきます。
HBITMAP hbmColor=pLocal->m_hbmColor;
HBITMAP hbmMask =pLocal->m_hbmMask;
::GetObject(hbmColor,sizeof(BITMAP),&bm);

//DDBの場合はDIBに変換します。
if ((bm.bmBitsPixel==32)&&(bm.bmBits==0)){
//第2引数のビット深度を0にすると、最小ビットカウントに変換します。
hbmColor=_DDBToDIB(hbmColor,0);
::DeleteObject(pLocal->m_hbmColor);
//256色以上の時は減色処理するので、原画ごと変更していしまいます。
pLocal->m_hbmColor=hbmColor;
//DIBのBITMAP構造体を取得し直します。
::GetObject(hbmColor,sizeof(BITMAP),&bm);
}

//カラー画像をコピーします。
HGDIOBJ hDstOld=::SelectObject(hDstDC,hbmColor32);
HGDIOBJ hSrcOld=::SelectObject(hSrcDC,hbmColor);

//初めての場合は元のDCのオブジェクトハンドルを保存します。
if (!hDstObj) hDstObj=hDstOld;
if (!hSrcObj) hSrcObj=hSrcOld;

::PatBlt(hDstDC,0,0,m_LSWidth,m_LSHeight,BLACKNESS);
::BitBlt(hDstDC,0,0,m_LSWidth,m_LSHeight,hSrcDC,0,0,SRCCOPY);

//カラービットマップのビット深度が32ビット以外で、
//マスク画像があればコピーします。

if ((bm.bmBitsPixel!=32)&&(hbmMask)){
::SelectObject(hDstDC,hbmMask32);
::SelectObject(hSrcDC,hbmMask);
::PatBlt(hDstDC,0,0,m_LSWidth,m_LSHeight,WHITENESS);
::BitBlt(hDstDC,0,0,m_LSWidth,m_LSHeight,hSrcDC,0,0,SRCCOPY);
}

//画像をメモリーDCから切り離します。
::SelectObject(hDstDC,hDstObj);
::SelectObject(hSrcDC,hSrcObj);

//32ビットバッファを作成してピクセルデータを保存します。
PBYTE pBitsWrite32=new BYTE[bh.biSizeImage];
::CopyMemory(pBitsWrite32,pvBitsColor32,bh.biSizeImage);

//32ビットバッファにマスクビットを保存します。
//カラーテーブル作成をより簡潔にするため、アルファ値をRGB32とは逆にします。

//カラービットマップのビット深度が32ビット以外の場合。
if (bm.bmBitsPixel!=32){
//32ビットバッファへのポインタを取得します。
DWORD* dst=(DWORD*)pBitsWrite32;
//マスク画像が有る場合。
if (hbmMask){
DWORD* msk=(DWORD*)pvBitsMask32;
for(UINT i=0;i<nPixels;i++){
//背景色を透過する
if ((msk)&&(((*msk++)&0xFFFFFF)==0xFFFFFF))
*dst++=0xFF000000;
//画像を残す部分
else
(*dst++)&=0x00FFFFFF;
}
}
//マスクがない場合。
else{
for(UINT i=0;i<nPixels;i++)
(*dst++)&=0x00FFFFFF;
}
}
//カラー画像のビット深度が32ビットの場合。
else{
//32ビットバッファへのポインタを取得します。
DWORD* dst=(DWORD*)pBitsWrite32;
//アルファDIB32かどうかチェックします。
BOOL bAlpha32=FALSE;
for(UINT i=0;i<nPixels;i++){
//アルファ値に有効な値があれば、フラグをセットしてループを抜けます。
if (((*dst++)&0xFF000000)!=0){
bAlpha32=TRUE;
break;
}
}
//マスク画像が有効な場合は(通常の32ビットアイコンのDDB)、
//減色処理をする場合を考慮して、マスク情報を原画のアルファビットに格納しておきます。

if ((!bAlpha32)&&(hbmMask)){
//原画のサイズがスクリーンサイズと違う場合を考慮して、
//縦横に分けてピクセルを処理します。

UINT width=bm.bmWidth; //原画の幅
UINT height=abs(bm.bmHeight); //原画の高さ
for (UINT i=0;i<height;i++){
//原画のピクセルデータへのポインタを取得します。
DWORD* bmp=(DWORD*)((PBYTE)bm.bmBits+bm.bmWidthBytes*i);
//32ビットバッファへのポインタを取得します。
DWORD* dst=(DWORD*)((PBYTE)pBitsWrite32+widthBytes*i);
//32ビットマスクDIBへのポインタを取得します。
DWORD* msk=(DWORD*)((PBYTE)pvBitsMask32+widthBytes*i);
for (UINT j=0;j<width;j++){
//背景色を透過する
if (((*msk++)&0xFFFFFF)==0xFFFFFF){
(*bmp++)&=0x00FFFFFF;
*dst++=0xFF000000;
}
//画像を残す部分
else{
(*bmp++)|=0xFF000000;
(*dst++)&=0x00FFFFFF;
}
}
}
}
//アルファ32ビットDIBの場合。(PNGアルファ画像/PNG圧縮アイコンのDDB)
else{
DWORD* dst=(DWORD*)pBitsWrite32;
for(UINT i=0;i<nPixels;i++){
//アルファ値が0x80未満なら、背景色を透過します。
if (((*dst)&0xFF000000)<0x80000000)
*dst++|=0xFF000000;
//それ以外は画像を残します。
else
(*dst++)&=0x00FFFFFF;
}
}
}

//CGIFLocalクラスに格納します。
ASSERT(!pLocal->m_pBitsWrite32);
pLocal->m_pBitsWrite32=pBitsWrite32;
pLocal->m_cEntries=0;
pLocal->m_indexTransparent=-1;
}

//先頭のCGIFLocalへのポインタの取得
CGIFLocal* pLocalFirst=GetLocal(0);

//ディスポサル方法に従って元の画像を復元します。----------------------------------------
//一番後ろの画像から見ていきます。

for(UINT i=m_nImageCount-1;i>0;i--){
//CGIFLocalクラスへのポインタを取得します。
CGIFLocal* pLocalPrev=GetLocal(i-1); //前の画像
CGIFLocal* pLocalNext=GetLocal(i); //現在着目している画像

//各画像のピクセルバッファへのポインタを取得します。
DWORD* prev=(DWORD*)pLocalPrev->m_pBitsWrite32; //前の画像へのポインタ
DWORD* next=(DWORD*)pLocalNext->m_pBitsWrite32; //現在着目している画像へのポインタ
DWORD* dest=next;
DWORD* first=(DWORD*)pLocalFirst->m_pBitsWrite32; //一番最初の画像へのポインタ

UINT nLctCount=0;
int indexTransparent=-1;
switch(pLocalPrev->m_disposalMethod){
case GIF_DISPOSE_NONE:
case GIF_DISPOSE_LEAVE:
//何もしない場合、透過色がなくて色数が256色びっちりある時は、
//透過色を新たに割り当てられないので画素数は減らせないため、まずは色数を求めます。

nLctCount=_CountPixelColor32(pLocalNext->m_pBitsWrite32,bh.biSizeImage,256,rgbqLct);
//色数が求められたら、透過色番号を求めておきます。
indexTransparent=(nLctCount)?_GetFixedColorIndex(0,nLctCount,rgbqLct,0xFF000000):-1;
//色数が256色未満か透過画像の場合、前の画像と比較してピクセルの色が同じ場合。
if ((nLctCount<256)||(indexTransparent!=-1)){
for(UINT j=0;j<nPixels;j++){
//一つ前の画像と色が同じ場合は、透過色に変更して画素数を減らします。
if (*prev++==*next++) *dest++=0xFF000000;
//それ以外は読み飛ばします。
else dest++;
}
}
break;
case GIF_DISPOSE_FIRST:
//一番最初の画像に復元する場合。
for(UINT j=0;j<nPixels;j++){
//一番最初の画像と色が同じ場合は、透過色に変更して画素数を減らします。
if (*first++==*next++) *dest++=0xFF000000;
//それ以外は読み飛ばします。
else
dest++;
}
break;
case GIF_DISPOSE_BACKGND: //背景色でディスポースする場合、何もしません。
case GIF_DISPOSE_PREV: //一つ前の画像に復元する場合は、良い方法が思いつきませんでした。
default:
break;
}
}
//ローカルカラーテーブルを作成します。-------------------------------------------
for(UINT i=0;i<m_nImageCount;i++){
//CGIFLocalクラスへのポインタを取得します。
CGIFLocal* pLocal=GetLocal(i);

//カラーテーブルを作成します。
UINT nLctCount=_CountPixelColor32(pLocal->m_pBitsWrite32,bh.biSizeImage,256,rgbqLct);

//色が256色より多い場合、減色処理をしてからもう一度やり直します。
if (!nLctCount){
//BITMAP構造体を取得しておきます。
HBITMAP hbmColor=pLocal->m_hbmColor;
HBITMAP hbmMask =pLocal->m_hbmMask;
::GetObject(hbmColor,sizeof(BITMAP),&bm);
UINT width =bm.bmWidth;
UINT height=abs(bm.bmHeight);

PBYTE pBitsWrite32=pLocal->m_pBitsWrite32;
//画像のビット深度が32ビットの場合は、減色する前に白色とアルファブレンドします。
PBYTE pBitsMask8=0;
if (bm.bmBitsPixel==32){
BYTE b,g,r,a;
BYTE b2,g2,r2;
//アルファ値を保存するためのバッファです。
pBitsMask8=new BYTE[width*height];
for(UINT i=0;i<height;i++){
PBYTE src=(PBYTE)bm.bmBits+bm.bmWidthBytes*i;
PBYTE dst=src;
PBYTE msk=pBitsMask8+width*i;
for (UINT j=0;j<width;j++){
b=*src++;
g=*src++;
r=*src++;
a=*src++;
//アルファ値65以上のピクセルは残します。
if (a>0x40){
ALPHA_BLEND(b2,b,a,0xFF);
ALPHA_BLEND(g2,g,a,0xFF);
ALPHA_BLEND(r2,r,a,0xFF);
*dst++=b2;
*dst++=g2;
*dst++=r2;
*msk++=0;
}
//それ以下は背景を透過します。
//透過部分を白色にして、減色効率を上げます。

else{
*dst++=0xFF;
*dst++=0xFF;
*dst++=0xFF;
*msk++=0xFF;
}
*dst++=0;
}
}
}
//24ビットDIBのコピー画像を作成します。
HBITMAP hbm24bit=_CopyDIB(hbmColor,24,0);
//メディアンカット減色で8ビットDIBを作成します。
HBITMAP hbm8bit=_MedianCut(hbm24bit,(hbmMask)?254:256);
//ディザをつけます。(マスク値:0xFEFEFE位が適当でした。)
_FloydSteinberg(hbm8bit,hbm24bit,0xFEFEFE);
//24ビットDIBはもう要らないので削除します。
::DeleteObject(hbm24bit);
//8ビットDIBを32ビットDIBにコピーします。
hDstObj=::SelectObject(hDstDC,hbmColor32);
hSrcObj=::SelectObject(hSrcDC,hbm8bit);
::PatBlt(hDstDC,0,0,m_LSWidth,m_LSHeight,BLACKNESS);
::BitBlt(hDstDC,0,0,m_LSWidth,m_LSHeight,hSrcDC,0,0,SRCCOPY);

//カラービットマップのビット深度が32ビット以外で、
//マスク画像があればコピーします。

if ((bm.bmBitsPixel!=32)&&(hbmMask)){
::SelectObject(hDstDC,hbmMask32);
::SelectObject(hSrcDC,hbmMask);
::PatBlt(hDstDC,0,0,m_LSWidth,m_LSHeight,WHITENESS);
::BitBlt(hDstDC,0,0,m_LSWidth,m_LSHeight,hSrcDC,0,0,SRCCOPY);
}

//画像をメモリーDCから切り離します。
::SelectObject(hDstDC,hDstObj);
::SelectObject(hSrcDC,hSrcObj);

//8ビットDIBはもう要らないので削除します。
::DeleteObject(hbm8bit);

//32ビットバッファにピクセルデータを保存します。
::CopyMemory(pBitsWrite32,pvBitsColor32,bh.biSizeImage);

//32ビットバッファにマスクビットを保存します。
//カラーテーブル作成をより簡潔にするため、アルファ値をRGB32とは逆にします。

DWORD* dst=(DWORD*)pBitsWrite32;
//カラービットマップのビット深度が32ビット以外の場合。
if (bm.bmBitsPixel!=32){
//マスク画像が有る場合。
if (hbmMask){
DWORD* msk=(hbmMask)?(DWORD*)pvBitsMask32:0;
for(UINT i=0;i<nPixels;i++){
//背景色を透過する
if ((msk)&&(((*msk++)&0xFFFFFF)==0xFFFFFF))
*dst++=0xFF000000;
//画像を残す部分
else
(*dst++)&=0x00FFFFFF;
}
}
//マスクがない場合。
else{
for(UINT i=0;i<nPixels;i++)
(*dst++)&=0x00FFFFFF;
}
}
//カラー画像のビット深度が32ビットの場合は、
//残してあったマスク情報をアルファビットに書き写します。

else {
ASSERT(pBitsMask8);
PBYTE msk=pBitsMask8;
for(UINT i=0;i<nPixels;i++){
//画像を残す部分
if ((*msk++)==0)
(*dst++)&=0x00FFFFFF;
//背景色を透過する部分
//減色前に白で塗りつぶした部分を黒にします。

else
*dst++=0xFF000000;
}
}
if (pBitsMask8) delete[]pBitsMask8;

//ローカルカラーテーブルを作成します。
nLctCount=_CountPixelColor32(pLocal->m_pBitsWrite32,bh.biSizeImage,256,rgbqLct);
//ローカルカラーテーブルが作成されていれば、仮の透過色番号を求めておきます。
pLocal->m_indexTransparent=(nLctCount)? _GetFixedColorIndex(0,nLctCount,rgbqLct,0xFF000000):-1;
}

//CGIFLocalクラスにローカルカラーテーブルを格納します。
pLocal->m_cEntries=nLctCount;
UINT nLctSize=(nLctCount%2)? nLctCount+1:nLctCount;
pLocal->m_pRgbq=new RGBQUAD[nLctSize];
::ZeroMemory(pLocal->m_pRgbq,sizeof(RGBQUAD)*nLctSize);
::CopyMemory(pLocal->m_pRgbq,rgbqLct,sizeof(RGBQUAD)*nLctCount);
pLocal->m_indexTransparent=_GetFixedColorIndex(0,nLctCount,pLocal->m_pRgbq,0xFF000000);
}

//メモリーDCと32ビットDIBは、もう必要ないので削除します。
::DeleteDC(hDstDC);
::DeleteDC(hSrcDC);

::DeleteObject(hbmColor32);
::DeleteObject(hbmMask32);

//グローバルカラーテーブルを作成します。-----------------------------------------
RGBQUAD rgbqGct[256]={0};
//グローバルカラーテーブルの色数
UINT nGctCount=0;
//ローカルカラーテーブルの作成で使用したスタックメモリーを再利用します。
::ZeroMemory(rgbqLct,sizeof(RGBQUAD)*256);
int indexTransparentGct=-1;

for(UINT i=0;i<m_nImageCount;i++){
//CGIFLocalクラスへのポインタを取得します。
CGIFLocal* pLocal=GetLocal(i);
//ローカルカラーテーブルへのポインタを取得します。
DWORD* src=(DWORD*)pLocal->m_pRgbq;
//ローカルカラーテーブルの色数
UINT nLctCount=pLocal->m_cEntries;
UINT nCount=nGctCount;
for(UINT j=0;j<nLctCount;j++){
//ローカルカラーテーブルから色(BGRAの順)を取り出します。
DWORD bgra=*src++;
BOOL bFind=FALSE;
//グローバルカラーテーブル内でへのポインタを取得します。
DWORD* dst=(DWORD*)rgbqLct;
for(UINT k=0;k<nCount;k++){
if (bgra==*dst++){
bFind=TRUE;
break;
}
}
if (!bFind){
//カラーテーブルが一杯の時は、ループを抜けます。
if (nCount>=256){
nCount++;
break;
}
//透過色の場合は透過色番号を保存しておきます。
if (bgra==0xFF000000) indexTransparentGct=nCount;
//カラーテーブルに色を加える。
*((DWORD*)(&rgbqLct[nCount++]))=bgra;
}
}
//色が256色より多いとき
if (nCount>256){
//作業用カラーテーブルを1つ前の状態に戻します。
::CopyMemory(rgbqLct,rgbqGct,sizeof(RGBQUAD)*256);
}
else{
nGctCount=nCount;
//グローバルカラーテーブルに結果をコピーします。
::CopyMemory(rgbqGct,rgbqLct,sizeof(RGBQUAD)*nGctCount);
//グローバルカラーテーブルにすべての色が収まったので、
//ローカルカラーテーブルを削除します。

delete[]pLocal->m_pRgbq;
pLocal->m_cEntries=0;
pLocal->m_pRgbq=0;
}
}
//メンバー変数にグローバルカラーテーブルをコピーします。
UINT nGctSize=(nGctCount%2)? nGctCount+1:nGctCount;
m_prgbqGct=new RGBQUAD[nGctSize];
::ZeroMemory(m_prgbqGct,sizeof(RGBQUAD)*nGctSize);
::CopyMemory(m_prgbqGct,rgbqGct,sizeof(RGBQUAD)*nGctCount);


//グローバルカラーテーブルに透過色があれば、
//グローバルカラーテーブルにない色を探して透過色に充てます。

DWORD bgrMask=0;
if (indexTransparentGct!=-1){
//カラーテーブルにない色(b,g,r,aの順)を取得します。
DWORD bgrMask=_GetMaskColor(nGctCount,m_prgbqGct);
ASSERT(bgrMask!=-1);
//透過色をカラーテーブルに格納します。
*((DWORD*)(&m_prgbqGct[indexTransparentGct]))=bgrMask;

//暗い順にグローバルカラーテーブルの色を並べます。
nGctCount=_SortColorTable(nGctCount,rgbqGct);

//再度グローバルカラーテーブル内の透過色番号を求めます
indexTransparentGct=_GetFixedColorIndex(0,nGctCount,m_prgbqGct,bgrMask);
}
else
//暗い順にグローバルカラーテーブルの色を並べます。
nGctCount=_SortColorTable(nGctCount,m_prgbqGct);

m_nGctCount=nGctCount;
m_bGct=TRUE;

//色数が奇数ならインクリメントします。
if (m_nGctCount%2) m_nGctCount++;

//各CGIFLocalクラスの透過色番号を設定します。------------------------------------------
for(UINT i=0;i<m_nImageCount;i++){
//CGIFLocalクラスへのポインタを取得します。
CGIFLocal* pLocal=GetLocal(i);
int indexTransparentLct=pLocal->m_indexTransparent;
RGBQUAD* prgbqLct=pLocal->m_pRgbq;
UINT nLctCount=pLocal->m_cEntries;
//透過色がある場合
if (indexTransparentLct!=-1){
//ローカルカラーテーブルの場合。
if (prgbqLct){
//カラーテーブルにない色(b,g,r,aの順)を取得します。
DWORD bgrMask=_GetMaskColor(nLctCount,prgbqLct);
ASSERT(bgrMask!=-1);
//透過色をカラーテーブルに格納します。
*((DWORD*)(&prgbqLct[indexTransparentLct]))=bgrMask;
//暗い順にローカルカラーテーブルの色を並べます。
nLctCount=_SortColorTable(nLctCount,prgbqLct);

//再度ローカルカラーテーブル内の透過色番号を求めます。
pLocal->m_indexTransparent=_GetFixedColorIndex(0,nLctCount,prgbqLct,bgrMask);
}
//グローバルカラーテーブルの場合
else pLocal->m_indexTransparent=indexTransparentGct;
}
else
//暗い順にローカルカラーテーブルの色を並べます。
nLctCount=_SortColorTable(nLctCount,prgbqLct);
}

//現在の値から各ブロックを作成して、ファイルに書き込みます。
BOOL bResult=WriteLocalBlock();

//GIF出力ストリームを削除します。
::EGifCloseFile(m_pGifInfo);
m_pGifInfo=0;

//この関数で作成したメンバー変数を削除します。
for(UINT i=0;i<m_nImageCount;i++){
//CGIFLocalクラスへのポインタを取得します。
CGIFLocal* pLocal=GetLocal(i);
if (pLocal->m_pBitsWrite32){
delete[]pLocal->m_pBitsWrite32;
pLocal->m_pBitsWrite32=0;
}
if (pLocal->m_pRgbq){
delete[]pLocal->m_pRgbq;
pLocal->m_pRgbq=0;
}
pLocal->m_cEntries=0;
pLocal->m_indexTransparent=-1;
}
if (m_prgbqGct){
delete[]m_prgbqGct;
m_prgbqGct=0;
}

return bResult;
}


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


スポンサーサイト



コメント: 0

この記事へのコメント
ブログ著者にのみ知らせます。

Trackback+Pingback: 0

TrackBack URL for this entry
http://hiroshi0945.blog75.fc2.com/tb.php/39-102b3590
Listed below are links to weblogs that reference
GIFファイルの読み込み 第2部 from マルチメディアファイルフォーマット

Home > GIFファイルフォーマット > GIFファイルの読み込み 第2部

タグクラウド
ブロとも申請フォーム

この人とブロともになる

ブロとも一覧

このページの先頭へ戻る