您的位置:首页 > 编程语言

用VC6.0编写Win32程序加载位图(含工程源代码)

2013-01-18 17:25 387 查看
一、实验原理

具体原理,请参考《数字图像处理编程入门》.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行的文件路径。

// ImageTemplate.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "resource.h"
#include "stdio.h"

#define MAX_LOADSTRING 100
//自己加的宏
#define WIDTHBYTES(i) ((i+31)/32*4)

// Global Variables:
HINSTANCE hInst;                                // current instance
TCHAR szTitle[MAX_LOADSTRING];                                // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];                                // The title bar text
//自己的全局变量
BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;
HPALETTE hPalette;
HBITMAP hBitmap ;
HGLOBAL hImgData;

// Foward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
BOOL LoadBmpFile(HWND hWnd,char *BmpFileName);

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR     lpCmdLine,
int       nCmdShow)
{
// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;

// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_IMAGETEMPLATE, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);

// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}

hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_IMAGETEMPLATE);

// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

return msg.wParam;
}

//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage is only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style            = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc    = (WNDPROC)WndProc;
wcex.cbClsExtra        = 0;
wcex.cbWndExtra        = 0;
wcex.hInstance        = hInstance;
wcex.hIcon            = LoadIcon(hInstance, (LPCTSTR)IDI_IMAGETEMPLATE);
wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName    = (LPCSTR)IDC_IMAGETEMPLATE;
wcex.lpszClassName    = szWindowClass;
wcex.hIconSm        = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);

return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HANDLE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;

hInst = hInstance; // Store instance handle in our global variable

hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

if (!hWnd)
{
return FALSE;
}

ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

return TRUE;
}

//
//  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND    - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY    - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
//    LPSTR szHello = "Fucko";
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
static HDC hMemDC;

switch (message)
{
case WM_COMMAND:
wmId    = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
//    hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
//    MessageBox(hWnd,"Error alloc memory","Error Message",MB_OK|MB_ICONEXCLAMATION);
//
//            RECT rt;
//            GetClientRect(hWnd, &rt);
//            DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
//            EndPaint(hWnd, &ps);
//            break;

hdc = BeginPaint(hWnd,&ps);
LoadBmpFile(hWnd,"e:\\xx.bmp");//这里的e:\\xx.bmp是你的位图路径
if (hBitmap)
{
hMemDC = CreateCompatibleDC(hdc);//建立一个内存设备上下文
if (hPalette)//有调色板
{
//将调色板选入屏幕设备上下文
SelectPalette(hdc,hPalette,FALSE);
//将调色板选入内存设备上下文
SelectPalette(hMemDC,hPalette,FALSE);
RealizePalette(hdc);
}
//将位图选入内存设备上下文
SelectObject(hMemDC,hBitmap);
//显示位图
BitBlt(hdc,0,0,bi.biWidth,bi.biHeight,hMemDC,0,0,SRCCOPY);
//释放内存设备上下文
DeleteDC(hMemDC);
}

//释放屏幕设备上下文
EndPaint(hWnd,&ps);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

// Mesage handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
return TRUE;

case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
break;
}
return FALSE;
}

//自己加入的代码
BOOL LoadBmpFile(HWND hWnd,char *BmpFileName)
{
HFILE hf;//
//
LPBITMAPINFOHEADER lpImgData;
LOGPALETTE *pPal;//
LPRGBQUAD lpRGB;
HPALETTE hPrevPalette;
HDC hDc;
HLOCAL hPal;
DWORD LineBytes;
DWORD ImgSize;
//
DWORD NumColors;
DWORD i;

if((hf=_lopen(BmpFileName,OF_READ))==HFILE_ERROR)
{
MessageBox(hWnd,"File c:\\test.bmp not found!","Error Message",
MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}

//将BITMAPFILEHEADER结构从文件中读出,写入bf中

_lread(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER));

//
_lread(hf,(LPSTR)&bi,sizeof(BITMAPINFOHEADER));

//需要定义一个宏,因为每行字节数必须为4的倍数
//#define WIDTHBYTES(i) ((i+31)/32*4)
//调用WIDTHBYTES(bi.biWidth*bi.biBitCount)即可完成换算

LineBytes=(DWORD)WIDTHBYTES(bi.biWidth*bi.biBitCount);

//ImgSize为实际的图象数据占用的字节数
ImgSize=(DWORD)LineBytes*bi.biHeight;

//NumColors为实际用到的颜色数,即调色板数组中的颜色个数
if(bi.biClrUsed!=0) //如果bi.biClrUsed 不为零,即为实际用到的颜色数
NumColors=(DWORD)bi.biClrUsed;
else //否则,用到的颜色数为2biBitCount
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,"Invalid color numbers!","Error Message", MB_OK|MB_ICONEXCLAMATION);
_lclose(hf);

