BMPの表示

余りに基本的な事なんで、今まで書きませんでしたが、一応グラフィックの基礎的な部分を書いておきます。

まず、グラフィックの種類にもいくつかありますが、色数で分ける前に、パレットの存在の有無による分けかたが

あります。これは実際には単純に16Bit以上のカラーモードの場合な訳ですが(Windowsにはデジタル8色モードは

ありません)、このパレットの扱い如何によってプログラムの仕方が随分と変わってきます。また、16Bit以上の

カラーのビットマップデータ等を256色モードの画面で出そうとした場合、16Bit色モード時と同じ方法で出したのでは

Windowsによりシステムパレットにマッピングされて非常に汚くなります。単純にHDCにパレットを結び付けただけで

は16Bitのビットマップをそのパレットを使って変換、なんて事はしてくれませんので、この場合は当然減色する

必要が出てきます。その辺りまでつっこむと話が非常に難しくなってくるので今はパスとして、以下ではパレット

形式のBMPファイルを単純にウィンドウ内に表示する手順を書いておきます。

 

*まず、最初は当然の事ながらパレット形式のBMPファイルを用意します。これは256色以下のBMPファイル

の事です。といっても、現在使用している色数が256色以下とかいうのでは無くて、グラフィックツール上で

扱っているデータ形式が8Bit以下、という事です。Photoshop等ではインデックスカラーモードという事に

なっています。

 

*次に、ファイルをメモリに読み込むプログラムを作ります。メモリの種類は問わないので兎に角メモリに

ファイルを読み込みます。ただ、速度的な問題によりメモリマップドファイルの多用はお勧めしません。

 

*メモリに読み込んだら、一つポインタを作り、そのメモリの先頭からsizeof(BITMAPFILEHEADER)だけ

飛ばした所に設定します。普通、BMPファイルは先頭にBITMAPFILEHEADERを付けるのですが、これは

無くても普通は困りません。ただ、普通そんな事は無い筈なのですが、例えばBITMAPFILEHEADERの後に

いくつかのGAPを設け、その後にBITMAPINFOを続けている様な形ではBITMAPFILEHEADERが無いと正常に

読めないことになります。が、普通そんな事は無いし、それは少なくともWindowsにおけるBMPファイルからは

規格外なので(OS2は考慮していません)考えなくても良いでしょう。と言う訳で、ファイルを読み込んだメモリ

の先頭からsizeof(BITMAPFILEHEADER)だけ飛ばした所にポインタを設定します。

 

*BITMAPFILEHEADERの次にはBITMAPINFOが来るので、このBITMAPINFOからこのビットマップの色数等

の情報を得て、RGBQUADのサイズを決めます。別にこの辺り等は個人の好きでどうにでもなる部分なので、

その辺は好きに組んだら良いでしょう。

 

*RGBQUADのパレットデータからLOGPALETTE構造体を構築します。実際にはLOGPALETTE内の

PALETTEENTRY構造体のメンバがRGBQUADと対応します。気を付けなければならないのは、決して

RGBQUAD==PALETTEENTRYでは無い、と言う事です(構造体メンバの宣言の順番が違う)。

 

*LOGPALETTEを構築したら、パレットハンドルを作成します。このパレットハンドルによりビットマップを

表示させる事になります。

 

*HWNDからHDCを得て、これを使ってCreateDIBitmap等を行います。この操作によりHBITMAPが

得られます。

 

*先ほどのHDCからメモリDCを作成し、そのメモリDCにHBITMAPを選択します。

 

*BitBlt等で描画します。

 

