OS Windows 7
コンパイラ Microsoft Visual Studio 2013
ライブラリ WindowsAPI
言語 C++
1・今回は、画像処理ソフトを作ろうと思いWindowsAPI C++ でプログラムを組もうと思い至りました。
最初は、DxLibで画像解析の方をしていたのですが、やはりゲームではないので操作面で不便があり、
WindowsAPIを使ってどうにかプログラミングできないものかと思い始めたのですが、
GetPixelが非常に遅く画像処理をする上で不便だと思い、
何日か調べてみたのですが、分からなかった為、こちらへ質問することとしました。
2・調べている中でネットに落ちているプログラムを見つけ、調べながら自分で足りないところを補強しながら作成しました。
なるべく理解しようと心がけて検索したりして色々調べながらプログラムをしていますが、画像表示部分などネットに落ちてるものを
調べながら書き写したものなので、丸写しになっている部分や少し改良を加えた部分などがあります。
extern TCHAR strFile[MAX_PATH];
extern HANDLE hMem, hMemInfo;
extern LONG wx, wy;
extern char *szBuffer;
extern DWORD dwFileSize, dwOffBits, dwSizeImage, dwClrUsed;
extern LPBITMAPINFO lpbmp_info;
extern BOOL bLoad;
extern WORD wBitCount;
extern HPALETTE hPalette;
extern HWND g_hDlg;
extern HWND g_hDlg2;
extern CCHAR str[256];
extern HINSTANCE g_hInst;
extern char *szBuffer2;
char rgb_RGB[4];
HBITMAP hBMP;
HPALETTE SetPalette(HWND hWnd, LPBITMAPINFOHEADER lpBi);
int MakeBMPInfo(LPBITMAPINFOHEADER lpBi);
int ReadDIB(HWND hWnd)
{
DWORD dwResult;
HANDLE hF,hMem1, hMem2;
LPBITMAPFILEHEADER lpBf;
LPBITMAPINFOHEADER lpBi;
char szFType[3];//
RECT rc;//RECT構造体
int x, y;
int my_x=0, my_y=0;
bLoad = TRUE;
hF = CreateFile(strFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
NULL);
//ファイル取得に失敗するとINVALID_HANDLE_VALUEが返る。 FFFFh
//成功したらファイルハンドルを返す。
if (hF == INVALID_HANDLE_VALUE)
{
MessageBox(hWnd, "ファイルのオープンに失敗しました", "Error", MB_OK);
return -1;
}
//ヒープ領域:動的に確保可能なメモリの領域
//指定されたバイト数のメモリを、ヒープ領域から確保します。
//GHND:移動可能メモリを確保し、その内容を 0 で埋め尽くします。
//成功:移動可能メモリを確保したときは、メモリオブジェクトのハンドルが返ります。
//失敗:0が返る。
hMem1 = GlobalAlloc(GHND, sizeof(BITMAPFILEHEADER));
//メモリ オブジェクトをロックし、メモリ ブロックの先頭ポインタを取得します。移動可能メモリにのみ有効です。
//関数が成功すると、メモリ ブロックの先頭ポインタが返ります。失敗すると、0 が返ります。
lpBf = (LPBITMAPFILEHEADER)GlobalLock(hMem1);
//バッファ:情報を一時的に蓄える記憶領域 キューやスタック
//(LPBI...)lpBf:ビットマップファイルハンドルのポインタのキャスト
//ReadFile:ファイルからデータを読み取る
//ファイルハンドル,データバッファ,読み取り対象のバイト数,読み取ったバイト数,オーバーラップ構造体のバッファ
//&dwResult:1 個の変数へのポインタを指定します。関数から制御が返ると、この変数に、実際に読み取ったバイト数が格納されます。
ReadFile(hF, (LPBITMAPFILEHEADER)lpBf, sizeof(BITMAPFILEHEADER), &dwResult, NULL);
dwFileSize = lpBf->bfSize; //"BM"の2バイト
szFType[0] = LOBYTE(lpBf->bfType);//ローバイト
szFType[1] = HIBYTE(lpBf->bfType);//ハイバイト
szFType[2] = '\0'; //NULL
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);
wx = lpBi->biWidth; //画像の幅
wy = lpBi->biHeight; //画像の高さ
wBitCount = lpBi->biBitCount; //ピクセルあたりの色数。
dwClrUsed = lpBi->biClrUsed; //イメージで使われている色数
dwSizeImage = lpBf->bfSize - lpBf->bfOffBits; //ファイルの先頭からビット配列までのオフセット値
RECT rc1 = { 0, 0, wx, wy };//RECT構造体
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);
//読みこむのはBITMAPINFOHEADER, RGBQUAD, ビットデータ
SetFilePointer(hF, sizeof(BITMAPFILEHEADER), 0, FILE_BEGIN);
ReadFile(hF, szBuffer,
dwSizeImage + dwClrUsed * sizeof(RGBQUAD)+sizeof(BITMAPINFOHEADER),
&dwResult, NULL);
if (dwClrUsed != 0 || wBitCount != 24) {
hPalette = SetPalette(hWnd, lpBi);
}
//BITMAPINFO構造体をセットする
lpbmp_info->bmiHeader = *lpBi;
MakeBMPInfo(lpBi);
GetWindowRect(hWnd, &rc);
x = rc.left;
y = rc.top;
rc.right = x + wx;
rc.bottom = y + wy;
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, TRUE);
AdjustWindowRectEx(&rc1, WS_OVERLAPPEDWINDOW, FALSE, 0);
SetWindowPos(g_hDlg, NULL, WINDOW_X, WINDOW_Y, rc1.right - rc1.left, rc1.bottom - rc1.top, (SWP_NOZORDER | SWP_NOOWNERZORDER));//ダイアログ1のサイズを画像サイズに変更
SetWindowPos(g_hDlg2, NULL, WINDOW_X, WINDOW_Y, rc1.right - rc1.left, rc1.bottom - rc1.top, (SWP_NOZORDER | SWP_NOOWNERZORDER));//ダイアログ1のサイズを画像サイズに変更
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;
}
void RGB_V(HWND hWnd, int x, int y, int *my_R, int *my_G, int *my_B, int *Flag)
{
//毎回この作業を行うのは重い
HBITMAP hBMPOLD;//画像が読まれていない
LPDWORD lpRGB;
HDC hdcBMP;
HDC hDC;
BITMAPINFO BmpInfo;
/*自分で作成したDCには自分で作成したビットマップを
SelectObjectしなければなりません。
それぞれコピー元とコピー先に指定してBitBltなどを実行すると、
デスクトップのビットマップイメージが自分で作成したビットマップに
コピーされます*/
switch (wx == NULL || wy == NULL ? *Flag = -1 : 0)
{
case 0:
hDC = GetDC(hWnd);
// ヘッダ情報の設定
ZeroMemory(&BmpInfo, sizeof(BITMAPINFO));
BmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BmpInfo.bmiHeader.biWidth = wx;//幅
BmpInfo.bmiHeader.biHeight = -wy;//高さ
BmpInfo.bmiHeader.biPlanes = 1;
BmpInfo.bmiHeader.biBitCount = 32;
BmpInfo.bmiHeader.biCompression = BI_RGB;
//*************************************************
hBMP = CreateDIBSection(hDC, &BmpInfo, DIB_RGB_COLORS, (LPVOID*)(&lpRGB), NULL, 0);
hdcBMP = CreateCompatibleDC(hDC);
hBMPOLD = (HBITMAP)SelectObject(hdcBMP, hBMP);
SelectObject(hdcBMP, hBMPOLD);
//*************************************************************************
//問題の部分
GetDIBits(hDC, hBMP, 0, wy,
lpRGB, &BmpInfo, DIB_RGB_COLORS
);
//**************************************************************************
*my_R = (BYTE)(lpRGB[wx * y + x] >> 8 * 2);
*my_G = (BYTE)(lpRGB[wx * y + x] >> 8 * 1);
*my_B = (BYTE)(lpRGB[wx * y + x] >> 8 * 0);
//*************************************************
DeleteObject(hdcBMP);
DeleteObject(hBMP);
*Flag = 0;
ReleaseDC(hWnd, hDC);
break;
}
}
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;
}
どのようにしたらよいでしょうか?
自分では、GetPixelで配列に格納してやってみましたが、読み込みに時間がかかってしまい
非効率でした。
3.(a)何がしたいのか・(b)結局何が知りたいのか
(a)1:メインダイアログに画像を1度表示する
2:画像から全ての画素値を参照する
3:参照した画素値から画像処理の計算を実行
4:メインダイアログのボタンをクリック後、
得られた画像を別のダイアログへ表示
この過程の中で、2の部分の画像を参照する部分、
また、4の画像を表示する部分で使用しているGetPixel、SetPixelが遅い為改善したい。
(b)上記したプログラムの配列の中にビット情報(RGB)を格納し、RGB値を参照する。
この配列の中にビット情報を格納する方法が分かりません。
※ちなみにBitmap構造体の仕組みは理解できたつもりですが、fileheaderやinfoheader
の関数は存在している物の肝心のデータは何処から参照すればよいのか?
SetFilePointerで画像データまでのオフセット値を飛ばし、ReadFileで読み込もうともしましたが
うまくいきませんでした。
4.C言語の知識
C言語の知識としましては、
・配列,関数,構造体,変数,nume,deleat,new,if,for,switchくらいはできます。
・ポインタ(まだ理解出来て日が浅い為、しっかり理解できているか不安ではあります。)
・クラス(こちらの方は、まだ手を付けていません)
クラスの方は、コンソールアプリケーションやDxLibなどの簡単なゲームなどを作るのに、
今ある知識で充分作れた為、まだ手を付けておりません。
どうにも今の自分には知識不足で
解決できそうにない為、どうかご教授願いただけたら幸いです。
どうか宜しくお願い致します。