return FALSE; //关闭文件,返回FALSE
}

if(bf.bfOffBits!=(DWORD)(NumColors*sizeof(RGBQUAD)+
sizeof(BITMAPFILEHEADER)+
sizeof(BITMAPINFOHEADER)))
{

//计算出的偏移量与实际偏移量不符,一定是颜色数出错
MessageBox(hWnd,"Invalid color numbers!","Error Message",MB_OK|MB_ICONEXCLAMATION);
_lclose(hf);
return FALSE; //关闭文件,返回FALSE
}

//分配内存,大小为INFOHEADER和FILEHEADER结构长度+调色板+实际位图
bf.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+
NumColors*sizeof(RGBQUAD)+ImgSize;

if ((hImgData=GlobalAlloc(GHND,(DWORD)
(sizeof(BITMAPINFOHEADER)+
NumColors*sizeof(RGBQUAD)+
ImgSize)))==NULL)
{
//分配内存错误
MessageBox(hWnd,"Error alloc memory","Error Message",MB_OK|MB_ICONEXCLAMATION);
_lclose(hf);
return FALSE;
}

//指针lpImgData指向该内存区
lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);

//文件指针重新定位到BITMAPINFOHEADER开始处
_llseek(hf,sizeof(BITMAPFILEHEADER),SEEK_SET);

//将文件内容读入lpImgData
_hread(hf,(char*)lpImgData,(long)sizeof(BITMAPINFOHEADER)
+(long)NumColors*sizeof(RGBQUAD)+ImgSize);

_lclose(hf);

//使用了调色板
if(NumColors!=0)
{
//为逻辑调色板分配局部内存,大小为逻辑调色板结构长度加
//NumColors个PALETTENTRY

hPal = LocalAlloc(LHND,sizeof(LOGPALETTE)+
NumColors*sizeof(PALETTEENTRY));
//指针pPal指向该内存区
pPal = (LOGPALETTE*)LocalLock(hPal);

//填写逻辑调色板结构的头
pPal->palNumEntries = (WORD)NumColors;
pPal->palVersion = 0x300;

//lpRGB指向的是调色板开始的位置
lpRGB = (LPRGBQUAD)((LPSTR)lpImgData+
(DWORD)sizeof(BITMAPINFOHEADER));

//填写每一项
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是一个全局变量
hPalette = CreatePalette(pPal);

//释放局部内存
LocalUnlock(hPal);
LocalFree(hPal);
}

//获得设备上下文句柄
hDc = GetDC(hWnd);

if (hPalette)
{
//将新的逻辑调色板选入DC,将旧的句柄保存在//hPrevPalette
hPrevPalette = SelectPalette(hDc,hPalette,FALSE);
RealizePalette(hDc);
}

//产生位图句柄
hBitmap= CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpImgData,
(LONG)CBM_INIT,
(LPSTR)lpImgData+sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD),
(LPBITMAPINFO)lpImgData,DIB_RGB_COLORS);

//将原来的调色板(如果有的话)选入设备上下文句柄
if(hPalette&&hPrevPalette)
{
SelectPalette(hDc,hPrevPalette,FALSE);
RealizePalette(hDc);
}

ReleaseDC(hWnd,hDc);
GlobalUnlock(hImgData);//解锁内存区

return TRUE;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: