用VC6.0编写Win32程序加载位图(含工程源代码)
一、实验原理
具体原理,请参考《数字图像处理编程入门》.pdf,呵呵。主要讲的是位图和windows的调色板。考虑到大家估计也没空,我就精简一下,用户代码的形式列出来。如下:
/************************************************************************/ 位图文件.bmp文件大体上分为四个部分: 位图文件头BITMAPFILEHEADER 位图信息头BITMAPINFOHEADER 调色板Palette 位图数据ImageData /************************************************************************/ /************************************************************************/ /* WORD 无符号16位整数 DWORD无符号32位整数 */ /************************************************************************/ //BITMAPFILEHEADER
//结构长度固定,为14个字节 typedef struct tagBITMFILEHEADER{ WORD bfType; //指定文件类型,必须是0x424D,及字符串“BM” DWORD bfSize; //指定文件大小,包括这14个字节 WORD bfReserved1; //保留字,不予考虑 WORD bfReserved2; //同上 DWORD bfOffBits; //为从文件头到实际的位图数据的皮衣字节数,即位图文件前三个部分长度和 } BITMAPFILEHEADER; //BITMAPINFOHEADER
//结构长度固定,为40个字节,其中LONG为32位整数 typedef struct tagBITMAPINFOHEADER{ DWORD biSize; //指定结构长度,为40 LONG biWidth; //指定图像的宽度,单位是像素 LONG biHeight; //高度 WORD biPlanes; //必须为1,不考虑 WORD biBitCount; //指定表示颜色时要用到的位数,常用有1(黑白二色图),24(真彩色图) DWORD biCompression; //指定位图是否压缩,有效值为BI_RGB(不压缩),BI_RLE8等 DWORD biSizeImage; //实际的位图数据占用的字节数 LONG biXPelsPerMeter; //指定目标设备的水平分辨率,单位:每米的像素个数 LONG biYPelsPerMeter; //同上 DWORD biClrUser; //本图像实际用到的颜色数。如果该值为0(即真彩色),颜色数为2^bitBitCount DWORD biClrImportant; //图像中重要的颜色数,如果为0,认为所有颜色都是重要的 }BITMAPINFOHEADER; //Palette 调色板,真彩色图像不需要调色板。 //也称作Look up table
//调色板实际上是一个数组(可以查看MSDN),共有biClrUsed个元素(若为0,则有2^bitBitCount个元素)。数组中每个元素的类型都是个 RGBQUAD结构,占4个字节。定义: typedef struct tagRGBQUAD{ BYTE rgbBlue; //该颜色的蓝色分量 BYTE rgbGreen; // BYTE rgbRed; // BYTE rgbReserved; //保留值 }RGBQUAD;
二、上机指导
打开VC++6.0,选择“新建”->“工程”->“win32 application”,然后输入名称,例如xxx,工作区间随意。在向导里面建议选择“一个简单的hello world程序”,点击“完成”。之所以选择简单的hello world 程序,是因为我们可以运行代码并且小范围修改代码,以此来观察win32程序是如何运行的。这里提示一点:
win32程序并不像我们以往学习的C语言一样顺序执行,而是在winproc的消息处理函数里面循环,等待消息触发。大概是这个意思。
三、工程源码
代码我们只需要修改xxx.cpp(即和工程名称相同的那个cpp)即可。该文件完整代码如下,可直接覆盖你的cpp文件。要修改的就是181行的文件路径。
1 // ImageTemplate.cpp : Defines the entry point for the application. 2 // 3 4 #include "stdafx.h" 5 #include "resource.h" 6 #include "stdio.h" 7 8 #define MAX_LOADSTRING 100 9 //自己加的宏 10 #define WIDTHBYTES(i) ((i+31)/32*4) 11 12 // Global Variables: 13 HINSTANCE hInst; // current instance 14 TCHAR szTitle[MAX_LOADSTRING]; // The title bar text 15 TCHAR szWindowClass[MAX_LOADSTRING]; // The title bar text 16 //自己的全局变量 17 BITMAPFILEHEADER bf; 18 BITMAPINFOHEADER bi; 19 HPALETTE hPalette; 20 HBITMAP hBitmap ; 21 HGLOBAL hImgData; 22 23 // Foward declarations of functions included in this code module: 24 ATOM MyRegisterClass(HINSTANCE hInstance); 25 BOOL InitInstance(HINSTANCE, int); 26 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 27 LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); 28 BOOL LoadBmpFile(HWND hWnd,char *BmpFileName); 29 30 int APIENTRY WinMain(HINSTANCE hInstance, 31 HINSTANCE hPrevInstance, 32 LPSTR lpCmdLine, 33 int nCmdShow) 34 { 35 // TODO: Place code here. 36 MSG msg; 37 HACCEL hAccelTable; 38 39 // Initialize global strings 40 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); 41 LoadString(hInstance, IDC_IMAGETEMPLATE, szWindowClass, MAX_LOADSTRING); 42 MyRegisterClass(hInstance); 43 44 // Perform application initialization: 45 if (!InitInstance (hInstance, nCmdShow)) 46 { 47 return FALSE; 48 } 49 50 hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_IMAGETEMPLATE); 51 52 // Main message loop: 53 while (GetMessage(&msg, NULL, 0, 0)) 54 { 55 if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 56 { 57 TranslateMessage(&msg); 58 DispatchMessage(&msg); 59 } 60 } 61 62 return msg.wParam; 63 } 64 65 66 67 // 68 // FUNCTION: MyRegisterClass() 69 // 70 // PURPOSE: Registers the window class. 71 // 72 // COMMENTS: 73 // 74 // This function and its usage is only necessary if you want this code 75 // to be compatible with Win32 systems prior to the 'RegisterClassEx' 76 // function that was added to Windows 95. It is important to call this function 77 // so that the application will get 'well formed' small icons associated 78 // with it. 79 // 80 ATOM MyRegisterClass(HINSTANCE hInstance) 81 { 82 WNDCLASSEX wcex; 83 84 wcex.cbSize = sizeof(WNDCLASSEX); 85 86 wcex.style = CS_HREDRAW | CS_VREDRAW; 87 wcex.lpfnWndProc = (WNDPROC)WndProc; 88 wcex.cbClsExtra = 0; 89 wcex.cbWndExtra = 0; 90 wcex.hInstance = hInstance; 91 wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_IMAGETEMPLATE); 92 wcex.hCursor = LoadCursor(NULL, IDC_ARROW); 93 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 94 wcex.lpszMenuName = (LPCSTR)IDC_IMAGETEMPLATE; 95 wcex.lpszClassName = szWindowClass; 96 wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL); 97 98 return RegisterClassEx(&wcex); 99 } 100 101 // 102 // FUNCTION: InitInstance(HANDLE, int) 103 // 104 // PURPOSE: Saves instance handle and creates main window 105 // 106 // COMMENTS: 107 // 108 // In this function, we save the instance handle in a global variable and 109 // create and display the main program window. 110 // 111 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) 112 { 113 HWND hWnd; 114 115 hInst = hInstance; // Store instance handle in our global variable 116 117 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, 118 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); 119 120 if (!hWnd) 121 { 122 return FALSE; 123 } 124 125 ShowWindow(hWnd, nCmdShow); 126 UpdateWindow(hWnd); 127 128 return TRUE; 129 } 130 131 // 132 // FUNCTION: WndProc(HWND, unsigned, WORD, LONG) 133 // 134 // PURPOSE: Processes messages for the main window. 135 // 136 // WM_COMMAND - process the application menu 137 // WM_PAINT - Paint the main window 138 // WM_DESTROY - post a quit message and return 139 // 140 // 141 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 142 { 143 int wmId, wmEvent; 144 PAINTSTRUCT ps; 145 HDC hdc; 146 TCHAR szHello[MAX_LOADSTRING]; 147 // LPSTR szHello = "Fucko"; 148 LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); 149 static HDC hMemDC; 150 151 switch (message) 152 { 153 case WM_COMMAND: 154 wmId = LOWORD(wParam); 155 wmEvent = HIWORD(wParam); 156 // Parse the menu selections: 157 switch (wmId) 158 { 159 case IDM_ABOUT: 160 DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); 161 break; 162 case IDM_EXIT: 163 DestroyWindow(hWnd); 164 break; 165 default: 166 return DefWindowProc(hWnd, message, wParam, lParam); 167 } 168 break; 169 case WM_PAINT: 170 // hdc = BeginPaint(hWnd, &ps); 171 // TODO: Add any drawing code here... 172 // MessageBox(hWnd,"Error alloc memory","Error Message",MB_OK|MB_ICONEXCLAMATION); 173 // 174 // RECT rt; 175 // GetClientRect(hWnd, &rt); 176 // DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER); 177 // EndPaint(hWnd, &ps); 178 // break; 179 180 hdc = BeginPaint(hWnd,&ps); 181 LoadBmpFile(hWnd,"e:\\xx.bmp");//这里的e:\\xx.bmp是你的位图路径 182 if (hBitmap) 183 { 184 hMemDC = CreateCompatibleDC(hdc);//建立一个内存设备上下文 185 if (hPalette)//有调色板 186 { 187 //将调色板选入屏幕设备上下文 188 SelectPalette(hdc,hPalette,FALSE); 189 //将调色板选入内存设备上下文 190 SelectPalette(hMemDC,hPalette,FALSE); 191 RealizePalette(hdc); 192 } 193 //将位图选入内存设备上下文 194 SelectObject(hMemDC,hBitmap); 195 //显示位图 196 BitBlt(hdc,0,0,bi.biWidth,bi.biHeight,hMemDC,0,0,SRCCOPY); 197 //释放内存设备上下文 198 DeleteDC(hMemDC); 199 } 200 201 //释放屏幕设备上下文 202 EndPaint(hWnd,&ps); 203 break; 204 205 206 case WM_DESTROY: 207 PostQuitMessage(0); 208 break; 209 default: 210 return DefWindowProc(hWnd, message, wParam, lParam); 211 } 212 return 0; 213 } 214 215 // Mesage handler for about box. 216 LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) 217 { 218 switch (message) 219 { 220 case WM_INITDIALOG: 221 return TRUE; 222 223 case WM_COMMAND: 224 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 225 { 226 EndDialog(hDlg, LOWORD(wParam)); 227 return TRUE; 228 } 229 break; 230 } 231 return FALSE; 232 } 233 234 //自己加入的代码 235 BOOL LoadBmpFile(HWND hWnd,char *BmpFileName) 236 { 237 HFILE hf;// 238 // 239 LPBITMAPINFOHEADER lpImgData; 240 LOGPALETTE *pPal;// 241 LPRGBQUAD lpRGB; 242 HPALETTE hPrevPalette; 243 HDC hDc; 244 HLOCAL hPal; 245 DWORD LineBytes; 246 DWORD ImgSize; 247 // 248 DWORD NumColors; 249 DWORD i; 250 251 if((hf=_lopen(BmpFileName,OF_READ))==HFILE_ERROR) 252 { 253 MessageBox(hWnd,"File c:\\test.bmp not found!","Error Message", 254 MB_OK|MB_ICONEXCLAMATION); 255 return FALSE; 256 } 257 258 //将BITMAPFILEHEADER结构从文件中读出,写入bf中 259 260 _lread(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER)); 261 262 // 263 _lread(hf,(LPSTR)&bi,sizeof(BITMAPINFOHEADER)); 264 265 //需要定义一个宏,因为每行字节数必须为4的倍数 266 //#define WIDTHBYTES(i) ((i+31)/32*4) 267 //调用WIDTHBYTES(bi.biWidth*bi.biBitCount)即可完成换算 268 269 LineBytes=(DWORD)WIDTHBYTES(bi.biWidth*bi.biBitCount); 270 271 272 //ImgSize为实际的图象数据占用的字节数 273 ImgSize=(DWORD)LineBytes*bi.biHeight; 274 275 //NumColors为实际用到的颜色数,即调色板数组中的颜色个数 276 if(bi.biClrUsed!=0) //如果bi.biClrUsed 不为零,即为实际用到的颜色数 277 NumColors=(DWORD)bi.biClrUsed; 278 else //否则,用到的颜色数为2biBitCount 279 switch(bi.biBitCount){ 280 case 1: 281 NumColors=2; 282 break; 283 case 4: 284 NumColors=16; 285 break; 286 case 8: 287 NumColors=256; 288 break; 289 case 24: 290 NumColors=0; //对于真彩色图,没用到调色板 291 break; 292 default: //不处理其它的颜色数,认为出错。 293 MessageBox(hWnd,"Invalid color numbers!","Error Message", MB_OK|MB_ICONEXCLAMATION); 294 _lclose(hf); 295 296 return FALSE; //关闭文件,返回FALSE 297 } 298 299 if(bf.bfOffBits!=(DWORD)(NumColors*sizeof(RGBQUAD)+ 300 sizeof(BITMAPFILEHEADER)+ 301 sizeof(BITMAPINFOHEADER))) 302 { 303 304 //计算出的偏移量与实际偏移量不符,一定是颜色数出错 305 MessageBox(hWnd,"Invalid color numbers!","Error Message",MB_OK|MB_ICONEXCLAMATION); 306 _lclose(hf); 307 return FALSE; //关闭文件,返回FALSE 308 } 309 310 //分配内存,大小为INFOHEADER和FILEHEADER结构长度+调色板+实际位图 311 bf.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+ 312 NumColors*sizeof(RGBQUAD)+ImgSize; 313 314 if ((hImgData=GlobalAlloc(GHND,(DWORD) 315 (sizeof(BITMAPINFOHEADER)+ 316 NumColors*sizeof(RGBQUAD)+ 317 ImgSize)))==NULL) 318 { 319 //分配内存错误 320 MessageBox(hWnd,"Error alloc memory","Error Message",MB_OK|MB_ICONEXCLAMATION); 321 _lclose(hf); 322 return FALSE; 323 } 324 325 //指针lpImgData指向该内存区 326 lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData); 327 328 //文件指针重新定位到BITMAPINFOHEADER开始处 329 _llseek(hf,sizeof(BITMAPFILEHEADER),SEEK_SET); 330 331 //将文件内容读入lpImgData 332 _hread(hf,(char*)lpImgData,(long)sizeof(BITMAPINFOHEADER) 333 +(long)NumColors*sizeof(RGBQUAD)+ImgSize); 334 335 _lclose(hf); 336 337 //使用了调色板 338 if(NumColors!=0) 339 { 340 //为逻辑调色板分配局部内存,大小为逻辑调色板结构长度加 341 //NumColors个PALETTENTRY 342 343 hPal = LocalAlloc(LHND,sizeof(LOGPALETTE)+ 344 NumColors*sizeof(PALETTEENTRY)); 345 //指针pPal指向该内存区 346 pPal = (LOGPALETTE*)LocalLock(hPal); 347 348 //填写逻辑调色板结构的头 349 pPal->palNumEntries = (WORD)NumColors; 350 pPal->palVersion = 0x300; 351 352 //lpRGB指向的是调色板开始的位置 353 lpRGB = (LPRGBQUAD)((LPSTR)lpImgData+ 354 (DWORD)sizeof(BITMAPINFOHEADER)); 355 356 //填写每一项 357 for(i=0;i<NumColors;i++){ 358 pPal->palPalEntry[i].peRed = lpRGB->rgbRed; 359 pPal->palPalEntry[i].peGreen = lpRGB->rgbGreen; 360 pPal->palPalEntry[i].peBlue = lpRGB->rgbBlue; 361 pPal->palPalEntry[i].peFlags = (BYTE)0; 362 363 lpRGB++; 364 } 365 366 //产生逻辑调色板,hPalette是一个全局变量 367 hPalette = CreatePalette(pPal); 368 369 //释放局部内存 370 LocalUnlock(hPal); 371 LocalFree(hPal); 372 } 373 374 //获得设备上下文句柄 375 hDc = GetDC(hWnd); 376 377 if (hPalette) 378 { 379 //将新的逻辑调色板选入DC,将旧的句柄保存在//hPrevPalette 380 hPrevPalette = SelectPalette(hDc,hPalette,FALSE); 381 RealizePalette(hDc); 382 } 383 384 //产生位图句柄 385 hBitmap= CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpImgData, 386 (LONG)CBM_INIT, 387 (LPSTR)lpImgData+sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD), 388 (LPBITMAPINFO)lpImgData,DIB_RGB_COLORS); 389 390 //将原来的调色板(如果有的话)选入设备上下文句柄 391 if(hPalette&&hPrevPalette) 392 { 393 SelectPalette(hDc,hPrevPalette,FALSE); 394 RealizePalette(hDc); 395 } 396 397 ReleaseDC(hWnd,hDc); 398 GlobalUnlock(hImgData);//解锁内存区 399 400 return TRUE; 401 402 }