1. 程式人生 > >C語言讀取BMP數字影象

C語言讀取BMP數字影象

// 資料型別說明:
// WORD:16位無符號短整形,佔2個位元組
// DWORD:32位無符號短整形,佔4個位元組
// LONG:有符號32位整形,佔4個位元組
// RGBQUAD:用於定義調色盤陣列元素的型別
// LPBITMAPINFOHEADER:點陣圖資訊頭(BITMAPINFOHEADER)的指標
// LOGPALETTE:定義了一個邏輯調色盤
// LPRGBQUAD:指向RGBQUAD結構的指標
// HPALETTE:調色盤控制代碼
// HDC:裝置控制代碼
// HLOCAL:區域性記憶體控制代碼
// HWND:視窗控制代碼
// HFILE:檔案控制代碼
// HBITMAP:點陣圖控制代碼
// HGLOBAL:表示一個記憶體塊控制代碼,GlobalAlloc分配,GlobalLock讀取
// LPSTR:一種字串資料型別,指向以‘\0’結尾的32位ANSI字元陣列指標
// LPCWSTR:一個紙箱unicode編碼字串的32位指標,指向字串是wchar型,而不是char型

// BITMAPFILEHEADER:點陣圖檔案頭結構體
// BITMAPINFOHEADER:點陣圖資訊頭結構體

//************ 關於檔案的操作:https://blog.csdn.net/virtualdesk/article/details/4379704 ************
// _lopen:以二進位制形式開啟指定的檔案
// lread: 將檔案中的資料讀入記憶體緩衝區
// lwrite:將資料從記憶體緩衝區寫入一個檔案
// lcreat:建立一個檔案

#include <stdio.h>
#include <Windows.h>

#define WIDTHBYTES(i) ((i+31)/32*4)

BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;
HBITMAP hBitmap;
HPALETTE hPalette;
HGLOBAL hImgData;
DWORD NumColors;
DWORD LineBytes; // 每行的位元組數
int xOffset = 0, yOffset = 0;

BOOL LoadBmpFile(HWND hWnd, char *BmpFileName)
{
	HFILE hf;                     // 檔案控制代碼
	LPBITMAPINFOHEADER lpImgData; // 資訊頭指標
	LOGPALETTE *pPal;             // 指向調色版的指標
	LPRGBQUAD lpRGB;              // 指向RGBQUAD結構的指標
	HPALETTE hPrePalette;         // 儲存裝置中調色盤
	HDC hDc;                      // 裝置控制代碼
	HLOCAL hPal;                  // 儲存調色盤的區域性記憶體控制代碼
	DWORD ImgSize;                // 實際的影象資料佔用的位元組數
	DWORD i;

	BmpFileName = "G:\c語言影象處理\T102\T102\0.bmp";

	if ((hf = _lopen(BmpFileName, OF_READ)) == HFILE_ERROR)
	{
		MessageBox(hWnd, (LPCWSTR)"File not found", (LPCWSTR)"ERROR Message", MB_OK | MB_ICONEXCLAMATION);
		return FALSE;
	}
	
	_lread(hf, (LPSTR)&bf, sizeof(BITMAPFILEHEADER)); // 將BITMAPFILEHEADER結構從檔案中讀出,寫到bf中
	_lread(hf, (LPSTR)&bi, sizeof(BITMAPINFOHEADER)); // 將BITMAPINFOHEADER結構從檔案中讀出,寫到bf中
	
	LineBytes = (DWORD)GDI_WIDTHBYTES(bi.biWidth*bi.biBitCount); // 每行的位元組數
	ImgSize = (DWORD)LineBytes*bi.biHeight;          // 實際的影象資料佔用的位元組數
	
	if (bi.biClrUsed != 0)                           // 調色盤陣列中實際的顏色數
		NumColors = (DWORD)bi.biClrUsed;
	else
	{
		switch (bi.biBitCount)
		{
		case 1:
			NumColors = 2;
			break;
		case 4:
			NumColors = 16;
			break;
		case 8:
			NumColors = 256;
			break;
		case 24:
			NumColors = 0;
			break;
		default:
			MessageBox(hWnd, (LPCWSTR)"Invalid Color Numbers", (LPCWSTR)"Error Message", MB_OK | MB_ICONEXCLAMATION);
		_lclose(hf);
		return FALSE;
		}

		if (bf.bfOffBits != (DWORD)(NumColors*sizeof(RGBQUAD)  // 計算出的偏移量與實際的偏移量不符,則顏色數出錯
			+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)))
		{
			MessageBox(hWnd, (LPCWSTR)"Invalid Color Numbers", (LPCWSTR)"Error Message", MB_OK | MB_ICONEXCLAMATION);
			_lclose(hf);
			return FALSE;
		}

		bf.bfSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)
			+NumColors*sizeof(RGBQUAD)+ImgSize; // 分配記憶體,大小為BITMAPINFOHEADER結構長度+調色盤+實際點陣圖

		hImgData = GlobalAlloc(GHND, (DWORD)(sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD)+ImgSize));

		if (hImgData == NULL)
		{
			_lclose(hf);
			return FALSE;
		}

		lpImgData = (LPBITMAPINFOHEADER)GlobalLock(hImgData); // lpImgData指向該記憶體區
		
		_llseek(hf, sizeof(BITMAPFILEHEADER), SEEK_SET); // 檔案指標重新定位到LPBITMAPINFOHEADER開始處

		_hread(hf, (char*)lpImgData, 
			(long)sizeof(BITMAPINFOHEADER)+(long)NumColors*sizeof(RGBQUAD)+ImgSize); // 將檔案內容讀入lpImgData

		_lclose(hf);

		if (NumColors != 0) // 顏色數不為0,說明用到了調色盤
		{
			hPal = LocalAlloc(LHND, sizeof(LOGPALETTE)+NumColors*sizeof(PALETTEENTRY)); // 為邏輯調色盤分配記憶體
			pPal = (LOGPALETTE*)LocalLock(hPal);         // 指標pPal指向記憶體區
			pPal->palNumEntries = NumColors;             // 邏輯調色盤結構頭
			pPal->palVersion = 0x300;
			lpRGB = (LPRGBQUAD)((LPSTR)lpImgData + (DWORD)sizeof(BITMAPINFOHEADER));   // lpRGB指向調色盤開始的位置
			
			for (i = 0; i < NumColors; ++i) // 填寫資料
			{
				pPal->palPalEntry[i].peRed = lpRGB->rgbRed;
				pPal->palPalEntry[i].peGreen = lpRGB->rgbGreen;
				pPal->palPalEntry[i].peBlue = lpRGB->rgbBlue;
				pPal->palPalEntry[i].peFlags = (BYTE)0;
				lpRGB++;
			}
			hPalette = CreatePalette(pPal);  // 產生邏輯調色盤
			LocalUnlock(hPal);  // 釋放區域性記憶體
			LocalFree(hPal);    // 釋放區域性記憶體
		}
		hDc = GetDC(hWnd); 		// 獲取上下文控制代碼

		if (hPalette)
		{
			hPrePalette = SelectPalette(hDc, hPalette, FALSE);//將新的邏輯調色盤選入 DC,將舊的邏輯調色盤控制代碼儲存在//hPrevPalette
			RealizePalette(hDc);
		}

		// 產生點陣圖控制代碼
		hBitmap = CreateDIBitmap(hDc, (LPBITMAPINFOHEADER)lpImgData, (LONG)CBM_INIT, 
			(LPSTR)lpImgData + sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD), (LPBITMAPINFO)lpImgData, DIB_RGB_COLORS);
		
		// 將原來的調色盤(如果有的話)選入裝置上下文控制代碼
		if (hPalette && hPrePalette)
		{
			SelectPalette(hDc, hPrePalette, FALSE);
			RealizePalette(hDc);
		}
		ReleaseDC(hWnd, hDc);    // 釋放裝置上下文
		GlobalUnlock(hImgData);  // 解鎖記憶體區

		return TRUE;
	}
}