ビットマップを読み込む方法は基本的には2つあってリソースに挿入してそこから読み込む
方法と、ファイルから読み込む方法とあります。リソースから読み込んだ方が簡単なのですが
ビット情報を直接操作することが難しいので、後々紹介するビットを直接操作して透過色を
実現するという方法が使えません。
またリソースにして読み込むとEXEファイルが膨大に
なってしまうので、たくさんのビットマップファイルを使いたいときやファイルを圧縮して
使いたい場合には、ファイルから読み込んだ方がよいでしょう。
前置きはこのへんして、中身に入ります。
16,256色のビットマップは
BITMAPFILEHEADER,BITMAPINFOHEADER,RGBQUADの配列,Color-Indexの配列からなるデータ群です。
読み込んで表示するためにはこれらの変数の意味を理解しなければなりませんので、それぞれを
説明していきます。
WORD bfType; //ビットマップを表す数 :0x4D42 DWORD bfSize; //ビットマップファイルのサイズ WORD bfReserved1;//予約されている :0 WORD bfReserved2;//予約されている :0 DWORD bfOffBits;//ビット列が格納されている位置を表す |
ここで、必要になる変数はこのファイルがビットマップかどうかを表すbfType とビット列が格納されている位置を表すbfOffBitsです
DWORD biSize; //構造体のサイズ LONG biWidth; //ビットマップの幅(ピクセル) LONG biHeight; //ビットマップの高さ(ピクセル) WORD biPlanes; //常に1 WORD biBitCount //1ピクセル辺りのビット数 DWORD biCompression;//圧縮形態 無圧縮ならばBI_RGB(16,256色の場合) DWORD biSizeImage;//画像のバイト数 biCompressionがBI_RGBの場合は0でもよい LONG biXPelsPerMeter;//X方向の1ピクセル辺りのメートル数 LONG biYPelsPerMeter;//Y方向の1ピクセル辺りのメートル数 DWORD biClrUsed;//カラーテーブルに含まれる色の数(0場合はその型の最大になる) DWORD biClrImportant; //重要な色の数 0の場合は全部重要 |
biWidth,biHeightでビットマップの幅と高さを,biPlanesとbiClrUsedで何色のビットマップファイルかが わかります。
BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; |
青、赤、緑の明るさを指定します。rgbReservedは0にします
ビットの配列は、直前のパレットのインデックスで表します。
1 BITMAPFILEHEADERを読み込む(ビットマップで無い場合はじく) 2 BITMAPINFOHEADERを読み込む(16,256で無い場合ははじく) 3 BITMAPINFOHEADER構造体とRGBQUAD構造体の配列からなるBITMAPINFO構造体の領域を 動的に確保する 4 CreateDIBSectionを使って、ビットマップビット列の領域を確保する 5 実際に描画する 6 プログラムが終了したときや、上書き読み込みをする場合は確保した領域を開放する |
このままでは意味不明なので、順を追って説明します。
1と2はそのままなので分かりますよね。問題は3と4だと思います。
5番の実際に描画するという部分で、今回はBitBltを使います。
そうなるとBITMAPのハンドルを取得してそれをメモリDCに選択するという動作が必要に
なりますよね。そこで必要となる関数がCreateDIBSectionなのです。
CreateDIBSectionに3のBITMAPINFO構造体を含むいろいろな引数をいれてやると返戻値として
HBITMAPを返し、ビットマップのビット列の領域も確保してくれます。
BitBltを使わない方法としては、StretchDIBitsがあります。ただこちらだとラスタオペレーションが 使えないのと、速度が若干遅い(?)のでやめておきます。
やっとコーディングだ・・・。
えーと、今回はC++のクラスを使いますのであらかじめご了承ください。
ヘッダ部分
class CDIB { public: CDIB(); ~CDIB(); BOOL Load(char*,HDC); void Draw(HDC hdc,int x,int y); private: void Free(); BITMAPINFO *pBmpInfo; BYTE *pBits; HBITMAP BMP; HANDLE h; int used; private: int Width(){return pBmpInfo->bmiHeader.biWidth;} int Height(){return pBmpInfo->bmiHeader.biHeight;} int Num(){return used;} }; |
関数部分
CDIB::CDIB() { pBmpInfo=NULL; pBits=NULL; } CDIB::~CDIB() { Free(); } void CDIB::Free() { //割り当てあったメモリを解放する if(BMP) { DeleteObject(BMP); BMP=NULL; pBits=NULL; } if(pBmpInfo) { free(pBmpInfo); pBmpInfo=NULL; } } BOOL CDIB::Load(char*filename,HDC hdc) { int PSize;//パレット領域のサイズ int BitsSize;//ビット領域のサイズ int IPSize;//インフォヘッダーとパレット領域を合わせたサイズ DWORD number; BITMAPFILEHEADER BmpFileHdr; BITMAPINFOHEADER BmpInfoHdr; Free();//割り当てた領域の解放 //ファイルを開く h=CreateFile(filename,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); if(h==INVALID_HANDLE_VALUE) return FALSE; //ファイルヘッダーを読み込む ReadFile(h,&BmpFileHdr,sizeof BmpFileHdr,&number,NULL); //インフォヘッダーを読み込む ReadFile(h,&BmpInfoHdr,sizeof BmpInfoHdr,&number,NULL); //16色と256色しかサポートしません(色数も決め打ちしてます) switch (BmpInfoHdr.biBitCount) { case 8: used=256; break; case 4: used=16; break; default: CloseHandle(h); return FALSE; } //パレット領域のサイズ PSize=used*sizeof(RGBQUAD); //インフォヘッダーと、パレットのサイズ IPSize=sizeof(BITMAPINFOHEADER)+PSize; //画像のビットのサイズ BitsSize=BmpFileHdr.bfSize-BmpFileHdr.bfOffBits; //(ヘッダー+パレット)の領域を確保 pBmpInfo=(LPBITMAPINFO)malloc(IPSize); memcpy(pBmpInfo,&BmpInfoHdr,sizeof(BITMAPINFOHEADER));//ヘッダをコピー //パレットを読み込む ReadFile(h,((LPBYTE)pBmpInfo)+sizeof BITMAPINFOHEADER,PSize,&number,NULL); //CreateDIBSectionを使う BMP=CreateDIBSection(hdc,pBmpInfo,DIB_RGB_COLORS,(VOID**)&pBits,0,0); //ビットの位置までシークして SetFilePointer(h,BmpFileHdr.bfOffBits,NULL,FILE_BEGIN); //ビットを読み込む ReadFile(h,pBits,BitsSize,&number,NULL); CloseHandle(h); return TRUE; } void CDIB::Draw(HDC hdc,int x,int y) { //メモリデバイスコンテキストを作成してビットマップを割り当て描画する HDC mem=CreateCompatibleDC(hdc); HBITMAP old; old=SelectObject(mem,BMP); BitBlt(hdc,0,0,Width(),Height(),mem,0,0,SRCCOPY); SelectObject(mem,old); DeleteDC(mem); } |
使い方
CDIB dib; //どこかで定義する case WM_CREATE: if(!dib.Load("ou.bmp",GetDC(hwnd))) { MessageBox(hwnd,"読み込みに失敗しました、終了します","",MB_OK); PostQuitMessage(1); } break; case WM_PAINT: hdc=BeginPaint(hwnd,&ps); dib.Draw(hdc,0,0); EndPaint(hwnd,&ps); break; |
でもこれってあってんのかな・・・。