现在的位置: 首页 > 综合 > 正文

VC多媒体编程

2013年08月13日 ⁄ 综合 ⁄ 共 31914字 ⁄ 字号 评论关闭
--  VC多媒体编程


--  Visual C++中基于多文档视窗模型的重叠图象拼接技术
摘要 
  图象拼接是在全景视频系统、地理信息系统等应用中经常遇到的一个问题,本文基于网格匹配的方法对边界部分有重叠的图象提出了一种行之有效的对准算法,并通过平滑因子对图象实现了无缝拼接。并应用文档视窗模型实现了该算法,并完成了位图文件的显示、存储等操作,具有一定的普遍意义。 

关键词: 

    图象拼接,算法,重叠图象,文档视窗,位图文件,图象显示 

文章正文 

一、      多文档视窗模型概述 

MFC的AppWizard可以生成三种类型的应用程序:基于对话框的应用、单文档应用(SDI)和多文档应用(MDI)。三种应用中,以多文档应用(MDI)最为复杂,其功能也最强大。当我们用AppWizard生成一个多文档应用时,系统由CMultiDocTemplate自动生成了一个从Cdocument类继承的文档类,一个从Cview类继承的视窗类,一个从CMDIChildWnd类继承的框架类。当我们每次建立一个新的文档时,程序根据文档模板生成一个新实例,这些我们均可不用关心AppWizard已经自动生成了代码。但如果我们要在程序中使用多个不同的文档类时,则需自己建立文档模板并控制文档实例的建立。假设我们要向一基于多文档的工程MDI中增加一Test的文档。具体步骤如下: 

1、用Clazard建立一个框架类CTestFrame基类选CMDIChildWnd。 

2、用Clazard建立一个文档类CTestDoc基类选CDocument。 

3、用Clazard建立一个文档类CTestView基类选CView。 

4、将三个类的头文件加入应用类CMDIApp中。 

5、创建新文档模板,在CMDIApp::InitInstance()函数中加入如下代码 

   CMultiDocTemplate* pDocTemplate; 

    pDocTemplate = new CMultiDocTemplate( 

       IDR_TESTTYPE, 

       RUNTIME_CLASS(CTestDoc), 

       RUNTIME_CLASS(CTestFrame), 

       RUNTIME_CLASS(CTestView)); 

    AddDocTemplate(pDocTemplate); 

6、定义一菜单项ID号为ID_NEWTEST,利用Clazard将其处理函数加入应用类(或主框架类),在其处理函数CMDIApp::OnNewtest()函数中加入如下代码 

    POSITION curTemplatePos = GetFirstDocTemplatePosition(); 

    while(curTemplatePos != NULL) 

    { 

       //取下一个文档模板指针 

       CDocTemplate* curTemplate =GetNextDocTemplate(curTemplatePos); 

       CString str; 

       curTemplate->GetDocString(str, CDocTemplate::docName);   //取文档名称 

       if(str == _T("Test"))   //判断当前文档文档是否Test类 

       { 

           curTemplate->OpenDocumentFile(NULL); //创建新的文档实例 

           return; 

       } 

    这样我们就建立了一个新的文档类。注意在5中创建文档模板时我们用到了一文档类型资源IDR_TESTTYPE,该资源ID在资源文件中定义如下(未包括图标和菜单的定义): 

STRINGTABLE PRELOAD DISCARDABLE 

BEGIN 

    ………. 

    IDR_TESTTYPE      "//nTest//nTest//n//n//nMDI.Document//nTest Document" 

END 

  文档类型标识包括七个子串,包括窗口标题、文档名称、文件扩展名等。在6中curTemplate->GetDocString(str, CDocTemplate::docName);取的就是第二个子串,文档名称。文档建立之后我们就可以对其进行操作了。当然文档类和视窗类,文档类和主窗口类,以及不同文档类之间进行通信也是较为复杂的,并非几句话就能说清楚,如不熟悉文档视窗的读者请参看其它有关资料。 

二、      重叠图象拼接技术 

1.算法思想 

在实现全景视频(Panoramic Video)系统、地理信息系统(GIS)及其它一些应用的过程中,我们通常会碰到这样的一个问题,就是要把几幅小的图象拼接成一幅大的图象。为了能让计算机自动对准图象我们要求待拼接的图象边界有部分重叠,计算机正是利用这些信息进行匹配对准。匹配算法的总体思想是既要保证对准的精度,又要保证运算量不至过大。这里算法利用了图象的自身特性,既在一般图象中,相邻的象素点的灰度值相差不大。因此,可在第二幅图象的边界取一个网格,然后将网格在第一幅图象上移动,计算所有网格点的两幅图象对应象素点的RGB值的差的平方和。记录最小的值的网格位置,即认为是最佳匹配位置。(如图1)为了减小运算量,我们将匹配分为两个步骤,第一步是粗略匹配,在该阶段网格每次水平或垂直移动一个网格间距。在完成粗略匹配之后,我们在当前最佳匹配点处进行精确匹配,在该阶段以当前最佳匹配点为中心,网格向上下、左右各移动一个小步长。初始步长为粗略拼接时移动步长的一半,即为半个网格间距。不断的与当前最小平方和进行比较,如果比当前值优,就替换当前最佳匹配点。循环进行这个过程每次步长减半,直到水平步长和垂直步长均为0为止。 

2.算法描述 

procedure ImageMatching 

    输入FirstImage; 

    输入SecondImage; 

    //获得两幅图象的大小 

    Height1=GetImageHeight(FirstImage); 

    Height2=GetImageHeight(SecondImage); 

    Width1=GetImageWidth(FirstImage); 
    Width2=GetImageWidth(SecondImage); 

// 从第二幅图象取网格匹配模板 

    SecondImageGrid = GetSecondImageGrid(SecondImage); 

// 粗略匹配,网格在第一幅图象中先从左向右移动,再从下到上移动,每次移动一个网格间距,Step_Width 或Step_Height,当网格移出重叠区域后结束 

y=Heitht1-GridHeight; 

MinValue = MaxInteger; 

While ( y<Height1-OverlapNumber)//当网格移出重叠部分后结束 

       x=Grid_Width/2; //当网格位于第一幅图象的最左边时,A点的横坐标。 

       While ( x<(Width1-Grid_Width/2) ) 

           FirstImageGrid=GetImgaeGrid(FirstImgaeGrid, x, y); 

        differ=CaculateDiff(FirstImgaeGrid, SecondImageGrid);//计算象素值差的平

                               //方和 

           if (differ<MinValue) 

           { 

              BestMatch_x=x; 

              BestMatch_y=y; 

              MinValue = differ; 

           } 

       x= x+Step_width; 

       } 

       y=y-Step_Height; 

}   

//精确匹配 

Step_Width= Step_Width/2; 

Step_Height= Step_Height/2; 

While ( Step_Height>0 & Step_Width>0)//当水平步长和垂直步长均减为零时结束 

    if(Step_Height==0)//当仅有垂直步长减为零时,将其置为1 

       Step_Height=1; 

    If(Step_Width==0) //当仅有水平步长减为零时,将其置为1 

       Step_Width=1; 

temp_x = BestMatch_x; 

temp_y = BestMatch_y; 

    for ( i= -1; i<1; i++) 

       for( j= -1; j<1; j++) 

       { 

           if ((i=0&j!=0)|(i!=0&j=0)) 

           { 

              FirstImageGrid=GetImgaeGrid(FirstImgaeGrid, 

temp_x+i*Step_Width, temp_y +j*Step_Height); 

           differ=CaculateDiff(FirstImgaeGrid, SecondImageGrid); 

       if (differ<MinValue) 

              { 

                  BestMatch_x=x; 

                  BestMatch_y=y; 

                  MinValue = differ; 

           } 

           } 

          } 

       Step_Height = Step_Height /2; 

       Step_Width = Step_Width/2; 
    } 

}  

三、      基于多文挡视窗模型的重叠图象拼接技术 

程序在Visual C++实现过程中有如下一些技术问题需要注意。 

1、 位图文件的读取和显示 

位图文件是一种最简单的图象文件,屏幕图象的每一点对应位图文件的几位数据。现有的标准有1位、4位、8位、24位。24位位图不含颜色表,每个象素用3个字节表示,依次表示RGB空间里的蓝、绿、红的灰度值。每种位图文件都由两部分组成,一部分是文件头和位图信息头,另一部分是图象的位数组。因此要想显示一个位图文件首先要声明一个CFile类实例将文件读入内存,然后根据文件头和位图信息头获得图象的相关信息和位数组的起始地址。调用SetDIBitsToDevice()函数即可把图象显示在屏幕上。 

2、 位图文件中任意象素点颜色值的获取 

要实现图象的处理,访问任意象素点的象素值是必需的操作。在访问位图文件时有两点需要注意,一是图象位数组的存储是按从下到上进行的。也就是说,图象的最底行的数据存在位数组的最开始位置。另一个特点是,图象的每行象素所占的空间是双字的整数倍,不足的用零填充。每行象素的实际存储大小可由以下公式加以计算。 

  WidthBytes=(((biWidth*biBitCount)+31)&~31)>>3            (1) 

  假设位数组的起始指针为lpStartBits屏幕坐标(x,y)在的象素值的指针可用下式计算。 

lpBits=lpStartBits + (WidthBytes*(Height-y-1) + x*biBitCount);            (2) 

  其中WidthBytes为(1)式计算的值,Height为图象的高度。 

3、 不同文档类之间的数据交换的实现 

不同文档类之间的数据交换我们可以通过应用程序类或主窗口类作为媒介进行。在文档类或视窗类可通过AfxGetApp()或AfxGetMainWnd()获得应用类和主窗口类的指针,在应用类和主窗口类则可以通过获得文档模板来获得文档类的指针来访问文档类的数据。这样我们可以通过应用类或主窗口类的成员变量进行数据交换了。 

4、 图象的平滑连接 

当找到最佳匹配点后,随后的工作将是把两幅图象合成一幅图象。对于重叠部分,我们如果只是简单的取第一幅图象或第二幅图象的数据,会造成图象的模糊和明显的边界,这是不能容忍的。即使取两幅图象的平均值,效果也不能令人满意。为了能使拼接区域平滑,保证图象质量,我们采用了渐入渐出的方法,即在重叠部分由第一幅图象慢慢过渡到第二幅图象,很自然我们可以想到设一渐变因子为0<d<1,对应的前后两幅图象为image1、image2,结果为image3,则image3=d*image1+(1-d)*imge2其中d的值由1渐变到0,它与该点距重叠边界的距离有关。 

四、      多文挡视窗模型的重叠图象拼接程序框架 

1. 程序构成 

程序除应用类、主窗口类以外,还包括CFristImageDoc, CSecondImageDoc, CThirdImageDoc类用来保存第一幅、第二副以及拼接后图象的数据。还有与其相连的文档类和框架类。 

2. 程序流程 

程序的主要工作在应用类中完成,首先打开第一幅图象,图象的显示等操作由CFirstImageDoc 类和与其相关的视窗类及框架类完成,并将图象的位数组指针和图象大小传给应用类成员变量。再打开第二幅图象同样完成显示等操作,也将位数组指针和图象大小传给应用类成员变量。在应用类中完成图象的匹配对准工作,最后实现图象的合成。将合成后的图象传给CThirdImageDoc类进行显示当用户对拼接结果基本满意后,可以选择平滑连接将两幅图象平滑的连接起来。用户可将最后的结果保存成bmp文件。 

五、      总结 

图象拼接中,图象对准是前提和关键,程序基于网格匹配的方法实现了图象对准,应用了交互技术让用户可以对网格点的多少,网格间距大小,均可调整,还允许用户输入重叠范围使拼接过程有的放矢,缺省是较小图象高度的1/3。该程序实现了对位图图象文件的各种操作,具有普遍意义,还引入了不同文档类型的多文档视窗模型,并在不同文档类间实现了数据交换,具有一定的实用价值。为了使程序简单易用,只实现了对24位位图的拼接,也未提供垂直方向的拼接,只提供了水平方向的拼接。如要对不符合条件的图象进行拼接我们只需在Windows提供的画笔中将图象转化一下即可


--  在VC下显示JPEG、GIF格式图像的一种简便方法
一、 引言 

JPEG图像压缩标准随然是一种有损图像压缩标准,但由于人眼视觉的不敏感,经压缩后的画质基本没有发生变化,很快便以较高的压缩率得到了广泛的认可。GIF格式虽然仅支持256色但它对于颜色较少的图像有着很高的压缩率,甚至超过JPEG标准,也得到了广泛的认同。但作为众多程序员的一个重要的开发工具--Microsoft Visual C++ 6.0的MFC库却仅对没有经过任何压缩的BMP位图文件有着良好的支持,可以读取、显示、存储甚至在内存中创建一块内存位图。由于BMP格式的图像没有经过任何的压缩,不论是作为程序的外部文件,还是作为程序的内部资源都要占据大量的空间,尤其是后者会大大增加可执行文件的长度。可以看出,如果能用经过压缩、具有较好的压缩率的JPEG或GIF格式的图像来取代BMP文件在VC中的应用,无疑还是很有吸引力的。 
二、 设计思路 
虽然有一些操作、处理JPEG、GIF等其他格式图像的Active X控件,但总的来说使用起来并不太方便,笔者经过实验摸索,总结出了一种借助于COM接口的OLE方法来实现上述功能的一种简便方法,现介绍如下以飨广大读者: 
下面我们要使用IPicture 的COM接口,有必要对该图像接口做些了解:该接口主要管理图像对象及其属性,图像对象为位图、图标和图元等提供一种与语言无关的抽象。和标准的字体对象一样,系统也提供了对图像对象的标准实现。其主要的接口是IPicture和IPictureDisp,后者是由IDispatch接口派生以便通过自动化对图像的属性进行访问。图像对象也支持外部接口IPropertyNotifySink,以便用户能在图像属性发生改变时作出决定。图像对象也支持IPersistStream接口,所以它能从一个IStream接口的实例对象保存、装载自己,而IStream接口也支持对流对象的数据读写。 
我们可以用函数OleLoadPicture从包含有图像数据的流中装载图像。该函数简化了基于流的图像对象的创建过程,可以创建一个新的图像对象并且用流中的内容对它进行初始化。其函数原型为: 
STDAPI OleLoadPicture( IStream * pStream, //指向包含有图像数据的流的指针LONG lSize, //从流中读取的字节数BOOL fRunmode, //图像属性对应的初值REFIID riid, //涉及到的接口标识,描述要返回的接口指针的类型VOID ppvObj // 在rrid中用到的接口指针变量的地址); 
三、 具体的实现 
在显示图像之前,首先要获取到图像文件的存放路径,这里采用标准的文件打开对话框来选取图像文件,文件名存放在CString型的变量m_sPath中: 
CFileDialog dlg(TRUE,"jpg","*.jpg", 
OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, 
"JPEG文件(*.jpg)|*.jpg|GIF文件(*.gif)|*.gif||",NULL); 
if(dlg.DoModal()==IDOK) 

m_sPath=dlg.GetPathName(); 
Invalidate(); 

为简单计,图形显示的代码直接在视类中的OnDraw中编写,首先打开文件并判断文件的可用性,并把文件内容放到流接口IStream的对象pStm中: 
IStream *pStm; 
CFileStatus fstatus; 
CFile file; 
LONG cb; 
…… 
if (file.Open(m_Path,CFile::modeRead)&&file.GetStatus(m_Path,fstatus)&& ((cb = fstatus.m_size) != -1)) 

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); 



