-- 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); // 从第二幅图象取网格匹配模板 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中的应用,无疑还是很有吸引力的。 为简单计,图形显示的代码直接在视类中的OnDraw中编写,首先打开文件并判断文件的可用性,并把文件内容放到流接口IStream的对象pStm中: 其中,显示工作主要是由IPicture接口对象的Render函数来完成的,该函数主要用来将图片的指定部分画到指定的设备环境的指定位置。原型如下: |
||
-- Visual C++实现Flash动画播放
|
||
-- 用RealPlayer控件制作的播放器
//多谢杜修杏 老师指点 /////////////////////////////////// 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中添加两个当我们使用鼠标单击这两个控件时响应的函数,标题为"控制鼠标移动范围"的控件的响应函数代码为: 标题为"释放鼠标"的控件的响应函数代码为: ClipCursor(NULL); 这行代码非常简单,就是使鼠标可以自由移动,不受限制。 把以上程序编译好了以后运行,点下"控制鼠标移动范围"按钮,鼠标就只能在这个窗口的范围以内活动,离不开这个窗口了,点下"释放鼠标"按钮,鼠标就恢复正常了。 知道了如何控制鼠标范围,该讲讲如何移动鼠标了。移动鼠标非常简单,只需要一个API函数SetCursorPos,这个函数有2个参数,第1个参数是屏幕的x坐标,第2个参数是屏幕的y坐标,它可以把鼠标移动到指定的坐标上去。 模拟鼠标的点击功能也非常简单,比如模拟点鼠标右键,可以使用下面两行代码: mouse_event(MOUSEEVENTF_RIGHTDOWN,0,0,0,0); mouse_event函数的功能就是模拟鼠标点击,第1行代码是模拟按下鼠标右键,第2行代码是模拟鼠标右键弹起,这两行代码就模拟了1次点击鼠标右键的操作,如果想模拟点鼠标左键的操作,只要以上两行代码中的MOUSEEVENTF_RIGHTDOWN和MOUSEEVENTF_RIGHTUP参数换成MOUSEEVENTF_LEFTDOWN和MOUSEEVENTF_LEFTUP就可以了。
鼠标的模拟操作讲完了。以上的程序在Windows98下,使用Visual V++6.0编译成功,调试正常。 |
||
-- 用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,以防止其它窗口同该调色板匹配颜色。; 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); // 设置时间码 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]; 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) // 确保选定区域不为空矩形 得到屏幕位图句柄以后,我们 int SaveBitmapToFile(HBITMAP hBitmap , //计算位图文件每个像素所占字节数 //设置位图信息头结构 dwBmBitsSize = ((Bitmap.bmWidth * |
||
-- 用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; 哎呀,腰酸背疼,眼球 |