第173章 ビットマップを反転する


今回は前回作ったプログラムの応用です。 ファイルから読み出したビットマップを左右、上下 もしくは左右上下反転します。また、ウィンドウサイズを 変えるとビットマップもその大きさに合わせて拡大・縮小します。



正常の画像です。



メニューの「表示」「効果」で「左右反転」を選択すると左のように なります。



メニューの「表示」「効果」で「上下反転」を選択すると 左の図のようになります。一見左右も反転されているように 見えますがこれで良いのです。(文字のビットマップを表示してみると よくわかります)



「左右反転」と「上下反転」をすると、このようになります。



作り方は簡単です。 前章までSetDIBitsToDevice関数を使っていたところを StretchDIBits関数を使います。引数も似ています。

int StretchDIBits( HDC hdc, // デバイスコンテキストハンドル int XDest, // 転送先左上隅のX座標 int YDest, // 転送先左上隅のY座標 int nDestWidth, // 転送先横幅 int nDestHeight, // 転送先高さ int XSrc, // 転送元左上隅のX座標 int YSrc, // 転送元左上隅Y座標 int nSrcWidth, // 転送元幅 int nSrcHeight, // 転送元高さ CONST VOID *lpBits, // ビットデータのポインタ CONST BITMAPINFO *lpBitsInfo, // BITMAPINFO構造体のポインタ UINT iUsage, //DIB_PAL_COLORまたはDIB_RGB_COLOR DWORD dwRop // ラスターオペレーション );

転送先左上隅の座標と幅高さをいろいろ変えることにより 拡大・縮小・反転などができます。

iUsageはここではDIB_RGB_COLORを使います。

ラスターオペレーションはSRCCOPYを使います。 なお、ラスターオペレーションについては 第26章で少しだけ解説があります。 では、プログラムを見てみましょう。

// bmpfile04.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "開く(&O)", IDM_OPEN MENUITEM SEPARATOR MENUITEM "終了(&X)", IDM_END END POPUP "表示(&V)" BEGIN MENUITEM "情報表示(&I)", IDM_INFO POPUP "効果(&E)" BEGIN MENUITEM "左右逆転(&M)", IDM_MIRROR MENUITEM "上下逆転(&U)", IDM_UPDOWN END END END

メニュー項目が少し増えました。

// bmpfile04.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); int ReadDIB(HWND, LONG *, LONG *); HPALETTE SetPalette(HWND, LPBITMAPINFOHEADER); int MakeBMPInfo(LPBITMAPINFOHEADER); int DrawBMP(HWND, HDC, int, int, char *); char szClassName[] = "bmpfile04"; //ウィンドウクラス char szFileName[128]; //オープンするビットマップファイル HANDLE hMem, hMemInfo; char *szBuffer; DWORD dwFileSize, dwOffBits, dwSizeImage, dwClrUsed; WORD wBitCount; LPBITMAPINFO lpbmp_info; BOOL bLoad = FALSE; HPALETTE hPalette; BOOL bMirror = FALSE, bUpdown = FALSE;

ReadDIB関数の引数が変わりました。これに伴って グローバル変数のwx, wyはなくなりました。

あたらにDrawBMP関数を作りました。 これは、WM_PAINTメッセージの所で呼ばれます。 その他bMirror, bUpdownなどの変数が増えました。

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; 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, "猫でもわかるBMP",//タイトルバーにこの名前が表示されます 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; OPENFILENAME ofn; HDC hdc; PAINTSTRUCT ps; char str[256]; static LONG wx, wy; static HMENU hMenu, hSub; switch (msg) { case WM_CREATE: hMenu = GetMenu(hWnd); hSub = GetSubMenu(hMenu, 1); break; case WM_INITMENU: if (bUpdown == TRUE) CheckMenuItem(hMenu, IDM_UPDOWN, MF_BYCOMMAND | MF_CHECKED); else CheckMenuItem(hMenu, IDM_UPDOWN, MF_BYCOMMAND | MF_UNCHECKED); if (bMirror == TRUE) CheckMenuItem(hMenu, IDM_MIRROR, MF_BYCOMMAND | MF_CHECKED); else CheckMenuItem(hMenu, IDM_MIRROR, MF_BYCOMMAND | MF_UNCHECKED); break; case WM_PAINT: if (bLoad) { hdc = BeginPaint(hWnd, &ps); if (dwClrUsed) { SelectPalette(hdc, hPalette, FALSE); RealizePalette(hdc); } DrawBMP(hWnd, hdc, wx, wy, szBuffer + dwOffBits - sizeof(BITMAPFILEHEADER)); EndPaint(hWnd, &ps); } else { return (DefWindowProc(hWnd, msg, wp, lp)); } break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_OPEN: 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 = szFileName; ofn.nMaxFile = 128; ofn.Flags = OFN_HIDEREADONLY; if (GetOpenFileName((LPOPENFILENAME)&ofn)) ReadDIB(hWnd, &wx, &wy); break; case IDM_INFO: wsprintf(str, "ファイル名 = %s\n" "ファイルサイズ = %d\n" "BMP幅 = %d, BMP高さ = %d\n" "biBitCount = %d, biClrUsed = %d\n" "biSizeImage = %d, bOffBits = %d", szFileName, dwFileSize, wx, wy, wBitCount, dwClrUsed, dwSizeImage, dwOffBits); MessageBox(hWnd, str, "BITMAP情報", MB_OK); break; case IDM_MIRROR: bMirror = !bMirror; InvalidateRect(hWnd, NULL, TRUE); break; case IDM_UPDOWN: bUpdown = !bUpdown; InvalidateRect(hWnd, NULL, TRUE); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { if (hMem) GlobalFree(hMem); if (hMemInfo) GlobalFree(hMemInfo); if (hPalette) DeleteObject(hPalette); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

WM_CREATEのところでメニューハンドルを取得します。

WM_INITMENUメッセージが来たら、「効果」がどうなっているかで メニュー項目のチェックをつけたりはずしたりします。 CheckMenuItem関数は第68章 に簡単な解説があります。現在ではSetMenuItemInfo関数を使うことが 推奨されていますが、CheckeMenuItem関数のほうが使い方が簡単なため 今でも良く使っています(筆者は)。

WM_PAINTのところではDrawBMP関数を呼んで描画させています。

IDM_MIRRORとIDM_UPDOWNのところをみてください。 これが選択されるたびにbMirror, bUpdownがTRUEになったり FALSEになったりします。トグルスイッチの時良く使う手法です。

int ReadDIB(HWND hWnd, LONG *pwx, LONG *pwy) { DWORD dwResult; HANDLE hF, hMem1, hMem2; LPBITMAPFILEHEADER lpBf; LPBITMAPINFOHEADER lpBi; char szFType[3]; RECT rc; int x, y; bLoad = TRUE; hF = CreateFile(szFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hF == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "ファイルのオープンに失敗しました", "Error", MB_OK); return -1; } hMem1 = GlobalAlloc(GHND, sizeof(BITMAPFILEHEADER)); lpBf = (LPBITMAPFILEHEADER)GlobalLock(hMem1); ReadFile(hF, (LPBITMAPFILEHEADER)lpBf, sizeof(BITMAPFILEHEADER), &dwResult, NULL); dwFileSize = lpBf->bfSize; szFType[0] = LOBYTE(lpBf->bfType); szFType[1] = HIBYTE(lpBf->bfType); szFType[2] = '\0'; dwOffBits = lpBf->bfOffBits; GlobalUnlock(hMem1); if (strcmp(szFType, "BM") != 0) { MessageBox(hWnd, "ビットマップではありません", "Error", MB_OK); GlobalFree(hMem1); CloseHandle(hF); return -1; } hMem2 = GlobalAlloc(GHND, sizeof(BITMAPINFOHEADER)); lpBi = (LPBITMAPINFOHEADER)GlobalLock(hMem2); ReadFile(hF, (LPBITMAPINFOHEADER)lpBi, sizeof(BITMAPINFOHEADER), &dwResult, NULL); *pwx = lpBi->biWidth; *pwy = lpBi->biHeight; wBitCount = lpBi->biBitCount; dwClrUsed = lpBi->biClrUsed; dwSizeImage = lpBf->bfSize - lpBf->bfOffBits; hMemInfo = GlobalAlloc(GHND, sizeof(BITMAPINFO) + dwClrUsed * sizeof(RGBQUAD)); lpbmp_info = (LPBITMAPINFO)GlobalLock(hMemInfo); if (hMem) { if(GlobalFree(hMem)) MessageBox(hWnd, "GlobalFree失敗", "Error", MB_OK); } hMem = GlobalAlloc(GHND, dwFileSize - sizeof(BITMAPFILEHEADER)); szBuffer = (char *)GlobalLock(hMem); if (dwClrUsed != 0 || wBitCount != 24) { hPalette = SetPalette(hWnd, lpBi); } //読みこむのはBITMAPINFOHEADER, RGBQUAD, ビットデータ SetFilePointer(hF, sizeof(BITMAPFILEHEADER), 0, FILE_BEGIN); ReadFile(hF, szBuffer, dwSizeImage + dwClrUsed * sizeof(RGBQUAD) + sizeof(BITMAPINFOHEADER), &dwResult, NULL); //BITMAPINFO構造体をセットする lpbmp_info->bmiHeader = *lpBi; MakeBMPInfo(lpBi); GetWindowRect(hWnd, &rc); x = rc.left; y = rc.top; rc.right = x + *pwx; rc.bottom = y + *pwy; AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, TRUE); MoveWindow(hWnd, x, y, rc.right - rc.left, rc.bottom - rc.top, TRUE); InvalidateRect(hWnd, NULL, TRUE); GlobalUnlock(hMemInfo); GlobalUnlock(hMem); if (GlobalFree(hMem1)) MessageBox(hWnd, "GlobalFree(hMem1)失敗", "Error", MB_OK); GlobalUnlock(hMem2); if (GlobalFree(hMem2)) MessageBox(hWnd, "GlobalFree(hMem2)失敗", "Error", MB_OK); CloseHandle(hF); return 0; }

前章と比べて引数が異なりますが、内容はほとんど同じです。

HPALETTE SetPalette(HWND hWnd, LPBITMAPINFOHEADER lpBi) { LPLOGPALETTE lpPal; LPRGBQUAD lpRGB; HANDLE hPal; WORD i; DWORD dwClrUsed; dwClrUsed = lpBi->biClrUsed; hPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + dwClrUsed * sizeof(PALETTEENTRY)); lpPal = (LPLOGPALETTE)GlobalLock(hPal); lpPal->palVersion = 0x300; lpPal->palNumEntries = (WORD)dwClrUsed; lpRGB = (LPRGBQUAD)((LPSTR)lpBi + lpBi->biSize); for (i = 0; i < dwClrUsed; i++, lpRGB++) { lpPal->palPalEntry[i].peRed = lpRGB->rgbRed; lpPal->palPalEntry[i].peGreen = lpRGB->rgbGreen; lpPal->palPalEntry[i].peBlue = lpRGB->rgbBlue; lpPal->palPalEntry[i].peFlags = 0; } GlobalUnlock(hPal); hPalette = CreatePalette(lpPal); if (hPalette == NULL) { MessageBox(hWnd, "パレット作成失敗", "Error", MB_OK); } GlobalFree(hPal); return hPalette; }