然后,就直接调用OleLoadPicture函数从流中装载图像: 
IPicture *pPic; 
…… 
OleLoadPicture(pStm,fstatus.m_size,TRUE,IID_IPicture,(LPVOID*)&pPic)); 
由于该函数有时会导致失败,所以应当用SUCCEEDED宏来做一些适当的保护工作,只有在数据装载成功的前提下才能继续下面的图像显示工作: 
if(SUCCEEDED(OleLoadPicture(pStm,fstatus.m_size,TRUE,IID_IPicture,(LPVOID*)&pPic))) 

OLE_XSIZE_HIMETRIC hmWidth; 
OLE_YSIZE_HIMETRIC hmHeight; 
pPic->get_Width(&hmWidth); 
pPic->get_Height(&hmHeight); 
double fX,fY; 
…… 
fX = (double)pDC->GetDeviceCaps(HORZRES)*(double)hmWidth/((double)pDC->GetDeviceCaps(HORZSIZE)*100.0); 
fY = (double)pDC->GetDeviceCaps(VERTRES)*(double)hmHeight/((double)pDC->GetDeviceCaps(VERTSIZE)*100.0); 
if(FAILED(pPic->Render(*pDC,0,0,(DWORD)fX,(DWORD)fY,0,hmHeight,hmWidth,-hmHeight,NULL))) 
AfxMessageBox("渲染图像失败!"); 
pPic->Release(); 

else 
AfxMessageBox("从流中装载图像失败!"); 