途中からは面倒くさくなったので(^_^; 適当に書いてますが、まぁこんな所です。で、上の操作を実際にソースに

した物が以下になります。なんかインデントが全滅してるけど(^_^;読解してみて下さい。

 

#include <windows.h>

#define SOURCE_FILENAME "C:\\LABO\\TESTBMP\\TEST.BMP"


HINSTANCE hInst ;
HPALETTE hPal ;
HBITMAP hBitmap ;
short width ;
short height ;

class exceptBasic {} ;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) ;

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE previnst, LPSTR lpstrCmdline, int nCmdshow)
{
hInst = hInstance ;

WNDCLASS wc;
wc.style = 0 ;
wc.lpfnWndProc = WindowProc ;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst ;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL ;
wc.lpszClassName = "TESTWINDOW" ;
RegisterClass(&wc);

HWND hwndMain = CreateWindow("TESTWINDOW",
"BMP Test",
WS_OVERLAPPEDWINDOW, /* style */
CW_USEDEFAULT, /* init. x pos */
CW_USEDEFAULT, /* init. y pos */
640, /* init. x size */
480, /* init. y size */
NULL, /* parent window */
NULL, /* menu handle */
hInst, /* program handle */
NULL /* create parms */
);
if(hwndMain == NULL) {
MessageBox(NULL, "Error:Windowの作成に失敗しました", "Test BMP", MB_OK|MB_ICONSTOP) ;
return -1 ;
}

// イメージの読み込み&各種ハンドルの作成
HANDLE hFile = INVALID_HANDLE_VALUE ;
LPVOID imageBuf = NULL ;
hPal = NULL ;
hBitmap = NULL ;
try{
hFile = CreateFile(SOURCE_FILENAME, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL) ;
if(hFile == INVALID_HANDLE_VALUE) {
char text[500] ;
wsprintf(text, "Error:%sのオープンに失敗しました", SOURCE_FILENAME) ;
MessageBox(NULL, text, "Test BMP", MB_OK|MB_ICONSTOP) ;
throw exceptBasic() ;
}

long filesize ;
filesize = GetFileSize(hFile, NULL) ; /* この部分をHeapAllocに直で入れないのは、
その方がデバッグが楽になるからである */
imageBuf = HeapAlloc(GetProcessHeap(), 0, filesize) ;
if(imageBuf == NULL) {
MessageBox(NULL, "メモリの確保に失敗しました", "Test BMP", MB_OK|MB_ICONSTOP) ;
throw exceptBasic() ;
}
DWORD readedBytes ;
if(ReadFile(hFile, imageBuf, filesize, &readedBytes, NULL) == FALSE) {
char text[500] ;
wsprintf(text, "Error:%sの読み込みに失敗しました", SOURCE_FILENAME) ;
MessageBox(NULL, text, "Test BMP", MB_OK|MB_ICONSTOP) ;
throw exceptBasic() ;
}

CloseHandle(hFile) ;
hFile = INVALID_HANDLE_VALUE ;

BITMAPINFO *bmInfo ;
bmInfo = (BITMAPINFO *)((unsigned long)imageBuf + sizeof(BITMAPFILEHEADER)) ;

// Bitmapのサイズを得る
width = (short)bmInfo->bmiHeader.biWidth ;
height = (short)bmInfo->bmiHeader.biHeight ;

// 色数を判断する
short colors ;
colors = 1 << bmInfo->bmiHeader.biBitCount ;

// パレットの作成
LOGPALETTE *lpal ;
lpal = (LOGPALETTE *)new char[sizeof(WORD) * 2 + sizeof(PALETTEENTRY) * colors] ;
if(lpal == NULL) {
MessageBox(NULL, "メモリの確保に失敗しました", "Test BMP", MB_OK|MB_ICONSTOP) ;
throw exceptBasic() ;
}
PALETTEENTRY *pal = lpal->palPalEntry ;
RGBQUAD *rgb = bmInfo->bmiColors ;
for(short i = 0 ; i < colors ; i ++) {
pal->peRed = rgb->rgbRed ;
pal->peGreen = rgb->rgbGreen ;
pal->peBlue = rgb->rgbBlue ;
pal->peFlags = 0 ;
}
lpal->palVersion = 0x300 ;
lpal->palNumEntries = colors ;
hPal = CreatePalette(lpal) ;
if(hPal == NULL) {
delete [] (char *)lpal ;
MessageBox(NULL, "パレットの作成に失敗しました", "Test BMP", MB_OK|MB_ICONSTOP) ;
throw exceptBasic() ;
}
delete [] (char *)lpal ;

// ビットマップハンドルの作成
HDC hdc ;
hdc = GetDC(hwndMain) ;
if(hdc == NULL) {
MessageBox(NULL, "DCの取得に失敗しました", "Test BMP", MB_OK|MB_ICONSTOP) ;
throw exceptBasic() ;
}
LPVOID imagePtr = (LPVOID)((unsigned long)bmInfo + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * colors) ;
hBitmap = CreateDIBitmap(hdc, (BITMAPINFOHEADER *)bmInfo, CBM_INIT, imagePtr, bmInfo, DIB_RGB_COLORS) ;
ReleaseDC(hwndMain, hdc) ;
if(hBitmap == NULL) {
MessageBox(NULL, "ビットマップハンドルの作成に失敗しました", "Test BMP", MB_OK|MB_ICONSTOP) ;
throw exceptBasic() ;
}

// 後始末
HeapFree(GetProcessHeap(), 0, imageBuf) ;
}
catch(exceptBasic &) {
if(hFile != INVALID_HANDLE_VALUE) {
CloseHandle(hFile) ;
}
if(imageBuf != NULL) {
HeapFree(GetProcessHeap(), 0, imageBuf) ;
}
if(hPal != NULL) {
DeleteObject(hPal) ;
}
if(hBitmap != NULL) {
DeleteObject(hBitmap) ;
}

DestroyWindow(hwndMain) ;
return -1 ;
}

ShowWindow(hwndMain, nCmdshow);
UpdateWindow(hwndMain);

MSG msg ;
while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

DeleteObject(hPal) ;
DeleteObject(hBitmap) ;

return msg.wParam;
}


/********************************************
Window procedure
*********************************************/
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg) {
case WM_PAINT:
{
PAINTSTRUCT ps ;
HDC hdc ;
hdc = BeginPaint(hwnd, &ps) ;
HDC hMemDC ;
hMemDC = CreateCompatibleDC(hdc) ;
SelectObject(hMemDC, hBitmap) ;
HPALETTE hOldPal = SelectPalette(hdc, hPal, FALSE) ;
RealizePalette(hdc) ;
BitBlt(hdc, 0, 0, width, height, hMemDC, 0, 0, SRCCOPY) ;
SelectPalette(hdc, hOldPal, TRUE) ;
DeleteDC(hMemDC) ;
EndPaint(hwnd, &ps) ;
}
return 0 ;

case WM_DESTROY:
PostQuitMessage(0) ;
return 0 ;
}

return DefWindowProc(hwnd, msg, wParam, lParam) ;
}

読みにくい?そりゃそうでしょう。こんだけインデントが無くなってて読みやすい訳がありません。

でもこの長さのソースを元に戻すのは面倒臭い(^_^; のでそのまま掲載しました。

という訳で、元のソースを落とせるようにします。このソースは私がこのアーティクルの為だけに

作った物なので、何かの一部という訳でも無いし、余り実用性は考慮していません。また、処理すべき

メッセージを処理していない部分もありますが、しかし基本的にはこれでOKでしょう。

 

testBMP.CPP

 

 

プログラミングTipsに戻る