随着计算机技术的广泛应用,各种图文、音频和视频文件大量涌现,不断地冲击着人们的日常生活,从而使用户对应用程序的要求不断地提高,所以程序员在开发项目时也要处理各种的多媒体文件。在本章中就位图文件、WAVE文件、AVI文件和Flash动画进行简单的介绍,其中需要重点掌握的是位图文件的各种操作。通过学习本章,读者可以达到以下学习目的:
♣ 了解位图文件结构;
♣ 掌握位图文件的显示;
♣ 掌握保存位图文件的方法;
♣ 掌握JPEG和GIF文件的显示;
♣ 掌握WAVE资源的播放;
♣ 掌握WAVE文件的播放;
♣ 掌握音量的控制;
♣ 掌握AVI文件的播放;
♣ 掌握Flash文件的播放。
| 11.1 | 图像处理技术
随着图形图像技术的发展,图像在程序界面的中的应用已经称为程序设计的主流,本章就以BMP位图文件为例介绍一下图像处理技术。
11.1.1 位图文件结构分析
位图(BMP)文件是Windows采用的图形文件格式,它常以bmp、dib或rle作为扩展名。位图文件最大的特点是没有经过压缩,它将图像中的每个像素的颜色值进行存储,所以文件本身比较大。位图文件由4个部分组成:位图文件头(bitmap-file header)、位图信息头(bitmap-information header)、彩色表(color table)和定义位图的字节阵列(位图的实际数据)。
1.位图文件头
位图文件头可以用下面的数据结构来描述:
typedef struct tagBITMAPFILEHEADER { // bmfh
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
数据结构中各参数的说明如表11.1所示。
表11.1 参数说明
|
参 数 |
描 述 |
|
bfType |
说明文件的类型,该值必需是0x4D42,即字符串“BM”。OS/2系统中也有位图,如果是OS/2系统此值可以是BA(Bitmap Array),CI(Color Icon)、CP(Color Pointer)、IC(Icon)、PT(Pointer),应用时要注意区分 |
|
bfSize |
说明文件的大小,以字节为单位 |
|
bfReserved1 |
保留,必须设置为0 |
|
bfReserved2 |
保留,必须设置为0 |
|
bfOffBits |
说明从文件头开始到实际的图像数据之间的字节的偏移量。这个参数是非常有用的,因为位图信息头和调色板的长度会根据不同情况而变化,所以用户可以用这个偏移值迅速地从文件中读取到位数据 |
2.位图信息头
位图信息头也可以用下面的数据结构来描述:
typedef struct tagBITMAPINFOHEADER{ // bmih
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
数据结构中各参数的说明如表11.2所示。
表11.2 参数说明
|
参 数 |
描 述 |
|
biSize |
说明BITMAPINFOHEADER结构所需要的字数,这个值并不一定是BITMAPINFOHEADER结构的尺寸,它也可能是sizeof(BITMAPV4HEADER)的值,或是sizeof(BITMAPV5HEADER)的值。BITMAPV4HEADER是Windows 95 和 Windows NT 4.0的位图结构,BITMAPV5HEADER是Windows NT 5.0 和 Windows 98的位图结构,Windows NT以前使用BITMAPINFOHEADER结构。实际使用过程中要根据系统来决定,但绝大多数的位图图像都是BITMAPINFOHEADER结构的 |
|
biWidth |
说明图像的宽度,以像素为单位 |
|
biHeight |
说明图像的高度,以像素为单位。这个值除了用于描述图像的高度之外,它还有另一个用处,就是指明该图像是倒向的位图,还是正向的位图。如果该值是一个正数,说明图像是倒向的;如果该值是一个负数,则说明图像是正向的。大多数的位图文件都是倒向的位图。当高度值是一个负数时,图像将不能被压缩 |
|
biPlanes |
为目标设备说明位面数,其值总是被设为1 |
|
biBitCount |
说明比特数/像素,值为1、4、8、16、24、或32 |
|
biCompression |
说明图像数据压缩的类型。值可以是BI_RGB、BI_RLE8、BI_RLE4和BI_BITFIELDS。BI_RGB说明位图没有压缩;BI_RLE8说明使用每个像素8比特的RLE压缩编码;BI_RLE4说明使用每个像素4比特的RLE压缩编码;BI_BITFIELDS说明每个像素的比特由指定的掩码决定 |
|
biSizeImage |
说明图像的大小,以字节为单位。当用BI_RGB格式时,可设置为0 |
|
biXPelsPerMeter |
说明水平分辨率,用像素/米表示 |
|
biYPelsPerMeter |
说明垂直分辨率,用像素/米表示 |
|
biClrUsed |
说明位图实际使用的彩色表中的颜色索引数,如果设为0,则说明使用所有调色板项 |
|
biClrImportant |
说明对图像显示有重要影响的颜色索引的数目,如果是0,表示都重要 |
3.彩色表
彩色表用下面的数据结构描述:
typedef struct tagRGBQUAD { // rgbq
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
数据结构中各参数的说明如表11.3所示。
表11.3 参数说明
|
参 数 |
描 述 |
|
rgbBlue |
指定蓝色强度 |
|
rgbGreen |
指定绿色强度 |
|
rgbRed |
指定红色强度 |
|
rgbReserved |
保留,设置为0 |
4.位图数据
紧跟在彩色表之后的是图像数据字节阵列。图像的每行由表示图像像素的连续的字节组成,每一行的字节数取决于图像的颜色数目和用像素表示的图像宽度。扫描行是由底向上存储的,这就是说,阵列中的第1个字节表示位图左下角的像素,而最后一个字节表示位图右上角的像素。如果是正向DIB,则扫描行是由顶向下存储的。
11.1.2 将位图文件绘制成对话框背景
在编写应用程序时,为了美化界面,通常会为对话框添加背景,但是使用控件显示图片时,当对话框大小改变时要改变显示图片的大小就很麻烦。为了解决这一问题,可以使用CDC类的StretchBlt函数将图片以对话框的大小绘制到对话框中,这样在对话框的大小改变时只要重新绘制一下图片就可以了。StretchBlt函数用位图的指定矩形部分和设备环境中指定的矩形区域贴图,如果位图的矩形部分同当前设备环境部分大小不一致,则将位图进行压缩或放大。
语法:
BOOL StretchBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop );
StretchBlt函数参数说明如表11.4所示。
表11.4 StretchBlt函数参数说明
|
参 数 |
描 述 |
|
x |
左上角横坐标 |
|
y |
左上角纵坐标 |
|
nWidth |
显示宽度 |
|
nHeight |
显示高度 |
|
pSrcDC |
设备环境指针 |
|
xSrc |
复制位图左上角横坐标 |
|
ySrc |
复制位图左上角纵坐标 |
|
nSrcWidth |
源位图宽度 |
|
nSrcHeight |
源位图高度 |
|
dwRop |
光栅操作类型 |
例如:
将位图文件绘制成对话框背景的程序设计步骤如下。
ch1101实例位置:mr\11\sl\01
(1)创建一个基于对话框的应用程序。
(2)在工作区窗口选择ResourceView视图,右键单击任意节点,在快捷菜单中选择Insert菜单项,向对话框中导入一个BMP位图资源。
(3)在对话框的OnPaint函数中添加代码,将BMP位图资源绘制成对话框背景,代码如下:
void CBackdropDlg::OnPaint()
{
……//此处代码省略
CRect rect;
GetClientRect(&rect);
CDC* pDC = GetDC();
//将位图选进设备场景中
CBitmap cbitmap;
cbitmap.LoadBitmap(IDB_BITMAP1);
CDC memdc;
BITMAP bmp;
memdc.CreateCompatibleDC(pDC);
memdc.SelectObject(&cbitmap);
cbitmap.GetBitmap(&bmp);
pDC->StretchBlt(rect.left,rect.top,rect.Width(),rect.Height(),&memdc,0,0,
bmp.bmWidth,bmp.bmHeight,SRCCOPY);
}
(4)程序运行效果如图11.1所示。

图11.1 将位图文件绘制成对话框背景
11.1.3 将设备上下文转换为位图
将设备上下文转换为位图就是将设备上下文中的内容保存成位图文件。
在前面的介绍中,读者已经了解了位图的文件结构。位图文件由位图文件头、位图信息头、调色板和位图数据组成。位图文件头是固定的结构,位图信息头及调色板可以通过CBitmap的GetBitmap方法获得,而对于位图数据可以GetDIBits方法获得,最后通过CFile对象创建位图文件,并将前面得到的数据写入文件中。
例如:
将设备上下文转换为位图的程序设计步骤如下。
ch1102实例位置:mr\11\sl\02
(1)创建一个基于单文档的应用程序。
(2)在视图类的OnDraw方法中将设备上下文中的内容保存成位图文件。代码如下:
void CCDCBMPView::OnDraw(CDC* pDC)
{
CCDCBMPDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDC->TextOut(100, 30,"弃我去者,昨日之日不可留;乱我心者,");
pDC->TextOut(60, 50,"今日之日多烦扰。长空万里送秋雁,对此可以");
pDC->TextOut(60, 70,"酣高楼。蓬莱文章建安骨,中间小谢又青发。");
pDC->TextOut(60, 90,"具怀逸兴壮思飞,欲上青天揽明月。抽刀断水");
pDC->TextOut(60,110,"水更流,举杯消愁愁更愁。人生在世不称意,");
pDC->TextOut(60,130,"明朝散发弄扁舟。");
CBitmap bmp;
bmp.CreateCompatibleBitmap(pDC,400,200);
CDC memDC;
memDC.CreateCompatibleDC(pDC);
memDC.SelectObject(&bmp);
memDC.BitBlt(0,0,400,200,pDC,0,0,SRCCOPY);
BITMAP bInfo;
bmp.GetBitmap(&bInfo);
int szPanel = 0;
if (bInfo.bmBitsPixel<16)
szPanel = pow(2,bInfo.bmBitsPixel)*sizeof(RGBQUAD);
BITMAPINFO* pBitInfo = (BITMAPINFO*) LocalAlloc(LPTR,sizeof(BITMAPINFO)+szPanel);
pBitInfo->bmiHeader.biBitCount = bInfo.bmBitsPixel;
pBitInfo->bmiHeader.biClrImportant = 0;
pBitInfo->bmiHeader.biCompression = 0;
pBitInfo->bmiHeader.biHeight = bInfo.bmHeight;
pBitInfo->bmiHeader.biPlanes = bInfo.bmPlanes;
pBitInfo->bmiHeader.biSize = sizeof(BITMAPINFO);
pBitInfo->bmiHeader.biSizeImage = bInfo.bmWidthBytes* bInfo.bmHeight;
pBitInfo->bmiHeader.biWidth = bInfo.bmWidth;
pBitInfo->bmiHeader.biXPelsPerMeter = 0;
pBitInfo->bmiHeader.biYPelsPerMeter = 0;
char* pData = new char[bInfo.bmWidthBytes*bInfo.bmHeight];
GetDIBits(memDC.m_hDC,bmp,0,bInfo.bmHeight,pData,pBitInfo,DIB_RGB_COLORS);
BITMAPFILEHEADER FileHeader;
FileHeader.bfType = 0x4d42; //BM
FileHeader.bfReserved1 = 0;
FileHeader.bfReserved2 = 0;
FileHeader.bfSize = sizeof(BITMAPFILEHEADER);
FileHeader.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFO)+szPanel;
CFile file;
file.Open("E:\\bmp.bmp",CFile::modeCreate|CFile::modeReadWrite);
file.WriteHuge(&FileHeader,sizeof(BITMAPFILEHEADER));
file.WriteHuge(pBitInfo,sizeof(BITMAPINFO));
file.WriteHuge(pData,bInfo.bmWidthBytes*bInfo.bmHeight);
file.Close();
LocalFree(pBitInfo);
delete pData;
}
(3)运行程序,保存的位图文件如图11.2所示。

图11.2 将设备上下文转换为位图
11.1.4 在程序中显示JPEG与GIF文件
可以通过IPicture接口来显示JPEG和GIF图像。首先利用CFile对象从文件中读取图像数据到缓冲区中,然后调用CreateStreamOnHGlobal方法在缓冲区中创建一个流对象,接着调用OleLoadPicture方法从流中加载图像信息到IPicture中,最后调用IPicture的Render方法绘制图像。
例如:
显示JPEG与GIF文件的程序设计步骤如下。
ch1103实例位置:mr\11\sl\03
(1)创建一个基于单文档的应用程序。
(2)在单文档中加入ID为IDD_GIF的对话框资源,并为对话框创建新类CViewGif,实现文件是ViewGif.cpp,头文件是ViewGif.h。
(3)设计菜单IDR_MAINFRAME,在菜单“查看”下新建子菜单,ID设为ID_VIEWGIF,Caption为gif。
(4)通过类向导为ID是ID_VIEWGIF的菜单添加消息处理函数,并在该函数中调用对话框。
(5)在对话框中添加OnPaint函数,用于显示GIF文件。代码如下:
void CViewGif::OnPaint()
{
CPaintDC dc(this);
char buf[512];
::GetCurrentDirectory(512,buf); //得到当前程序所在文件夹
strcat(buf,"\\test.gif");
CFileStatus fstatus;
CFile file;
IStream *pStm;
LONG cb;
if(file.Open(buf,CFile::modeRead)&&file.GetStatus(buf,fstatus)&&
((cb = fstatus.m_size) != -1)) //打开要读取的GIF文件
{
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, cb); //分配空间
LPVOID pvData = NULL;
if (hGlobal != NULL)
{
if ((pvData = GlobalLock(hGlobal)) != NULL) //锁定分配的空间
{
file.ReadHuge(pvData, cb);
GlobalUnlock(hGlobal);
CreateStreamOnHGlobal(hGlobal, TRUE, &pStm);
}
}
}
IPicture *pPic;
if(SUCCEEDED(OleLoadPicture(pStm,fstatus.m_size,false,
IID_IPicture,(LPVOID*)&pPic))) //装载图片资源
{
long a,b;
pPic->get_Width(&a);
pPic->get_Height(&b);
CSize sz(a,b);
CDC *pdc = GetDC();
pdc->HIMETRICtoDP(&sz);
CRect rect;
this->GetClientRect(&rect);
pPic->Render(*pdc,rect.left+1,rect.top+1,sz.cx,sz.cy,0,b,a,-b,&rect);
}
}
(6)程序运行结果如图11.3所示。

图11.3 显示JPEG和GIF图像
说明:
显示JPEG同显示GIF的算法一样,只要将打开的文件换成JPEG文件即可。
| 11.2 | 音频处理技术
WAVE格式的音频文件是比较常用的音频文件类型,本章分别介绍了以WAVE资源和WAVE文件两种方式进行声音的播放,并简单介绍了对音量的控制。