其中,显示工作主要是由IPicture接口对象的Render函数来完成的,该函数主要用来将图片的指定部分画到指定的设备环境的指定位置。原型如下: 
HRESULT Render( HDC hdc, //渲染图像用的设备环境句柄 
long x, //在hdc上的水平坐标 
long y, //在hdc上的垂直坐标 
long cx, //图像宽度 
long cy, //图像高度 
OLE_XPOS_HIMETRIC xSrc, //在源图像上的水平偏移 
OLE_YPOS_HIMETRIC ySrc, //在源图像上的垂直偏移 
OLE_XSIZE_HIMETRIC cxSrc,//在源图像上水平拷贝的数量 
OLE_YSIZE_HIMETRIC cySrc,//在源图像上垂直拷贝的数量 
LPCRECT prcWBounds //指向目标图元设备环境句柄的指针); 
小结:到此为止,通过上述代码已经能够在程序的客户区内显示JPEG、GIF等标准的图像了,但对于有多帧图片(即有动画)的GIF格式的图像,目前还只能显示第一帧,如要完整的显示GIF 动画的全过程,还需要外部Active X控件的支持。


--  Visual C++实现Flash动画播放
摘要: 本文通过在VC中将外格式文件内嵌为VC的内部资源,使其在程序运行过程中从资源动态释放到临时文件,从而实现VC对Flash动画的播放。 

   引言 

   Flash动画由于可以很方便地把用户的想象通过动画显现出来,使原本只属于专业制作人员的动画制作变的异乎寻常的快捷、方便。由于Flash制作的动画在层次、内容、表现形式等诸多方面均比较出色,因此在网络上得到迅猛的发展,更有不少厂商用Flash在互联网上做起了广告和产品演示,效果丝毫不比视频的差,而体积则要小的多。Flash不仅在网络上有广泛的应用,在普通的应用程序中也可以借助Flash实现一些VC、Delphi等编程语言所难以实现的特效,比如在一些演示版的程序中完全可以将程序运行前的闪屏用Flash来制作。本文下面将通过对内嵌资源的动态释放来实现VC对Flash动画的播放,并给出了部分实现代码。 

   内嵌资源的动态释放 

   Flash动画在此是作为程序的一个模块,虽然也可以以文件的形式作为一个外部资源来使用,但为了避免因外部模块遗失而造成程序的非正常运行,可将由Flash 5.0预先制作好格式的文件以资源的形式打包到应用程序中去,而在程序运行时再将其从资源恢复到文件,使用完毕再通过程序将其从磁盘删除。 

   在导入资源时由格式文件并非VC的标准资源,所以在导入时需要在"Resource type"栏指定资源类型",特别需要注意的是在此必须要包含引号。加入到资源后可以通过资源视图看到导入资源是以二进制形式保存的,一但加入就不能再通过资源视图对其进行编辑了。 

   在使用资源前首先要将其动态从应用程序中释放到文件中才可对资源做进一步的使用。可先通过宏MAKEINTRESOURCE()将资源标识号IDR转换成字符串Name,再分别通过FindResource()、LoadResource()函数查找、装载该资源到内存: 

CString Type="; 
HRSRC res=FindResource (NULL,Name,Type); 
HGLOBAL gl=LoadResource (NULL,res); 

   当资源加载到内存后,还要通过对资源内存的锁定来返回指向资源内存的地址的指针,并籍此实现资源从内存到磁盘的保存,至于存盘的操作则由文件函数CreateFile()、和WriteFile()来完成: 

LPVOID lp=LockResource(gl); //返回指向资源内存的地址的指针。 
CString filename="Temp"; //保存的临时文件名 
// CREATE_ALWAYS为不管文件存不存在都产生新文件。 
fp= CreateFile(filename ,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL); 
DWORD a; 
//sizeofResource 得到资源文件的大小 
if (!WriteFile (fp,lp,SizeofResource (NULL,res),&a,NULL)) 
return false; 
CloseHandle (fp); //关闭句柄 
FreeResource (gl); //释放内存 

   通过上述代码,可资源从应用程序中提取并释放到临时文件Temp中,在此后只对此临时文件操作,与程序内嵌资源无关。 

   Flash动画的播放 

  格式的Flash动画通常主要应用在网页上,也就是说IE浏览器本身可以支持Flash动画的播放。这样就不必再单独编写用于播文件的代码,从而大大减少编程的工作量。在VC ++ 6.0中新增了一个从CView派生的、用于处理网页的视类CHtmlView,由于该类是以Internet Explorer为后台支持,因此在创建工程时只需在最后一步指定视类从CHtmlView派生就可以使程序不编一行代码而具备IE浏览器的网页显示能力。 

   程序刚生成的时候缺省的连接主页是为微软公司的主页,需要对此修改,使程序在执行时立即显示刚才提取出来的Flash临时文件Temp。显示缺省主页的代码是在视类的初始化函数中进行的: 

void CEmbedModuleView::OnInitialUpdate() 

CHtmlView::OnInitialUpdate(); 
Navigate2(_T("http://www.microsoft.com"),NULL,NULL); 

   显然要将Navigate2()函数的第一个参数改成Temp的存放路径。刚才在释放资源到文件时并没有指定绝对路径,因此释放出来的资源文件应当和应用程序处于同一目录。但是在此处如果不写明绝对路径是无法显示该临时文件的。获取该临时文件的绝对路径可用如下方法实现:先获取应用程序本身的绝对路径,然后去处应用程序全名(程序名和扩展名)此时得到的是应用程序和临时文件所处文件夹的路径,最后只需在此基础上加上临时文件的文件名Temp即可得到临时文件的全路径。下面是实现的主要代码: 

//获取应用程序的全路径 
char exeFullPath[MAX_PATH]; 
GetModuleFileName(NULL,exeFullPath,MAX_PATH); 
//将其格式化为字符串 
m_TempFile.Format("%s",exeFullPath); 
//去掉应用程序的全名(15为应用程序文件全名的长度) 
exeFullPath[m_TempFile.GetLength()-15]=/'/'; 
//得到应用程序所在路径 
m_TempFile.Format("%s",exeFullPath); 
//得到临时文件的全路径 
m_TempFile+="Temp"; 

   最后将得到的临时文件的全路径m_TempFile作为参数传递给Navigate2()即可在程序运行时把Flash动画作为主页而显示(如上图所示)。 

   由于临时文件Temp是在程序运行过程中从应用程序的资源中提取出来的,因此在程序退出之前需要将其删除。一般是在消息WM_DESTORY的响应函数里通过DeleteFile()函数来加以实现的。 

   小结 

   本文通过对CHtmlView和内嵌资源的动态释放实现了Flash动画在VC程序中的播放,并对资源的动态释放作了较为清晰的描述。通过类似的方法,可以将动态链接库、HTML文件等程序模块作为资源嵌入其中,在使用时再动态释放到临时文件,这样可有效避免文件模块过多时的杂乱以及程序模块丢失导致程序非正常运行等情况的发生。本文所述程序在Windows 98下,由Microsoft Visual C++ 6.0编译通过。Flash动画由 Macromedia Flash 5.0制作,所需浏览器支持为Internet
Explorer 6.0。


--  用RealPlayer控件制作的播放器
本文介绍如何插入RealPlay控件实现媒体文件的播放,代码运行效果图如左: 
下面简要介绍一下具体实现步骤: 

一、建立基于对话框的程序 

二、在对话框内添加RealPlayer G2 control的ActiveX控件 
(工程->添加工程->compontent and controls->registed ActiveX controls )。 

三、在对话框内添加源程序内所示的按钮和静态文本 分别用于控制打开播放等控制及显示歌曲信息 其ID号如源程序 

四、用MFC映射各按钮消息 

void CSunapplerealplayerDlg::OnOpen() 

char szFileFilter[]= 

    "RM File(*.rm)|*.rm|" 

        "Mp3 File(*.mp3)|*.mp3|" 

        "MPEG File(*.mpeg)|*.mpeg|" 

        "Media File(*.asf)|*.asf|" 

        "Video File(*.dat)|*.dat|" 

        "MPGA File(*.mpga)|*.mpga|" 

        "Wave File(*.wav)|*.wav|" 

        "AVI File(*.avi)|*.avi|" 

        "Movie File(*.mov)|*.mov|" 

        "Mid File(*.mid;*,rmi)|*.mid;*.rmi|" 

        "Wma File(*.wma)|*.wma|" 

        "All File(*.*)|*.*||"; 

    CFileDialog dlg(TRUE,NULL,NULL,OFN_HIDEREADONLY,szFileFilter); 

    if(dlg.DoModal()==IDOK){ 

        CString PathName=dlg.GetPathName(); 

        PathName.MakeUpper(); 

        m_player->SetSource(PathName); 

        m_player->DoPlay(); 

        SetDlgItemText(IDC_STATIC1,m_player->GetAuthor()); 

      SetDlgItemText(IDC_STATIC2,m_player->GetTitle()); 

      SetDlgItemText(IDC_COPYRIGHT,m_player->GetCopyright()); 

      SetDlgItemText(IDC_SOURCE,m_player->GetSource()); 

    }    

void CSunapplerealplayerDlg::OnPlay() 

{   

    SetDlgItemText(IDC_STATIC1,m_player->GetAuthor()); 

    SetDlgItemText(IDC_STATIC2,m_player->GetTitle()); 

    SetDlgItemText(IDC_COPYRIGHT,m_player->GetCopyright()); 

    SetDlgItemText(IDC_SOURCE,m_player->GetSource()); 

  m_player->DoPlay(); 

     

    UpdateData(false); 

    SetTimer(1,20,NULL); 

void CSunapplerealplayerDlg::OnTimer(UINT nIDEvent) 

  if(0&&isRepeat) 

        m_player->DoPlay(); 

    CDialog::OnTimer(nIDEvent); 

void CSunapplerealplayerDlg::OnClose() 

    ///////添加此代码时不要忘了在stdafx.h开头处添加前两行 

    AnimateWindow(GetSafeHwnd(),1000,AW_HIDE|AW_BLEND); 

    KillTimer(0); 

//////////////////////////////////// 

//此处采用DestroyWindow关闭窗口

//多谢杜修杏 老师指点 

/////////////////////////////////// 

    this->DestroyWindow(); 

     

void CSunapplerealplayerDlg::OnFullscreen() 

     m_player->DoPause(); 

     m_player->SetFullScreen(); 

     m_player->DoPlay(); 

void CSunapplerealplayerDlg::OnMp3down() 

    ShellExecute(NULL,_T("open"),"http://sunapple.51.net",NULL,NULL,TRUE); 

     

void CSunapplerealplayerDlg::OnPause() 

    m_player->DoPause(); 

void CSunapplerealplayerDlg::OnStop() 

    m_player->DoStop(); 

    KillTimer(0); 

void CSunapplerealplayerDlg::OnRepeat() 

{  

  m_player->SetLoop(true); 

    if(isRepeat){ 

        isRepeat=FALSE; 

        SetDlgItemText(IDC_REPEAT,"循环"); 

  } 

  else 

  { 

      isRepeat=TRUE; 

      SetDlgItemText(IDC_REPEAT,"正常"); 

  } 

void CSunapplerealplayerDlg::OnLower() 

    // TOD Add your control notification handler code here 

    short volume=m_player->GetVolume(); 

    m_player->DoPause(); 

    m_player->SetVolume(volume-100); 

    m_player->DoPlay(); 

void CSunapplerealplayerDlg::OnUpper() 

    // TOD Add your control notification handler code here 

    short volume=m_player->GetVolume(); 

    m_player->DoPause(); 

    m_player->SetVolume(volume+100); 

    m_player->DoPlay(); 

void CSunapplerealplayerDlg::OnFloat() 

    //  TOD Add your command handler code here 

    ShellExecute(NULL,_T("open"),"http://sunapple.51.net",NULL,NULL,TRUE); 

void CSunapplerealplayerDlg::OnPetroleum() 

    // TOD Add your command handler code here 

    ShellExecute(NULL,_T("open"),"http://www.hdpu.edu.cn",NULL,NULL,TRUE); 

五、映射WM_CTLCOLOR消息,用于控制文本显示的颜色 

HBRUSH CSunapplerealplayerDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 

    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); 

     

  if(nCtlColor==CTLCOLOR_STATIC) 

        if(pWnd.GetDlgCtrlID()==IDC_VOLUME) 

        { 

            pDC.SetTextColor(RGB(165,182,222)); 

            pDC.SetBkMode(TRANSPARENT); 

            return (HBRUSH) m_brush.GetSafeHandle(); 
        } 

        if(pWnd.GetDlgCtrlID()==IDC_STATIC1||pWnd.GetDlgCtrlID()==IDC_STATIC2

        ||pWnd.GetDlgCtrlID()==IDC_SOURCE||pWnd.GetDlgCtrlID()==IDC_COPYRIGHT)

        { 

            pDC.SetTextColor(RGB(0,0,255)); 

            pDC.SetBkMode(TRANSPARENT); 

            return (HBRUSH) m_brush.GetSafeHandle(); 

        } 

        if(pWnd.GetDlgCtrlID()==IDC_STATIC||pWnd.GetDlgCtrlID()==IDC_INFO)

        { 

     pDC.SetTextColor(RGB(255,0,0)); 

            pDC.SetBkMode(TRANSPARENT); 

            return (HBRUSH) m_brush.GetSafeHandle(); 

        } 

    // TOD Return a different brush if the default is not desired 

    return hbr; 

六、在APP类里的initInstance()里添加下面函数改变对话框背景。 

SetDialogBkColor(RGB(206,227,99)); 

七、为了美观我们的外形可引入CButtonXP类,将个按钮类型设置为CButtonXP 

好了,应该大功告成了!调试一下吧! 
本程序在调试过程中要多谢杜修杏老师的指点


--  Visual C++编程控制鼠标
鼠标是现在计算机的标准配置,很多软件都有控制鼠标的功能,比如,有的保密软件可以使鼠标移动限制在一定范围以内,有的可以模拟鼠标的点击,有的可以使鼠标自己移动。要实现以上的功能,必须使用Windows的API函数。 

   我们以下面的程序例子,来说明如何控制鼠标。我们使用Visual C++6.0来写这个程序。打开Visual C++6.0,使用MFC AppWizard新建1个基于对话框的工程,工程名为Mouse,在对话框上加上2个button控件,一个标题为"控制鼠标移动范围",另外1个的标题是"释放鼠标"在MFC Clazard中添加两个当我们使用鼠标单击这两个控件时响应的函数,标题为"控制鼠标移动范围"的控件的响应函数代码为: 
//首先使用GetWindowRect获得这个程序窗口的范围 
CRect rect;
this->GetWindowRect(&rect);
//然后使用ClipCursor函数把鼠标控制在这个范围以内,这个函数的功能就是控制鼠标的范围。 
ClipCursor(&rect);

   标题为"释放鼠标"的控件的响应函数代码为: 

ClipCursor(NULL); 

   这行代码非常简单,就是使鼠标可以自由移动,不受限制。 

   把以上程序编译好了以后运行,点下"控制鼠标移动范围"按钮,鼠标就只能在这个窗口的范围以内活动,离不开这个窗口了,点下"释放鼠标"按钮,鼠标就恢复正常了。 

   知道了如何控制鼠标范围,该讲讲如何移动鼠标了。移动鼠标非常简单,只需要一个API函数SetCursorPos,这个函数有2个参数,第1个参数是屏幕的x坐标,第2个参数是屏幕的y坐标,它可以把鼠标移动到指定的坐标上去。 

   模拟鼠标的点击功能也非常简单,比如模拟点鼠标右键,可以使用下面两行代码: 

mouse_event(MOUSEEVENTF_RIGHTDOWN,0,0,0,0); 
mouse_event(MOUSEEVENTF_RIGHTUP,0,0,0,0); 

   mouse_event函数的功能就是模拟鼠标点击,第1行代码是模拟按下鼠标右键,第2行代码是模拟鼠标右键弹起,这两行代码就模拟了1次点击鼠标右键的操作,如果想模拟点鼠标左键的操作,只要以上两行代码中的MOUSEEVENTF_RIGHTDOWN和MOUSEEVENTF_RIGHTUP参数换成MOUSEEVENTF_LEFTDOWN和MOUSEEVENTF_LEFTUP就可以了。 

   鼠标的模拟操作讲完了。以上的程序在Windows98下,使用Visual V++6.0编译成功,调试正常。 
本期知识点:控制鼠标的API函数。


--  用VC实现图象渐显和渐隐
摘 要 图象的渐显/渐隐被广泛运用与图象处理和多媒提娱乐软件。本文基于windows的调色板动画和时间码技术设计了通用的图象渐显和渐隐算法,并实现了其visual c++程序编码。 

关键词 渐显、渐隐、调色板、调色板动画、时间码 

图象的渐显/渐隐是十分重要的图象效果,广泛运用于图象处理和多媒提娱乐软件。渐显/渐隐算法设计的最大困难是速度控制,包括定时和快速改变图象中各象素的颜色。如采用普通的全图扫描算法,则速度较慢,很难真正体现渐显/渐隐效果。 

利用windows(3.x.95/98/nt)操作系统特殊的调色板管理和时间码定时机制能设计出有效的图象渐显/渐隐算法。windows提供一种被称为调色板动画(palette animation)的颜色处理技术,它通过快速改变颜色调色板中所选取的表项中的颜色能模拟颜色的变化。设置时间码,定时调用该技术使图象颜色渐变就能实现图象的渐显和渐隐。 

一、调色板动画 

在visual c++中实现调色板动画依赖于mfc类库提供的cpalette类和cdc类中的若干成员函数,其基本步骤如下: 

调用cpalette::createpalette(lplogpalette lplogpalette)函数创建逻辑调色板,注意将参数lplogpalette所指向的各颜色表项结构的peflags域设置为pc_reserved,以防止其它窗口同该调色板匹配颜色。; 
调用cdc::selectpalette和cdc::realizepalette函数选择和实现所创建的逻辑调色板; 
调用cpalette::animatepalette函数改变颜色,实现调色板动画; 
动画完成后应恢复系统调色板。 
cpalette::animatepalette是其中最关键的函数,其原型如下: 

void animatepalette( 

uint nstartindex, // 起始的表项号 

uint nnumentries, // 变化的表项数 

lppaletteentry lppalettecolors ); // 逻辑调色板表项指针 

lppalettecolors为指向paletteentry结构的指针,其中存储着逻辑调色板将要更新的颜色信息。paletteentry结构定义如下: 

typedef struct tagpaletteentry { // pe 

byte pered; 

byte pegreen; 

byte peblue; 

byte peflags; 

} paletteentry; 

pered、pegreen、peblue分别表示逻辑调色板项的r、g、b颜色分量值。peflags 应被置为pc_reserved 。 

nstartindex为lppalettecolors中将变化的起始表项号,nnumentries 为lppalettecolors中将变化的表项数。 

二、时间码定时 

cwnd::settimer函数可设置一个系统时间码,并指定每经过一定的时间间隔使windows系统发送一个wm_timer消息到窗口的消息队列中。窗口在每当接收到相应的wm_timer消息时做一定的处理,便实现了定时处理。 

通常应在窗口的消息循环中接受和处理wm_timer消息,这样将很难编制通用的定时操作。通用的定时操作应将定时处理封装在一个函数中,而不与其它的代码纠缠在一起。笔者实现这一技术的技巧是,在循环操作中截获窗口消息,如消息为指定的时间码消息,则进行定时处理;否则分发消息给窗口消息处理机制。如果定时操作已结束,则修改循环标志,退出循环。具体的代码如下: 

……………………………… 

// 设置时间码,pwnd为处理定时操作的窗口对象指针 

pwnd->settimer(0x100, utimeout, null); 

// 屏蔽鼠标操作,使定时操作不受影响 

pwnd->setcapture(); 

// 开始定时操作 

bool bdone = false; 

msg msg; 

while (! bdone) 

if (::peekmessage(&msg, null, 0, 0, pm_remove)) 

if (msg.message == wm_timer && msg. wparam == 0x100) 

………………….. 

定时操作代码 

………………….. 

// 如定时操作完成,则设置循环标志,结束操作 

if (定时操作完成) 

bdone = true; 

::translatemessage(&msg); 

::dispatchmessage(&msg); 

// 释放鼠标 

::releasecapture(); 

// 删除时间码 

pwnd->killtimer(0x100); 

………………………….. 

函数peekmessage截获窗口消息,translatemessage和dispatchmessage函数解释和分发除指定时间码消息之外的所有消息,以避免丢失消息。 

三、渐显 

渐显就是将显示颜色由黑色(rgb(0, 0, 0))逐渐变化为图象各象素的颜色的过程。开始时调用cpalette::getpaletteentries函数保存图象调色板的各逻辑表项信息,然后调用cpalette::setpaletteentries函数将逻辑调色板中各逻辑表项的pered、pegreen、peblue置为0,定时调用cpalette::animatepalette,每次将各逻辑表项的pered、pegreen、peblue值增加一个变化量,直到它们分别等于图象逻辑调色板中各逻辑表项的pered、pegreen、peblue值。 

下面的函数fadein通过对调色板颜色表项中的各颜色分量值先设为0,然后进行递增,直到所有颜色值都恢复成原调色板中颜色值来实现渐显。 

// 图象渐显效果 

// 参数: 

// pwnd – 显示图象的窗口 

// ppal – 调色板指针 

// ndeta – 各颜色分量的减小量 

// utimeout – 时间的变化量 

void fadein(cwnd *pwnd, cpalette *ppal, int ndeta, uint utimeout) 

// 保留原来的调色板颜色表项 

int ntotalcolors = ppal->getentrycount(); 

paletteentry palettecolors0[256]; 

ppal->getpaletteentries(0, ntotalcolors, palettecolors0); 

// 先将调色板表项中各颜色分量置为0 

paletteentry palettecolors1[256]; 

for (int i=0; i<ntotalcolors; ++i) 

palettecolors1[i].pered = 0; 

palettecolors1[i].pegreen = 0; 

palettecolors1[i].peblue = 0; 

palettecolors1[i].peflags = pc_reserved; 

ppal->setpaletteentries(0, ntotalcolors, palettecolors1);
ppal->animatepalette(0, ntotalcolors, palettecolors1); 

// 设置时间码 

pwnd->settimer(0x100, utimeout, null); 

// 开始渐显 

pwnd->setcapture(); 

bool bdone = false; 

msg msg; 

while (! bdone) 

if (::peekmessage(&msg, null, 0, 0, pm_remove)) 

if (msg.message == wm_timer && msg.wparam == 0x100) 

cclientdc dc(pwnd); 

cpalette *poldpal = dc.selectpalette(ppal, false); 

dc.realizepalette(); 

// 递增各颜色分量 

paletteentry palettecolors[256]; 

ppal->getpaletteentries(0, ntotalcolors, palettecolors); 

bool bredzero=false; 

bool bgreenzero=false; 

bool bbluezero=false; 

for (int i=0; i<ntotalcolors; ++i) 

if (palettecolors[i].pered + ndeta < 

palettecolors0[i].pered) 

palettecolors[i].pered += ndeta; 

bredzero = false; 

else if (palettecolors[i].pered + 1 < 

palettecolors0[i].pered) 

palettecolors[i].pered++; 

bredzero = false; 

else 

bredzero = true; 

if (palettecolors[i].pegreen + ndeta < 

palettecolors0[i].pegreen) 

palettecolors[i].pegreen += ndeta; 

bgreenzero = false; 

else if (palettecolors[i].pegreen + 1 < 

palettecolors0[i].pegreen) 

palettecolors[i].pegreen++; 

bgreenzero = false; 

else 

bgreenzero = true; 

if (palettecolors[i].peblue + ndeta < 

palettecolors0[i].peblue) 

palettecolors[i].peblue += ndeta; 

bbluezero = false; 

else if (palettecolors[i].peblue +1 < 

palettecolors0[i].peblue) 

palettecolors[i].peblue++; 

bbluezero = false; 

else 

bbluezero = true; 

// 直到恢复原始值结束 

bdone = bredzero && bgreenzero && bbluezero; 

// 使系统改变调色板 

ppal->animatepalette(0, ntotalcolors, palettecolors); 

::translatemessage(&msg); 

::dispatchmessage(&msg); 

::releasecapture(); 

pwnd->killtimer(0x100); 

// 恢复原始调色板 

ppal->setpaletteentries(0, ntotalcolors, palettecolors0); 

ppal->animatepalette(0, ntotalcolors, palettecolors0); 

四、渐隐 

渐隐就是将显示颜色由图象各象素的颜色逐渐变化为黑色(rgb(0, 0, 0))的过程,即定时调用cpalette::animatepalette,每次将各逻辑表项的pered、pegreen、peblue值减小一个变化量,直到它们都为0。 

下面的函数fadeout通过对调色板颜色表项中的各颜色分量值进行递减,直到所有颜色值都变成0(即黑色)来实现渐隐。 

// 图象渐隐效果 

// 参数: 

// pwnd – 显示图象的窗口 

// ppal – 调色板指针 

// ndeta – 各颜色分量的减小量 

// utimeout – 时间的变化量 

void fadeout(cwnd *pwnd, cpalette *ppal, int ndeta, uint utimeout) 

// 保留原来的调色板颜色表项 

int ntotalcolors = ppal->getentrycount(); 

paletteentry palettecolors0[256]; 

ppal->getpaletteentries(0, ntotalcolors, palettecolors0); 

// 设置时间码 

pwnd->settimer(0x100, utimeout, null); 

// 开始渐隐 

pwnd->setcapture(); 

bool bdone = false; 

msg msg; 

while (! bdone) 

if (::peekmessage(&msg, null, 0, 0, pm_remove)) 

if (msg.message == wm_timer && msg.wparam == 0x100) 

cclientdc dc(pwnd); 

cpalette *poldpal = dc.selectpalette(ppal, false); 

dc.realizepalette(); 

paletteentry palettecolors[256];
ppal->getpaletteentries(0, ntotalcolors, palettecolors); 

bool bredzero=false; 

bool bgreenzero=false; 

bool bbluezero=false; 

// 递减颜色分量 

for (int i=0; i<ntotalcolors; ++i) 

if (palettecolors[i].pered > ndeta) 

palettecolors[i].pered -= ndeta; 

bredzero = false; 

else if (palettecolors[i].pered > 1) 

palettecolors[i].pered--; 

bredzero = false; 

else 

bredzero = true; 

if (palettecolors[i].pegreen > ndeta) 

palettecolors[i].pegreen -= ndeta; 

bgreenzero = false; 

else if (palettecolors[i].pegreen > 1) 

palettecolors[i].pegreen--; 

bgreenzero = false; 

else 

bgreenzero = true; 

if (palettecolors[i].peblue > ndeta) 

palettecolors[i].peblue -= ndeta; 

bbluezero = false; 

else if (palettecolors[i].peblue > 1) 

palettecolors[i].peblue--; 

bbluezero = false; 

else 

bbluezero = true; 

// 如所有颜色分量都为0,则结束渐隐 

bdone = bredzero && bgreenzero && bbluezero; 

// 使系统改变调色板 

ppal->animatepalette(0, ntotalcolors, palettecolors); 

::translatemessage(&msg); 

::dispatchmessage(&msg); 

::releasecapture(); 

pwnd->killtimer(0x100); 

// 恢复原始调色板 

ppal->setpaletteentries(0, ntotalcolors, palettecolors0); 

ppal->animatepalette(0, ntotalcolors, palettecolors0); 


--  用VC进行屏幕截取编程
---- 屏幕截取是令人比较感兴趣的事情.虽然现在有不少应用程序如HYPERSNAP等可以用来截取你所喜欢的屏幕画面,但是如果能把这个功能加到自己的程序中,就更能利用它强大的作用. 

---- 下面用VC来逐步介绍在Windows95下的实现过程.首先我们要确定屏幕截取的区域,用LPRECT结构来定义.可以截取一个窗口,或整个屏幕.以下代码把选定的屏幕区域拷贝到位图中. 

HBITMAP CopyScreenToBitmap(LPRECT lpRect) 
//lpRect 代表选定区域 

HDC hScrDC, hMemDC; 
// 屏幕和内存设备描述表 
HBITMAP hBitmap, hOldBitmap; 
// 位图句柄 
int nX, nY, nX2, nY2; 
// 选定区域坐标 
int nWidth, nHeight; 
// 位图宽度和高度 
int xScrn, yScrn; 
// 屏幕分辨率 

// 确保选定区域不为空矩形 
if (IsRectEmpty(lpRect)) 
return NULL; 
//为屏幕创建设备描述表 
hScrDC = CreateDC("DISPLAY", NULL, NULL, NULL); 
//为屏幕设备描述表创建兼容的内存设备描述表 
hMemDC = CreateCompatibleDC(hScrDC); 
// 获得选定区域坐标 
nX = lpRect- >left; 
nY = lpRect- >top; 
nX2 = lpRect- >right; 
nY2 = lpRect- >bottom; 
// 获得屏幕分辨率 
xScrn = GetDeviceCaps(hScrDC, HORZRES); 
yScrn = GetDeviceCaps(hScrDC, VERTRES); 
//确保选定区域是可见的 
if (nX 〈0) 
nX = 0; 
if (nY 〈 0) 
nY = 0; 
if (nX2 > xScrn) 
nX2 = xScrn; 
if (nY2 > yScrn) 
nY2 = yScrn; 
nWidth = nX2 - nX; 
nHeight = nY2 - nY; 
// 创建一个与屏幕设备描述表兼容的位图 
hBitmap = CreateCompatibleBitmap 
(hScrDC, nWidth, nHeight); 
// 把新位图选到内存设备描述表中 
hOldBitmap = SelectObject(hMemDC, hBitmap); 
// 把屏幕设备描述表拷贝到内存设备描述表中 
BitBlt(hMemDC, 0, 0, nWidth, nHeight, 
hScrDC, nX, nY, SRCCOPY); 
//得到屏幕位图的句柄 
hBitmap = SelectObject(hMemDC, hOldBitmap); 
//清除 
DeleteDC(hScrDC); 
DeleteDC(hMemDC); 
// 返回位图句柄 
return hBitmap; 

得到屏幕位图句柄以后,我们 
可以把屏幕内容粘贴到剪贴板上. 
if (OpenClipboard(hWnd)) 
//hWnd为程序窗口句柄 

//清空剪贴板 
EmptyClipboard(); 
//把屏幕内容粘贴到剪贴板上, 
hBitmap 为刚才的屏幕位图句柄 
SetClipboardData(CF_BITMAP, hBitmap); 
//关闭剪贴板 
CloseClipboard(); 

我们也可以把屏幕内容以位图格式存到磁盘文件上. 

int SaveBitmapToFile(HBITMAP hBitmap , 
LPSTR lpFileName) //hBitmap 为刚才的屏幕位图句柄 
{ //lpFileName 为位图文件名 
HDC hDC; 
//设备描述表 
int iBits; 
//当前显示分辨率下每个像素所占字节数 
WORD wBitCount; 
//位图中每个像素所占字节数 
//定义调色板大小, 位图中像素字节大小 , 
位图文件大小 , 写入文件字节数 
DWORD dwPaletteSize=0, 
dwBmBitsSize, 
dwDIBSize, dwWritten; 
BITMAP Bitmap; 
//位图属性结构 
BITMAPFILEHEADER bmfHdr; 
//位图文件头结构 
BITMAPINFOHEADER bi; 
//位图信息头结构 
LPBITMAPINFOHEADER lpbi; 
//指向位图信息头结构 
HANDLE fh, hDib, hPal,hOldPal=NULL; 
//定义文件,分配内存句柄,调色板句柄 

//计算位图文件每个像素所占字节数 
hDC = CreateDC("DISPLAY",NULL,NULL,NULL); 
iBits = GetDeviceCaps(hDC, BITSPIXEL) * 
GetDeviceCaps(hDC, PLANES); 
DeleteDC(hDC); 
if (iBits 〈 = 1) 
wBitCount = 1; 
else if (iBits 〈 = 4) 
wBitCount = 4; 
else if (iBits 〈 = 8) 
wBitCount = 8; 
else if (iBits 〈 = 24) 
wBitCount = 24; 
//计算调色板大小 
if (wBitCount 〈 = 8) 
dwPaletteSize = (1 〈 〈 wBitCount) * 
sizeof(RGBQUAD); 

//设置位图信息头结构 
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap); 
bi.biSize = sizeof(BITMAPINFOHEADER); 
bi.biWidth = Bitmap.bmWidth; 
bi.biHeight = Bitmap.bmHeight; 
bi.biPlanes = 1; 
bi.biBitCount = wBitCount; 
bi.biCompression = BI_RGB; 
bi.biSizeImage = 0; 
bi.biXPelsPerMeter = 0; 
bi.biYPelsPerMeter = 0; 
bi.biClrUsed = 0; 
bi.biClrImportant = 0; 

dwBmBitsSize = ((Bitmap.bmWidth * 
wBitCount+31)/32)* 4 
*Bitmap.bmHeight ; 
//为位图内容分配内存 
hDib = GlobalAlloc(GHND,dwBmBitsSize+ 
dwPaletteSize+sizeof(BITMAPINFOHEADER)); 
lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib); 
*lpbi = bi; 
// 处理调色板 
hPal = GetStockObject(DEFAULT_PALETTE); 
if (hPal) 

hDC = GetDC(NULL); 
hOldPal = SelectPalette(hDC, hPal, FALSE); 
RealizePalette(hDC); 

// 获取该调色板下新的像素值 
GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight,
(LPSTR)lpbi + sizeof(BITMAPINFOHEADER) 
+dwPaletteSize, 
(BITMAPINFOHEADER *) 
lpbi, DIB_RGB_COLORS); 
//恢复调色板 
if (hOldPal) 

SelectPalette(hDC, hOldPal, TRUE); 
RealizePalette(hDC); 
ReleaseDC(NULL, hDC); 

//创建位图文件 
fh = CreateFile(lpFileName, GENERIC_WRITE, 
0, NULL, CREATE_ALWAYS, 
FILE_ATTRIBUTE_NORMAL | FILE_ 
FLAG_SEQUENTIAL_SCAN, NULL); 
if (fh == INVALID_HANDLE_VALUE) 
return FALSE; 
// 设置位图文件头 
bmfHdr.bfType = 0x4D42; // "BM" 
dwDIBSize = sizeof(BITMAPFILEHEADER) 
+ sizeof(BITMAPINFOHEADER) 
+ dwPaletteSize + dwBmBitsSize; 
bmfHdr.bfSize = dwDIBSize; 
bmfHdr.bfReserved1 = 0; 
bmfHdr.bfReserved2 = 0; 
bmfHdr.bfOffBits = (DWORD)sizeof 
(BITMAPFILEHEADER) 
+ (DWORD)sizeof(BITMAPINFOHEADER) 
+ dwPaletteSize; 
// 写入位图文件头 
WriteFile(fh, (LPSTR)&bmfHdr, sizeof 
(BITMAPFILEHEADER), &dwWritten, NULL); 
// 写入位图文件其余内容 
WriteFile(fh, (LPSTR)lpbi, dwDIBSize, 
&dwWritten, NULL); 
//清除 
GlobalUnlock(hDib); 
GlobalFree(hDib); 
CloseHandle(fh); 


