• ベストアンサー

読み込んだBMPデータの行方

参考書を元にBMPを読み込み、 BYTE*型にデータを移して、画像処理や転送に利用できるようにしたいのですが、 どこに画像データの実体が有るのかがよくわかりません・・。 ---------- static LPBYTE lpDIB = NULL; static LPBITMAPINFO lpbiInfo; static LPDWORD lpPixel; LPBYTE lpBMP; LPBITMAPINFOHEADER lpbiBMPInfo; LPBYTE lpBMPPixel; BYTE* ppp; static int iWidth, iHeight, iLength; int iFileSize; DWORD dwOffset; int i, j; HANDLE fhBMP; DWORD dwRead; HDC hdc; PAINTSTRUCT ps; /*BMP取得*/ //ファイルオープン fhBMP = CreateFile("test.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fhBMP == INVALID_HANDLE_VALUE) { MessageBox(NULL, "test.bmpが見つかりません。", "エラー", MB_OK); return 0; } //ファイルサイズ取得 iFileSize = GetFileSize(fhBMP, NULL); //ファイル読み込みバッファ確保 lpBMP = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, iFileSize); //ファイル読み込み ReadFile(fhBMP, lpBMP, iFileSize, &dwRead, NULL); //ファイルを閉じる CloseHandle(fhBMP); //BMP内のBITMAPINFO取得 lpbiBMPInfo = (LPBITMAPINFOHEADER) (lpBMP + sizeof(BITMAPFILEHEADER)); //先頭からピクセル列までのオフセット取得 dwOffset = *(LPDWORD)(lpBMP + 10); //BMP内ピクセル列の先頭アドレス計算 lpBMPPixel = lpBMP + dwOffset; //ビットマップの大きさ取得 iWidth = lpbiBMPInfo->biWidth; iHeight = lpbiBMPInfo->biHeight; //BMPピクセル列の1ラインの長さを計算 if (iWidth % 4 == 0) { iLength = iWidth * 3; } else { iLength = iWidth * 3 + (4 - (iWidth * 3) % 4); } //DIB用バッファを確保 lpDIB = (LPBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFO) + iWidth * iHeight * 4); //DIB用ポインタ分配 lpbiInfo = (LPBITMAPINFO)lpDIB; lpPixel = (LPDWORD)(lpDIB + sizeof(BITMAPINFO)); //BITMAPINFO設定 lpbiInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); lpbiInfo->bmiHeader.biWidth = iWidth; lpbiInfo->bmiHeader.biHeight = iHeight; lpbiInfo->bmiHeader.biPlanes = 1; lpbiInfo->bmiHeader.biBitCount = 32; lpbiInfo->bmiHeader.biCompression = BI_RGB; //BMP内のピクセル列を32ビット化してコピー for (i = 0;i < iHeight;i++) for (j = 0;j < iWidth; j++) CopyMemory(lpPixel + j + i * iWidth, lpBMPPixel + j * 3 + i * iLength, 3); //ファイル読み込みバッファ解放 HeapFree(GetProcessHeap(), 0, lpBMP); /*描画*/ hdc = BeginPaint(hWnd, &ps); //DIBをウインドウのDCに描画 StretchDIBits(hdc, 0, 0, iWidth, iHeight, 0, 0, iWidth, iHeight, lpPixel, lpbiInfo, DIB_RGB_COLORS,SRCCOPY); EndPaint(hWnd, &ps); ---------- 描画部分のStretchDIBits()を調べて lpPixelに格納されているように思えたのですが、これはただのDWORDですし。 lpBMPPixelだとしても、描画では全く使われていないのが不可解で。 なぜこう(描画にDWORD)なっているのでしょうか? どこに画像データが有るのでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
回答No.3

>どこに画像データが有るのでしょうか? >//ファイルサイズ取得 >iFileSize = GetFileSize(fhBMP, NULL); >//ファイル読み込みバッファ確保 >lpBMP = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, iFileSize); >//ファイル読み込み >ReadFile(fhBMP, lpBMP, iFileSize, &dwRead, NULL); まず「ファイルの中身」が、ヘッダーや、生の画像データごと、まるまま「lpBMPが指しているメモリ」に入って来る。 このメモリはHeapAllocで確保している。 つまり「bmpファイルの中身そのまま」が「lpBMPが指すメモリ」に居る。 >//BMP内ピクセル列の先頭アドレス計算 >lpBMPPixel = lpBMP + dwOffset; で、画像データは、lpBMPからdwOffsetだけ進んだ先にあるので、lpBMPPixelに「ファイルから読み込んだ、生の画像データの先頭のポインタ」を代入している。 >//DIB用バッファを確保 >lpDIB = (LPBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFO) + iWidth * iHeight * 4); 一方、実際に描画に使うデータはDIBデータなので、ヘッダとDIBデータのサイズを求め、そのサイズ分だけのメモリを確保し、確保したメモリの先頭アドレスをlpDIBに代入している。 >//DIB用ポインタ分配 >lpbiInfo = (LPBITMAPINFO)lpDIB; >lpPixel = (LPDWORD)(lpDIB + sizeof(BITMAPINFO)); で、確保したメモリの先頭部分がヘッダ、ヘッダの大きさ分だけ進んだアドレスがDIBピクセルデータなので、それぞれをlpbiInfo、lpPixelに代入している。 次に、lpbiInfoとlpPixelが指すアドレスに「ファイルから読み込んだデータを、DIBデータに合うように変形して、コピー」して、DIBデータを作る必要がある。 それが >//BITMAPINFO設定 >lpbiInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); >lpbiInfo->bmiHeader.biWidth = iWidth; >lpbiInfo->bmiHeader.biHeight = iHeight; >lpbiInfo->bmiHeader.biPlanes = 1; >lpbiInfo->bmiHeader.biBitCount = 32; >lpbiInfo->bmiHeader.biCompression = BI_RGB; > >//BMP内のピクセル列を32ビット化してコピー >for (i = 0;i < iHeight;i++) >for (j = 0;j < iWidth; j++) >CopyMemory(lpPixel + j + i * iWidth, >lpBMPPixel + j * 3 + i * iLength, 3); の部分。 「lpBMPが指す、ファイルの中身」を「lpbiInfoが指すヘッダ、lpBMPPixelが指すDIBピクセルデータ」にコピーし終わったなら、もう「lpBMPが指す、ファイルの中身」は不要なので >HeapFree(GetProcessHeap(), 0, lpBMP); で解放している。 このプログラム、lpDIBが指してるDIBヘッダ+DIBデータのメモリ、確保だけ行って使い終わってから解放してないので、何度も実行するとメモリがリークするので注意。 要は lpbiInfo = (LPBITMAPINFO)lpDIB; lpPixel = (LPDWORD)(lpDIB + sizeof(BITMAPINFO)); が肝心の部分で、 lpbiInfo⇒読み込んだデータのDIBヘッダー lplpPixel⇒読み込んだデータのDIB画像データ って事。 因みに、lpDIBは「DIBヘッダーとDIB画像データの両方を合わせた、確保したメモリのアドレス」を保持していて、不要になったら HeapFree(GetProcessHeap(), 0, lpDIB); で解放しなければならない。

zaxs5968
質問者

お礼

詳しい説明ありがとうございます。 lpPixelはデータそのものではなく、 lpPixelを始点として、そこから画像データが並んでいて それを指しているのですね。 解放についても教えてくださりありがとうございます。 感謝。

その他の回答 (2)

回答No.2

 こんばんは。大分的ハズレな回答をしてしましました・・・。  lpBMPPixelについての事ですが、  //BMP内のピクセル列を32ビット化してコピー  for (i = 0;i < iHeight;i++)   for (j = 0;j < iWidth; j++)    CopyMemory(lpPixel/*こっちにコピーしている*/ + j + i * iWidth, lpBMPPixel/*ココで必要*/ + j * 3 + i * iLength, 3);  の所で使用されています。描画に関連してこないのは、lpPixelにコピーしているからです。  コメントに書いて有る様に、lpBMPPixelは、24ビットの扱いに成っていますから、32ビット型のバッファであるlpPixelに変換コピーして、lpPixelの方を描写に使用するという事です。  なので、このプログラムは読み込んでくるtest.bmpが24ビット型でなければいけない事に成ります。  後ビットフィールドの事ですが、32ビット型ではビットフィールドが無くてもBI_RGBのフラグを立てると描写が出来る様です。  ただ、此の侭32ビット型のビットマップファイルとしてセーブした時、他のソフトがどの様に読み込もうとするかは不明です。

zaxs5968
質問者

お礼

>32ビット型ではビットフィールドが無くてもBI_RGBのフラグを立てると描写が出来る様です。 なるほど・・。 ありがとうございました。

回答No.1

 こんばんは。  LPDWORDに成っているのは、32ビット型のDIBを処理するのに楽だからだと思います。LPBYTEで回した場合、次のピクセルを指すのに4つ分飛ばさないといけません(回し方にもよります)。  lpBMPPixelに読み取り元、lpPixelに書き込み先ではないでしょうか。  但し、其の前に、32ビット型DIBで有るにもかかわらず、ビットフィールドの指定が無い等、何だかパッと見て怪しい様な感じが・・・。  本来BI_RGBではなく、BI_BITFIELDSを指定した上でBITMAPINFOHEADERの直下にRGBQUAD[3]相当の拡張割り当てが無ければいけない筈です。  一応似た様な事例を挙げておきます。  http://oshiete1.goo.ne.jp/qa4364441.html  http://oshiete1.goo.ne.jp/qa4431353.html  どちらにしろ、その参考書は置いて、DIBを取り扱ったサイトを見た方が良いと思います。

zaxs5968
質問者

お礼

ご回答ありがとうございます。