また、第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; }
メニューからIDM_SAVEが選択されたら自作関数SaveResを呼びます。 あとは同じです。//ウィンドウプロシージャ 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; }
これは、前回と同じです。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; }
FindResource, LoadResource, LockResourceでビットマップリソースの BITMAPINFO構造体のアドレスを取得します。これさえ求まれば ファイルに保存するのは簡単です。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; }
保存する時のファイル名を自作のGetSaveName関数で求めます。
次にBITMAPFILEHEADER構造体の各メンバを埋めて、ファイルに書き込みます。
あとは、BITMAPINFO構造体のアドレスからビットマップデータの最後までを 書きこめば終わりです。
さて、いろいろなところでFindResource, LoadResource, LockResource関数を 呼び出すセットが登場するのでいちいち同じコードを書くのは無駄です。 これを関数にして必要に応じてこの関数を呼び出すように書きなおしてみて下さい。
ここでもBITMAPINFO構造体のアドレスを取得するために一連の FindResource, LoadResource, LockResource呼びたしが行われています。 BITMAPINFO構造体に収められているdwSizeImageと自分で計算した サイズ(dwMySizeImage)を両方表示するようにしました。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; }
まずビットマップの横幅にbiBitCountをかけると横幅のビット数がわかります。 これを8で割るとバイト数がわかります。このバイト数が4の倍数か どうかを調べます。もし、4の倍数であれば幅のバイト数(LnWidth)は
幅のピクセル数*ビットカウント/8
ということになります。4の倍数でない時は4からそのあまりを引いた数を 加えてやります。たとえば15であれば4で割るとあまりは3です。 4−3=1で15に1を加えると4の倍数となります。
幅のバイト数がわかったら高さをかけるとサイズイメージがわかります。
さて、昔からビットマップの幅のバイト数を求める式というのがあります。 これは、次のようなものです。
なぜこの式で求めることができるのか研究してみて下さい。LnWidth = (((wx * bitcount) + 31)& ~31) >> 3;
セーブするためのファイル名を取得する関数です。 第174章のGetSaveName関数とほぼ同じです。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; }
Update 14/Feb/1999 By Y.Kumei