--  用VC制作图片屏幕保护程序
VC++可谓神通广大,如果学到家了,或者就掌握了那么一点MFC,你也会感到它的方便快捷,当然最重要的是功能强大。不是吗,从最基本的应用程序.EXE到动态连接库DLL,再由风靡网上的ActiveX控件到Internet Server API,当然,还有数据库应用程序……瞧,我都用它来做屏幕保护程序了。一般的屏幕保护程序都是以SCR作为扩展名,并且要放在c://windows 目录或 c://windows//system 目录下,由Windows 98内部程序调用(Windows NT 是在 c://windows//system32
目录下)。怎么调用?不用说了,这谁不知道。 
  好了,我们来作一个简单的。选择MFC AppWizard(exe),Project Name 为MyScreensaver,[NEXT],对话框,再后面随你了。打开菜单Project、Settings,在Debug页、Executable for debug session项,以及Link页中Output file name项改为c://windows//MyScreensaver.scr,这样,你可以调试完后,直接在VC中运行(Ctrl+F5),便可看到结果。当然,这样做的唯一缺点是你必须手动清除Windows
目录下的垃圾文件(当然是在看到满意结果后;还有,你可借助SafeClean 这个小东东来帮你清除,除非你的硬盘大的让你感到无所谓……快快快回来,看我跑到那里去了)。接下来用Class Wizard生成CMyWnd类,其基类为CWnd(在Base Class 中为generic CWnd)。这个类是我们所要重点研究的。创建满屏窗口、计时器,隐藏鼠标,展示图片,响应键盘、鼠标等等,这家伙全包了。至于MyScreensaverDlg.h与MyScreensaverDlg.cpp文件我们暂时不管。打开MyScreensaver.cpp,修改InitInstance()函数: 
  BOOL CMyScreensaverApp::InitInstance() 
  { 
   AfxEnableControlContainer(); 
  #ifdef _AFXDLL 
   Enable3dControls(); // Call this when using MFC in a shared DLL 
  #else 
   Enable3dControlsStatic(); // Call this when linking to MFC statically 
  #endif 
   CMyWnd* pWnd = new CMyWnd; 
   pWnd->Create(); 
   m_pMainWnd = pWnd; 
   return TRUE; 
  } 
  当然,再这之前得先 #include “MyWnd.h" 。后面要做的都在MyWnd.h 与 MyWnd.cpp 两文件中了。 
  下面给出CMyWnd 的说明: 
  class CMyWnd : public CWnd 
  { 
  public: 
   CMyWnd(); 
   static LPCSTR lpszClassName; //注册类名 
  public: 
   BOOL Create(); 
  public: 
   // Clazard generated virtual function overrides 
   //{{AFX_VIRTUAL(CMyWnd) 
   protected: 
   virtual void PostNcDestroy(); 
   //}}AFX_VIRTUAL 
  public: 
   virtual ~CMyWnd(); 
  protected: 
   CPoint m_prePoint; //检测鼠标移动 
   void DrawBitmap(CDC& dc, int nIndexBit); 
   //{{AFX_MSG(CMyWnd) 
   afx_msg void OnPaint(); 
   afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); 
   afx_msg void OnLButtonDown(UINT nFlags, CPoint point); 
   afx_msg void OnMButtonDown(UINT nFlags, CPoint point); 
   afx_msg void OnMouseMove(UINT nFlags, CPoint point); 
   afx_msg void OnRButtonDown(UINT nFlags, CPoint point); 
   afx_msg void OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); 
   afx_msg void OnDestroy(); 
   afx_msg void OnTimer(UINT nIDEvent); 
   afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized); 
   afx_msg void OnActivateApp(BOOL bActive, HTASK hTask); 
   //}}AFX_MSG 
   DECLARE_MESSAGE_MAP() 
  }; 
  MyWnd.cpp 文件: 
  …… 
  CMyWnd::CMyWnd() 
  { 
   m_prePoint=CPoint(-1, -1); 
  } 
  LPCSTR CMyWnd::lpszClassName=NULL; 
  BOOL CMyWnd::Create() 
  { 
   if(lpszClassName==NULL) 
   { 
   lpszClassName=AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW, 
  ::LoadCursor(AfxGetResourceHandle(),MAKEINTRESOURCE(IDC_NOCURSOR)));
  //注册类;IDC_NOCURSOR为新建光标的ID,这个光标没有任何图案 
   } 
   CRect rect(0, 0, ::GetSystemMetrics(SM_CXSCREEN), 
   ::GetSystemMetrics(SM_CYSCREEN)); 
   CreateEx(WS_EX_TOPMOST, lpszClassName, _T(“”), WS_VISIBLE|WS_POPUP, 
   rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 
   GetSafeHwnd(), NULL, NULL); //创建一个全屏窗口 
   SetTimer(ID_TIMER, 500, NULL);//计时器,ID_TIMER别忘了定义 
   return TRUE; 
  } 
  为了防止同时运行两个相同的程序,下面两个函数是必需的: 
  void CMyWnd::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) 
  { 
   CWnd::OnActivate(nState,pWndOther,bMinimized); 
   if (nState==WA_INACTIVE) 
   PostMessage(WM_CLOSE); 
  } 
  void CMyWnd::OnActivateApp(BOOL bActive, HTASK hTask) 
  { 
   CWnd::OnActivateApp(bActive, hTask); 
   if (!bActive) //is being deactivated 
   PostMessage(WM_CLOSE); 
  } 
  OnPaint()函数将全屏窗口置为黑色: 
  void CMyWnd::OnPaint() 
  { 
   CPaintDC dc(this); 
   CBrush brush(RGB(0,0,0)); 
   CRect rect; 
   GetClientRect(rect); 
   dc.FillRect(&rect, &brush); 
  } 
  由计数器调用DrawBitmap()函数,切换图片;注意,下面两个函数中的IDB_BITMAP1, dc.BitBlt(0,0,800,600……以及if(nIndexBit>=5)中的有关数据依据你的bmp图片个数、尺寸、位置不同而不同,我是选择了5张800x600的bmp图片。注意,ID值是连续的,IDB_BITMAP1最小。 
  void CMyWnd::DrawBitmap(CDC &dc, int nIndexBit) 
  { 
   CDC dcmem; 
   dcmem.CreateCompatibleDC(&dc); 
   CBitmap m_Bitmap; 
   m_Bitmap.LoadBitmap(IDB_BITMAP1+nIndexBit); 
   dcmem.SelectObject(m_Bitmap); 
   dc.BitBlt(0,0,800,600,&dcmem,0,0,SRCCOPY); 
  } 
  void CMyWnd::OnTimer(UINT nIDEvent) 
  { 
   CClientDC dc(this); 
   static nIndexBit=0; 
   if(nIndexBit>=5) 
   nIndexBit=0; 
   DrawBitmap(dc, nIndexBit++); 
   CWnd::OnTimer(nIDEvent); 
  } 
  
  响应键盘、鼠标是屏幕保护程序不可缺少的,在OnKeyDown()、 OnLButtonDown()、 OnMButtonDown()、OnRButtonDown()、OnSysKeyDown()函数中都加入: 
  PostMessage(WM_CLOSE); 
  OnMouseMove()函数比较特殊,它应加的代码为: 
   if(m_prePoint == CPoint(-1,-1)) 
   m_prePoint = point; 
   else if(m_prePoint!=point) 
   PostMessage(WM_CLOSE); 
  快要完工了。在OnDestroy()函数中删掉计时器:KillTimer(ID_TIMER); 
  还有啦,在CMyWnd::PostNcDestroy() 中加入: delete this; 
  哎呀,腰酸背疼,眼球

抱歉!评论已关闭.