第176章 ビットマップリソースをそのまま保存する


今回は、簡単です。 前章ではビットマップリソースをロードする時に BITMAPINFO構造体へのポインタを取得できました。 これがわかれば、ファイルに保存するのは非常に楽です。 BITMAPFILEHEADR構造体に値をセットしてファイルに書き込み、 続けてBITMAPINFO構造体以下を書きこめばできあがりです。

また、第172章でも少し書きましたが BITMAPINFOHEADER構造体のbiSizeImageメンバを求めることは 重要です。そこで今回はbiSizeImageをプログラム的に求める方法も 考えてみます。

// res02.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "リソースの読みこみ...", IDM_READ MENUITEM "名前を付けて保存(&A)...", IDM_SAVE MENUITEM SEPARATOR MENUITEM "終了(&X)", IDM_END END POPUP "表示(&V)" BEGIN MENUITEM "情報表示(&I)", IDM_INFO END END ///////////////////////////////////////////////////////////////////////////// // // Bitmap // MYBMP1 BITMAP DISCARDABLE "my59.BMP" MYBMP2 BITMAP DISCARDABLE "mybmp2.bmp" MYBMP3 BITMAP DISCARDABLE "mybmp3.bmp" MYBMP4 BITMAP DISCARDABLE "bitmap1.bmp" ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYDLG DIALOG DISCARDABLE 0, 0, 130, 53 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "ビットマップリソースの選択" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,70,7,50,14 PUSHBUTTON "キャンセル",IDCANCEL,70,24,50,14 COMBOBOX IDC_COMBO1,7,7,60,79,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP END

メニュー項目に「名前を付けて保存」「表示」が増えました。また、その項目を 選択するとダイアログボックスなどが出て、直ちにコマンドが実行されるわけでは ない項目については「...」を付けてみました。

前章同様MYBMP1, MYBMP2, MYBMP3, MYBMP4にはフルカラー、256色、16色、 モノクロのビットマップを用意して下さい。

// res02.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <windowsx.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); int ReadRes(HWND); int SaveRes(HWND); int ShowInfo(HWND); int GetSaveName(HWND, char *); char szClassName[] = "res02"; //ウィンドウクラス char szBMPName[64]; //ビットマップリソース名 HINSTANCE hInst; HPALETTE hPalette; int nClr; BOOL bLoad = FALSE; int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; hInst = hCurInst; //グローバル変数にコピー if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 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 = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるリソース", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

