用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 }
原文地址:https://www.cnblogs.com/muyun/p/2866767.html