1.制作封页位图:制作应用程序多媒体启动封页真彩位图,记录位图的高度和宽度,建立所需要的其它多媒体声音等文件;
2.建立应用程序:利用FILE->NEW菜单建立名为START的多文档应用程序框架,并利用类向导AppWizard增加基类为generic CWnd 的新类CSplashWnd,将新类的文件改名为splash.h和splash.cpp,并增加WM_CREATE和WM_PAINT的消息映射函数;
3.定义变量和函数:在splash.h中定义如下成员变量和函数:
public:
BOOL Create(CWnd* pParentWnd=NULL);
HANDLE hBitsSrc;
LPSTR pBitsSrc;
UINT iW,iH;
4.建立启动封页窗口:在splash.cpp中增加Create函数,建立显示位图的弹出窗口:
#include "windowsx.h"//增加扩展窗口建立
......
struct{//位图文件头结构
BITMAPINFOHEADER Header;
}Info;
BOOL CSplashWnd::Create(CWnd* pParentWnd)
{ //建立显示位图的弹出窗口
return CreateEx(0,AfxRegisterWndClass(0,
AfxGetApp()->LoadStandardCursor(IDC_ARROW)),
NULL,WS_POPUP|WS_VISIBLE,0,0,672,480,
pParentWnd->GetSafeHwnd(),NULL);
}
int CSplashWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{ ......
// TODO: Add your specialized creation code here
CenterWindow();//使弹出窗口居中
return 0;
}
5.完善析构函数:在splash.cpp中完善析构函数,为显示位图作准备:
CSplashWnd::CSplashWnd()//完善析构函数
{ iW=672;iH=480;//位图的宽度和高度
Info.Header.biSize=sizeof(BITMAPINFOHEADER);
Info.Header.biWidth=iW;
Info.Header.biHeight=iH;
Info.Header.biPlanes=1L;
Info.Header.biBitCount=24L;
hBitsSrc=::GlobalAlloc(GHND,(long)(iW*iH*3));//分配内存
pBitsSrc=(LPSTR)::GlobalLock(hBitsSrc);//加锁内存
}
CSplashWnd::~CSplashWnd()
{ ::GlobalUnlock(hBitsSrc);//解锁内存
::GlobalFree(hBitsSrc);//释放内存
}
6.显示真彩位图:在splash.cpp中完善Onpaint函数,实现真彩位图的显示:
void CSplashWnd::OnPaint()
{......// TODO: Add your message handler code here
CFile fbmp;
fbmp.Open("c:\\windows\\EMC1.BMP",CFile::modeRead|
CFile::typeBinary|CFile::shareExclusive);//打开文件
fbmp.Seek(54,CFile::begin);//位图文件头为54字节
fbmp.ReadHuge(pBitsSrc,(long)(iW*iH*3));//读文件
fbmp.Close();//关闭文件
HBITMAP hBitmap=::CreateDIBitmap(dc.m_hDC,
(BITMAPINFOHEADER FAR*)&(Info.Header),CBM_INIT,
pBitsSrc,(LPBITMAPINFO)&Info,DIB_RGB_COLORS);//建立位图
HDC hMemDC=::CreateCompatibleDC(dc.m_hDC);//建立内存
HBITMAP hBitmapOld=SelectBitmap(hMemDC,hBitmap);//选择对象
::StretchBlt(dc.m_hDC,0,0,iW,iH,hMemDC,
0,0,iW,iH,SRCCOPY);//显示真彩位图
::DeleteObject(SelectObject(hMemDC,hBitmapOld));//删除位图
}
7.弹出窗口类声明:在Mainfrm.h中增加代码,为显示和撤销弹出窗口作准备:
class CsplashWnd;//闪烁窗口类
......
class CMainFrame : public CMDIFrameWnd
{......
public:
CSplashWnd SplashWnd;//声明类
8.真彩封页的实现:在Mainfrm.cpp中增加显示弹出窗口、播放音乐、延时功能和撤销弹出窗口的代码,以实现真彩封页的整个处理过程:
#include "splash.h" //新类支持
#include "mmsystem.h"//多媒体支持
#pragma comment(lib,"WINMM.LIB")//多媒体支持
......
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
SplashWnd.Create(this);//创建弹出窗口
SplashWnd.ShowWindow(SW_SHOW);//显示弹出窗口
SplashWnd.UpdateWindow();//更新弹出窗口
int dd=sndPlaySound("c:\\windows\\reminder.wav",
SND_ASYNC|SND_LOOP);//异步播放WAV文件
if (dd==FALSE){//参数SND_ASYNC|SND_LOOP可循环播放
AfxMessageBox("播放WAV文件错误~");
}
::Sleep(2000);//进程休眠2秒
......//应用程序初始化工作
dd=sndPlaySound("",SND_ASYNC);//终止播放.WAV文件
SplashWnd.DestroyWindow();//撤消弹出窗口
return 0;
}
经过上述八个步骤的处理,整个多媒体真彩启动封页制作完毕。
PictureEx图片显示类支持以下格式的图片:GIF (including animated GIF87a and GIF89a), JPEG, BMP, WMF, ICO, CUR等,我特别推崇的是可以做出动画,而且轻而易举,确实很COOL。
下面是详细的编程过程:
1. 新建项目:在VC6中用MFC新建一个基于对话框的GifDemo应用程序,接受所有缺省选项即可;
2.在项目中插入文件:把PictureEx.h,PictureEx.cpp文件copy 到项目文件夹下,Project->Add to Project->Files中选上PictureEx.h,PictureEx.cpp, Insert;
3.加入图片控件:从对话框控件中把Picture Control(图片控件)拖入主对话框中,修改其属性:ID:IDC_GIF,TYPE:Rectangle,其余接受缺省选项。再在ClassWiard中为IDF_GIF加入CSatic控制变量m_GifPic, 注意看一下,GifDemoDlg.h中是否加上了#include "PictureEx.h"(由ClassWiard加入)。然后将CSatic m_GifPic;更改成CPictureEx m_GifPic;
4.加载动画文件:先将要加载的动画文件放到 res 资源文件夹下,再将其Import进项目中,由于MFC只支持256BMP文件的图片,因此,我们要新建一个图片类型:"GIF",我在这里将我网站的宣传图片roaring.gif放进去 ,并将其ID修改成:IDR_GIFROARING。
import(导入)gif动画的详细过程:
在resourceview窗口中,单击鼠标右键,在出现的环境菜单中选择“import...”命令,会出现“import resource”选择文件对话框,文件类型选择“所有文件(*.*)”,open as 选项为"auto",再选择动画文件所在目录,选上要载入的动画文件 roaring.gif,再单击 import,由于gif动画类型不是vc默认的文件类型,这时会出现"custom resource type"对话框,键入“"gif"”,再单击ok,然后再修改其id。
5.在程序的适当位置添入加载代码: 这里,我们在CGifDemoDlg::OnInitDialog()函数中加入如下代码:
// TODO: Add extra initialization here
if (m_GifPic.Load(MAKEINTRESOURCE(IDR_GIFROARING),_T("Gif")))
m_GifPic.Draw();
如果仅仅把动画载入,到这就可以了,运行一下,应该看看您的的成果了。
下面附带说说如何将这幅动画制作成超链接,以后,咱们也可以宣传自已的公司、网站或产品了。
6.利用ClassWiard加入一个LButtonDown鼠标左键消息处理函数CGifDemoDlg::OnLButtonDown(UINT nFlags, CPoint point), 添入如下代码:
void CGifDemoDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CRect rect;
m_GifPic.GetWindowRect(&rect);
ScreenToClient(&rect);
if (rect.PtInRect(point))
ShellExecute(AfxGetMainWnd() ->m_hWnd,_T("open"),
_T("http://roaringwind.best.163.com"),_T(""),NULL,0);
CDialog::OnLButtonDown(nFlags, point);
}
我在这儿将我主页的地址放上了,运行,点击动画图片就能进入我的站点的了。当然要是能象所有的超链接一样,能将鼠标变成手形,就更好了。
7.改变鼠标形状:将一个鼠标文件放在res文件夹中,IMPORT,ID:IDC_CURSOR1,利用ClassWiard加入一个WM_SETCURSOR消息处理函数CGifDemoDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message), 添入如下代码:
BOOL CGifDemoDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
// TODO: Add your message handler code here and/or call default
CRect rect;
m_GifPic.GetWindowRect(&rect);
ScreenToClient(&rect);
CPoint point;
GetCursorPos(&point);
ScreenToClient(&point);
if (rect.PtInRect(point) && m_hCursor)
{
SetCursor(m_hCursor);
return TRUE;
};
return CDialog::OnSetCursor(pWnd, nHitTest, message);
}
不错吧。
可视动画控件ActiveMovie是Microsoft公司开发的ActiveX控件,从开始的1.0版、1.2版到现在的2.0版,功能上已经有了很大的改进。由于该控件内嵌了Microsoft MPEG音频解码器和Microsoft MPEG视频解码器,所以能够很好地支持音频文件和视频文件,用其播放的VCD效果就很好。
另外,播放时若用鼠标右键单击画面,可以直接对画面的播放、暂停、停止等进行控制,读者还可以自行在“属性”栏中对影片播放进行控制设置,用起来非常方便。
在Microsoft公司去年推出的VC++6.0中已经包含了ActiveMovie控件的2.0版,笔者 在VC++6.0下利用这个控件自制了一个简易的媒体播放器,除了满屏功能外,还可以对音量进行控制。下面把具体做法介绍给读者。
一 建立工程
利用 VC++6.0的AppWizard生成一个基于对话框的工程Player,去掉对话框上的确定和取消按钮,并加入ActiveMovie控件(通常情况下ActiveMovie控件并不出现在控件面板中,可在菜单中依次选择“project—Add To Project— >Components And Controls”,在出现的“Components And Controls Gallery”对话框中打开“Registered Active Controls”文件夹,选中“ActiveMovie Control Object”选项,按“Insert”后关闭该对话框,ActiveMovie控件便出现在控件面板中),调整好控件在对话框中的位置。为了能够控制控件的操作,应为对话框设计一个菜单,菜单的项目可以定为文件、屏幕控制和音量控制。
二 添加代码
首先利用ClassWizard为ActiveMovie控件声明一个变量m_ActiveMovie。然后为菜单文件添加两个菜单项打开文件和退出,并分别添加函数OnOpen()和OnExit(),代码如下:
void Cplayer::OnOpen()
{ // TODO: Add your command handler code here char szFilter[] =
" Video File (*.dat)∣ *.dat∣Wave File (*.wav)∣*.wav∣AVI File (*.avi)∣ (*.avi)∣Movie File
(*.mov)∣(*.mov)∣ Media File (*.mmm)∣(*.mmm)∣Mid File(*.mid;*.rmi)∣ (*.mid;*.rmi)∣MPEG File
(*.mpeg)∣(*.mpeg)∣ All File (*.*)∣*.* ";
//用于设置FileDialog的文件类型
CFileDialog FileDlg( TRUE, NULL, NULL, OFN_HIDEREADONLY, szFilter );
if( FileDlg.DoModal() == IDOK ) { CString PathName = FileDlg.GetPathName();
PathName.MakeUpper();
m_ActiveMovie.SetFileName(PathName);
}
}
OnOpen()函数的作用是显示“打开”对话框,通过该对话框选择要执行的文件。
AVI文件就是我们所说的多媒体文件,所谓的AVI图像就是视频图像,该文件是一个RIFF说明文件,它用于获取、编辑、演示音频、视频序列。一般的AVI文件包含音频流和视频流,有的特殊的AVI还包含一个控制路径或MIDI路径作为附加的数据流。
现在播放AVI文件的软件很多,但大多无法从AVI视频文件中读取一帧图像并生成BMP格式的文件。笔者在使用AVI文件开发项目过程中对AVI文件的操作积累了一些经验,对于如何实现从AVI视频流中获取任意帧的图像数据并存储成BMP文件,其中最关键的是要从AVI文件中获取具体某一帧的图像数据,为此我利用Windows提供的API函数实现了自定义的CAvi类,用于操作AVI文件。
在使用API函数操作AVI文件时,一定要注意用AVIFileInit()来初始化AVI库,程序结束时用AVIFileExit()释放AVI库,否则API函数无法使用。现以操作包含真彩色图像的AVI文件为例,给出Cavi类的部分函数的具体实现,其中CaviCreate()函数用于读取AVI文件信息并初始化Cavi类的成员,例如根据AVI文件信息定义每帧图像的宽、高、每帧图像的信息头结构等等;函数AviRead(int mFrame)用于从AVI文件中读取第mFrame帧。实现代码显示如下:
//Cavi类头文件定义;
class CAvi file://AVI类,处理AVI文件
{
public:
int cy;//图象高
int cx;//图象宽
file://long m_maxFrame;
BYTE *pData;//寸储图象数据
BITMAPINFO *m_pBMI;//位图文件信息头
PAVISTREAM pavi;//AVI流
PAVIFILE pfile;//AVI文件指针
AVIFILEINFO * pfi; file://AVI信息
BOOL AviRead(int mFrame);//读AVI文件的第mFrame帧
CAvi();//标准构造函数
CAviCreate(CString &string);//用文件名初始化AVI类的成员
virtual ~CAvi();
};
//Cavi类文件实现部分;
CAvi::CAvi()
{ AVIFileInit();//初始化AVI库
cx=0;//定义图象宽、高、等成员
cy=0;
m_pBMI=NULL;
pData=NULL;
file://m_maxFrame=0;
pfi=NULL;
}
CAvi::~CAvi()//析构、释放指针
{
// AVIFileClose(pfile);
AVIFileExit();
if(pData!=NULL)
delete pData;
pData=NULL;
if(m_pBMI!=NULL)
delete m_pBMI;
m_pBMI=NULL;
if(pfi!=NULL)
delete pfi;
pfi=NULL;
}
CAvi::CAviCreate(CString &string)//读文件初始化该类
{
HRESULT hr;
pfi=new AVIFILEINFO;
hr = AVIFileOpen(&pfile, // returned file pointer
string, // file name
OF_READ, // mode to open file with
NULL);
hr= AVIFileInfo(pfile, file://获取AVI信息,放入pfi中
pfi,
sizeof(AVIFILEINFO)
);
cx=pfi->dwWidth;//图象宽、高
cy=pfi->dwHeight;
hr=AVIFileGetStream(//将AVI变成视频流
pfile,
&pavi,
streamtypeVIDEO,
0//LONG lParam
);
m_pBMI=new BITMAPINFO;//定义BMP信息头
m_pBMI->bmiHeader.biBitCount=24;
m_pBMI->bmiHeader.biClrImportant=0;
m_pBMI->bmiHeader.biClrUsed=0;
m_pBMI->bmiHeader.biCompression=BI_RGB;
m_pBMI->bmiHeader.biHeight=cy;
m_pBMI->bmiHeader.biWidth=cx;
m_pBMI->bmiHeader.biPlanes=1;
m_pBMI->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
m_pBMI->bmiHeader.biXPelsPerMeter=0;
m_pBMI->bmiHeader.biYPelsPerMeter=0;
m_pBMI->bmiHeader.biSizeImage=cx*cy*3;
pData=(BYTE*)new char[cx*cy*3];//根据AVI中BMP图象的信息定义缓冲区
}
BOOL CAvi::AviRead(int mFrame)//将AVI文件的M帧数据读入PData缓冲区
{
HRESULT hr;
hr= AVIStreamRead( pavi,
mFrame,
1,
pData,
cx*cy*3,
NULL,
NULL
);
if(hr==0)
return TRUE;
else
return FALSE;
}
上述Cavi类实现部分所涉及到的API函数可以参考微软提供的MSDN。Cavi类中的pData缓冲区存放AVI文件中的具体某一帧图像数据,同时Cavi类的m_pBMI为BMP图像文件信息结构,这时可以根据图像的大小定义BMP图像文件头结构,关于BMP文件的存储,由于篇幅的原因,我不在多讲了,有兴趣的读者可以参见笔者的拙作"Visual C++6.0开发灰度位图处理"(天极网软件栏目2001.9.10发表),该文里面讲述了如何存取BMP文件。以上程序在Windows2000、Visual C++6.0环境下顺利编译通过,运行正常。
前言:
在图像的编程中,经常会遇到这样一种情况,在有限的区域中显示了一幅大图,这时要浏览图像的各个部分,这就需要用到图像的滚动。关于它的实现,许多书都有提及,但其中的关键点和难点,即拖动中的刷新和闪烁问题,却讲述的不多,这也是我写本文的目的所在,下面我将详细分析实现方法。
实现效果及实现方法:
在图像区域中按下鼠标左键,可拖动图像在某一有限区域中任意滚动。
方法为 :拖动时计算上次与本次的偏移,然后将图像显示的起始点进行变化并刷新图像区域。
第一步:响应WM_LBUTTONDOWN 消息,记录按下开始拖动的起始位置。
void CWingImgDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_lPicOldLeft = point.x;
m_lPicOldTop = point.y;
CDialog::OnLButtonDown(nFlags, point);
}
第二步:响应WM_MOUSEMOVE 消息,实现滚动。
void CWingImgDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
file://如果鼠标按下
if( (nFlags & MK_LBUTTON) == MK_LBUTTON )
{
m_lPicNewLeft = point.x;
m_lPicNewTop = point.y;
DWORD dwLRShift = m_lPicNewLeft - m_lPicOldLeft;
DWORD dwTBShift = m_lPicNewTop - m_lPicOldTop;
file://改变图像显示的起始点
m_lPicLeft = m_lPicLeft - dwLRShift;
m_lPicTop = m_lPicTop - dwTBShift;
file://判断边界的语句,省去。
m_lPicOldLeft = m_lPicNewLeft;
m_lPicOldTop = m_lPicNewTop;
file://进行刷新的语句,见第四步。
}
CDialog::OnMouseMove(nFlags, point);
}
第三步:在OnPaint中显示,显示的其他部分,如图像的得来等,省去。
void CWingImgDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
file://其他的显示内容,省去。
if(m_pImgInfo != NULL)
dc.BitBlt(m_wShowAdjLeft,m_wShowAdjTop,m_lWidth,m_lHeight,&m_AdjDC,
m_lPicLeft,m_lPicTop,SRCCOPY);
CDialog::OnPaint();
}
第四步:刷新处理。
最常想到的方法,当然是使用Invalidate(TRUE);//刷新整个无效区
UpdateWindow();
这时,会刷新整个程序区域的无效区,闪烁非常严重,改正如下:
InvalidateRect(&m_rtPic,TRUE); file://仅刷新图像显示区域
UpdateWindow();
此时,仅会刷新图像所在区域,闪烁有所缓解,再进一步,可使用
InvalidateRect(&m_rtPic,TRUE); file://使用快速重画
ReDrawWindow(&m_rtPic,NULL,RDW_INTERNALPAINT| RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
进行处理,此时闪烁更进一步减小,考虑到,其他部分可能影响刷新区域,干脆将OnPaint()直接使用在此处,即变为:
OnPaint();
但如果在OnPaint()中有大量的绘图语句,这种方法仍旧不可行,考虑到不能激发OnPaint()这一因素及控制刷新范围,我采用了如下非标准的方法解决,代码如下:
CDC *pDC;
pDC = GetDC();
if(m_pImgInfo != NULL)
pDC->BitBlt(m_wShowLeft,m_wShowTop,m_lWidth,m_lHeight,&m_AdjDC,m_lPicLeft,m_lPicTop,SRCCOPY);
ReleaseDC(pDC);
这种方法很好地解决了刷新闪烁的问题,图像拖动时很平稳且无闪烁。
这是我在实践中遇到的一个问题,写出来,与大家共享。如果有问题,或者有更好的建议和做法,欢迎和我联系探讨,如需要整个程序的源文件,请Mail联系(DavidZheng@Acercm.com.cn)。