このへんはいつもと同じです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; HDC hdc; PAINTSTRUCT ps; HRSRC hRsrc; HANDLE hRes; LPBITMAPINFO lpBinfo; char *szBuf; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_READ: ReadRes(hWnd); break; case IDM_SAVE: SaveRes(hWnd); break; case IDM_INFO: ShowInfo(hWnd); break; } break; case WM_PAINT: if (!bLoad) return (DefWindowProc(hWnd, msg, wp, lp)); hdc = BeginPaint(hWnd, &ps); if (nClr) { SelectPalette(hdc, hPalette, FALSE); RealizePalette(hdc); } hRsrc = FindResource(hInst, szBMPName, RT_BITMAP); hRes = LoadResource(hInst, hRsrc); lpBinfo = (LPBITMAPINFO)LockResource(hRes); szBuf = (char *)lpBinfo + sizeof(BITMAPINFOHEADER) + nClr * sizeof(RGBQUAD); SetDIBitsToDevice(hdc, 0, 0, lpBinfo->bmiHeader.biWidth, lpBinfo->bmiHeader.biHeight, 0, 0, 0, lpBinfo->bmiHeader.biHeight, szBuf, lpBinfo, DIB_RGB_COLORS); EndPaint(hWnd, &ps); break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { if (hPalette) DeleteObject(hPalette); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

メニューからIDM_SAVEが選択されたら自作関数SaveResを呼びます。 あとは同じです。

int ReadRes(HWND hWnd) { HANDLE hResource, hPalMem; HRSRC hRsrc; LPBITMAPINFO lpbi; LPLOGPALETTE lplp; int i, id, index; char str[256]; char szCl[5][64] = { "フルカラー", "モノクロ", "16色", "256色", "その他"}; id = DialogBox(hInst, "MYDLG", hWnd, (DLGPROC)MyDlgProc); if (id == IDCANCEL) return -1; hRsrc = FindResource(hInst, szBMPName, RT_BITMAP); if (hRsrc == NULL) { MessageBox(hWnd, "FindResource Error", "Error", MB_OK); return -1; } hResource = LoadResource(hInst, hRsrc); if (hResource == NULL) { MessageBox(hWnd, "LoadResource Error", "Error", MB_OK); return -1; } lpbi = (LPBITMAPINFO)LockResource(hResource); if (lpbi->bmiHeader.biClrUsed != 0) nClr = lpbi->bmiHeader.biClrUsed; else { if (lpbi->bmiHeader.biBitCount == 24) nClr = 0; else nClr = 1 << (lpbi->bmiHeader.biBitCount); } if (nClr) { hPalMem = GlobalAlloc(GHND, sizeof(LOGPALETTE) + nClr * sizeof(PALETTEENTRY)); lplp = (LPLOGPALETTE)GlobalLock(hPalMem); lplp->palNumEntries = nClr; lplp->palVersion = 0x300; for (i = 0; i < nClr; i++) { lplp->palPalEntry[i].peRed = lpbi->bmiColors[i].rgbRed; lplp->palPalEntry[i].peGreen = lpbi->bmiColors[i].rgbGreen; lplp->palPalEntry[i].peBlue = lpbi->bmiColors[i].rgbBlue; lplp->palPalEntry[i].peFlags = NULL; } hPalette = CreatePalette(lplp); GlobalUnlock(hPalMem); GlobalFree(hPalMem); } bLoad = TRUE; switch (nClr) { case 0: index = 0; break; case 2: index = 1; break; case 16: index = 2; break; case 256: index = 3; break; default: index = 4; break; } wsprintf(str, "%sのリソースを読みこみます", szCl[index]); MessageBox(hWnd, str, "ビットマップ情報", MB_OK); InvalidateRect(hWnd, NULL, TRUE); return 0; }

これは、前回と同じです。

LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hCtrl; switch (msg) { case WM_INITDIALOG: hCtrl = GetDlgItem(hDlg, IDC_COMBO1); ComboBox_AddString(hCtrl, "MYBMP1"); ComboBox_AddString(hCtrl, "MYBMP2"); ComboBox_AddString(hCtrl, "MYBMP3"); ComboBox_AddString(hCtrl, "MYBMP4"); if (bLoad) ComboBox_SetText(hCtrl, szBMPName); else ComboBox_SetText(hCtrl, "MYBMP1"); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: ComboBox_GetText(hCtrl, szBMPName, sizeof(szBMPName) - 1); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; }

これは、リソースの読み込みの時リソースを選択するダイアログボックスの プロシージャです。前回と同じです。

int SaveRes(HWND hWnd) { HRSRC hRsrc; HANDLE hResource; LPBITMAPINFO lpbmp_info; HANDLE hF; DWORD dwImgSize, dwPalSize, dwWritten; BITMAPFILEHEADER bmFH; char szSaveName[MAX_PATH]; if (bLoad == FALSE) { MessageBox(hWnd, "リソースがロードされていません", "Error", MB_OK); return -1; } hRsrc = FindResource(hInst, szBMPName, RT_BITMAP); if (hRsrc == NULL) { MessageBox(hWnd, "FindResource Error", "Error", MB_OK); } hResource = LoadResource(hInst, hRsrc); if (hResource == NULL) { MessageBox(hWnd, "LoadResource Error", "Error", MB_OK); return -1; } lpbmp_info = (LPBITMAPINFO)LockResource(hResource); GetSaveName(hWnd, szSaveName); hF = CreateFile(szSaveName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if(hF == NULL) { MessageBox(hWnd, "CreateFile Error", "Error", MB_OK); return -1; } dwPalSize = nClr * sizeof(RGBQUAD); dwImgSize = lpbmp_info->bmiHeader.biSizeImage; //BITMAPFILEHEADER構造体の準備 memset(&bmFH , 0 , sizeof(bmFH)); bmFH.bfType = 0x4d42; bmFH.bfOffBits = dwPalSize + sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER); bmFH.bfSize = dwImgSize + bmFH.bfOffBits; //BITMAPFILEHEADE構造体を書き込む WriteFile(hF, &bmFH, sizeof(bmFH), &dwWritten, NULL); //BITMAPINFOHEADER構造体以下を書き込む WriteFile(hF, lpbmp_info, (DWORD)(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nClr + dwImgSize), &dwWritten, NULL); CloseHandle(hF); return 0; }

FindResource, LoadResource, LockResourceでビットマップリソースの BITMAPINFO構造体のアドレスを取得します。これさえ求まれば ファイルに保存するのは簡単です。

保存する時のファイル名を自作のGetSaveName関数で求めます。

次にBITMAPFILEHEADER構造体の各メンバを埋めて、ファイルに書き込みます。

あとは、BITMAPINFO構造体のアドレスからビットマップデータの最後までを 書きこめば終わりです。

さて、いろいろなところでFindResource, LoadResource, LockResource関数を 呼び出すセットが登場するのでいちいち同じコードを書くのは無駄です。 これを関数にして必要に応じてこの関数を呼び出すように書きなおしてみて下さい。

int ShowInfo(HWND hWnd) { HRSRC hRsrc; HANDLE hResource; LPBITMAPINFO lpbmp_info; DWORD dwSizeImage, dwClrUsed, dwMySizeImage, LnWidth; LONG wx, wy; WORD wPlanes, wBitCount; char str[512]; if (bLoad == FALSE) { MessageBox(hWnd, "リソースがロードされていません", "Error", MB_OK); return -1; } hRsrc = FindResource(hInst, szBMPName, RT_BITMAP); if (hRsrc == NULL) { MessageBox(hWnd, "FindResource Error", "Error", MB_OK); } hResource = LoadResource(hInst, hRsrc); if (hResource == NULL) { MessageBox(hWnd, "LoadResource Error", "Error", MB_OK); return -1; } lpbmp_info = (LPBITMAPINFO)LockResource(hResource); dwSizeImage = lpbmp_info->bmiHeader.biSizeImage; dwClrUsed = lpbmp_info->bmiHeader.biClrUsed; wx = lpbmp_info->bmiHeader.biWidth; wy = lpbmp_info->bmiHeader.biHeight; wPlanes = lpbmp_info->bmiHeader.biPlanes; wBitCount = lpbmp_info->bmiHeader.biBitCount; //自分でSizeImageを計算する if ((((wx * wBitCount) / 8) % 4) == 0) LnWidth = wx * wBitCount / 8; else LnWidth = wx * wBitCount / 8 + (4 - ((wx * wBitCount / 8) % 4)); dwMySizeImage = LnWidth * wy; wsprintf(str, "dwSizeImage = %d, wx = %d, wy = %d\n" "dwClrUsed = %d, wPlanes = %d, wBitCount = %d\n" "dwMySizeImage = %d", dwSizeImage, wx, wy, dwClrUsed, wPlanes, wBitCount, dwMySizeImage); MessageBox(hWnd, str, "情報表示", MB_OK); return 0; }

ここでもBITMAPINFO構造体のアドレスを取得するために一連の FindResource, LoadResource, LockResource呼びたしが行われています。 BITMAPINFO構造体に収められているdwSizeImageと自分で計算した サイズ(dwMySizeImage)を両方表示するようにしました。

まずビットマップの横幅にbiBitCountをかけると横幅のビット数がわかります。 これを8で割るとバイト数がわかります。このバイト数が4の倍数か どうかを調べます。もし、4の倍数であれば幅のバイト数(LnWidth)は

幅のピクセル数*ビットカウント/8

ということになります。4の倍数でない時は4からそのあまりを引いた数を 加えてやります。たとえば15であれば4で割るとあまりは3です。 4−3=1で15に1を加えると4の倍数となります。

幅のバイト数がわかったら高さをかけるとサイズイメージがわかります。

さて、昔からビットマップの幅のバイト数を求める式というのがあります。 これは、次のようなものです。

LnWidth = (((wx * bitcount) + 31)& ~31) >> 3;

なぜこの式で求めることができるのか研究してみて下さい。

int GetSaveName(HWND hWnd, char *szF) { OPENFILENAME ofn; char szSaveN[MAX_PATH]; strcpy(szSaveN, szBMPName); strcat(szSaveN, ".bmp"); strcpy(szF, szSaveN); memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "Bitmap (*.BMP)\0*.BMP\0\0"; ofn.nFilterIndex = 1; ofn.lpstrFile = szF; ofn.nMaxFile = 128; ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; if (GetSaveFileName((LPOPENFILENAME)&ofn)) return 0; return -1; }

セーブするためのファイル名を取得する関数です。 第174章のGetSaveName関数とほぼ同じです。
[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 14/Feb/1999 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。