前章と変わりありません。

int MakeBMPInfo(LPBITMAPINFOHEADER lpBi) { LPRGBQUAD lpRGB; int i; lpRGB = (LPRGBQUAD)(szBuffer + sizeof(BITMAPINFOHEADER)); for (i = 0; i < (int)lpBi->biClrUsed; i++) { lpbmp_info->bmiColors[i].rgbBlue = lpRGB->rgbBlue; lpbmp_info->bmiColors[i].rgbGreen = lpRGB->rgbGreen; lpbmp_info->bmiColors[i].rgbRed = lpRGB->rgbRed; lpbmp_info->bmiColors[i].rgbReserved = 0; lpRGB++; } return 0; }

これも前章と変わりありません。

int DrawBMP(HWND hWnd, HDC hdc, int wx, int wy, char *szBuf) { RECT rc; GetClientRect(hWnd, &rc); int A, B, C, D; if (bUpdown == FALSE && bMirror == FALSE) { A = 0; B = 0; C = rc.right; D = rc.bottom; } if (bUpdown == FALSE && bMirror ==TRUE) { A = rc.right; B = 0; C = -rc.right; D = rc.bottom; } if (bUpdown == TRUE && bMirror == FALSE) { A = rc.right; B = rc.bottom; C = -rc.right; D = -rc.bottom; } if (bUpdown == TRUE && bMirror == TRUE) { A = 0; B = rc.bottom; C = rc.right; D = -rc.bottom; } StretchDIBits(hdc, A, B, //転送先座標 C, D, //幅、高さ 0, 0, //転送元座標 wx, wy, //転送元幅、高さ (char *)szBuf,//ビットマップデータ開始のアドレス lpbmp_info, //BITMAPINFO構造体へのポインタ DIB_RGB_COLORS, SRCCOPY); return 0; }

転送先座標とその幅、高さを変化させて反転を行います。

正常表示させるには転送先左上座標を(0,0)にして、幅・高さも クライアント領域の大きさに合わせます。

左右反転表示させるには、転送先左上座標を(クライアント領域の幅,0) として幅をマイナスにします。こうすることによって転送元の左上隅は 転送先の右上隅に来ます。

上下の場合は注意が必要です。プログラムを良く見て研究して下さい。 最初にも書いたように文字を描いたビットマップを表示してみると よくわかります。


[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

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