-- VC多媒體編程本文來自http://study.feloo.com/ css
|
||
-- 做者:admin -- 發佈時間:2006-8-11 16:05:00c++ -- Visual C++中基於多文檔視窗模型的重疊圖象拼接技術摘要 程序員 圖象拼接是在全景視頻系統、地理信息系統等應用中常常遇到的一個問題,本文基於網格匹配的方法對邊界部分有重疊的圖象提出了一種行之有效的對準算法,並經過平滑因子對圖象實現了無縫拼接。並應用文檔視窗模型實現了該算法,並完成了位圖文件的顯示、存儲等操做,具備必定的廣泛意義。 算法 關鍵詞: 圖象拼接,算法,重疊圖象,文檔視窗,位圖文件,圖象顯示 sql 文章正文 數據庫 1、 多文檔視窗模型概述 MFC的AppWizard能夠生成三種類型的應用程序:基於對話框的應用、單文檔應用(SDI)和多文檔應用(MDI)。三種應用中,以多文檔應用(MDI)最爲複雜,其功能也最強大。當咱們用AppWizard生成一個多文檔應用時,系統由CMultiDocTemplate自動生成了一個從Cdocument類繼承的文檔類,一個從Cview類繼承的視窗類,一個從CMDIChildWnd類繼承的框架類。當咱們每次創建一個新的文檔時,程序根據文檔模板生成一個新實例,這些咱們都可不用關心AppWizard已經自動生成了代碼。但若是咱們要在程序中使用多個不一樣的文檔類時,則需本身創建文檔模板並控制文檔實例的創建。假設咱們要向一基於多文檔的工程MDI中增長一Test的文檔。編程 具體步驟以下: windows 1、用Clazard創建一個框架類CTestFrame基類選CMDIChildWnd。 api 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);取的就是第二個子串,文檔名稱。 文檔創建以後咱們就能夠對其進行操做了。固然文檔類和視窗類,文檔類和主窗口類,以及不一樣文檔類之間進行通訊也是較爲複雜的,並不是幾句話就能說清楚,如不熟悉文檔視窗的讀者請參看其它有關資料。 2、 重疊圖象拼接技術 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; } } 3、 基於多文擋視窗模型的重疊圖象拼接技術 程序在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,它與該點距重疊邊界的距離有關。 4、 多文擋視窗模型的重疊圖象拼接程序框架 1. 程序構成 程序除應用類、主窗口類之外,還包括CFristImageDoc, CSecondImageDoc, CThirdImageDoc類用來保存第一幅、第二副以及拼接後圖象的數據。還有與其相連的文檔類和框架類。 2. 程序流程 程序的主要工做在應用類中完成,首先打開第一幅圖象,圖象的顯示等操做由CFirstImageDoc 類和與其相關的視窗類及框架類完成,並將圖象的位數組指針和圖象大小傳給應用類成員變量。再打開第二幅圖象一樣完成顯示等操做,也將位數組指針和圖象大小傳給應用類成員變量。在應用類中完成圖象的匹配對準工做,最後實現圖象的合成。將合成後的圖象傳給CThirdImageDoc類進行顯示當用戶對拼接結果基本滿意後,能夠選擇平滑鏈接將兩幅圖象平滑的鏈接起來。用戶可將最後的結果保存成bmp文件。 5、 總結 圖象拼接中,圖象對準是前提和關鍵,程序基於網格匹配的方法實現了圖象對準,應用了交互技術讓用戶能夠對網格點的多少,網格間距大小,都可調整,還容許用戶輸入重疊範圍使拼接過程有的放矢,缺省是較小圖象高度的1/3。該程序實現了對位圖圖象文件的各類操做,具備廣泛意義,還引入了不一樣文檔類型的多文檔視窗模型,並在不一樣文檔類間實現了數據交換,具備必定的實用價值。爲了使程序簡單易用,只實現了對24位位圖的拼接,也未提供垂直方向的拼接,只提供了水平方向的拼接。如要對不符合條件的圖象進行拼接咱們只需在Windows提供的畫筆中將圖象轉化一下便可
|
||
-- 做者:admin -- 發佈時間:2006-8-11 16:06:00 -- 在VC下顯示JPEG、GIF格式圖像的一種簡便方法 1、 引言 JPEG圖像壓縮標準隨然是一種有損圖像壓縮標準,但因爲人眼視覺的不敏感,經壓縮後的畫質基本沒有發生變化,很快便以較高的壓縮率獲得了普遍的承認。GIF格式雖然僅支持256色但它對於顏色較少的圖像有着很高的壓縮率,甚至超過JPEG標準,也獲得了普遍的認同。但做爲衆多程序員的一個重要的開發工具--Microsoft Visual C++ 6.0的MFC庫卻僅對沒有通過任何壓縮的BMP位圖文件有着良好的支持,能夠讀取、顯示、存儲甚至在內存中建立一塊內存位圖。因爲BMP格式的圖像沒有通過任何的壓縮,不管是做爲程序的外部文件,仍是做爲程序的內部資源都要佔據大量的空間,尤爲是後者會大大增長可執行文件的長度。能夠看出,若是能用通過壓縮、具備較好的壓縮率的JPEG或GIF格式的圖像來取代BMP文件在VC中的應用,無疑仍是頗有吸引力的。 2、 設計思路 雖然有一些操做、處理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中用到的接口指針變量的地址); 3、 具體的實現 在顯示圖像以前,首先要獲取到圖像文件的存放路徑,這裏採用標準的文件打開對話框來選取圖像文件,文件名存放在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控件的支持。
|
||
-- 做者:admin -- 發佈時間:2006-8-11 16:07:00 -- Visual C++實現Flash動畫播放
|
||
-- 做者:admin -- 發佈時間:2006-8-11 16:08:00 -- 用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); }
5、映射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; } 6、在APP類裏的initInstance()裏添加下面函數改變對話框背景。 SetDialogBkColor(RGB(206,227,99)); 7、爲了美觀咱們的外形可引入CButtonXP類,將個按鈕類型設置爲CButtonXP 好了,應該大功告成了!調試一下吧! 本程序在調試過程當中要多謝杜修杏老師的指點
|
||
-- 做者:admin -- 發佈時間:2006-8-11 16:09:00 -- 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 標題爲"釋放鼠標"的控件的響應函數代碼爲: ClipCursorNULL 這行代碼很是簡單,就是使鼠標能夠自由移動,不受限制。 把以上程序編譯好了之後運行,點下"控制鼠標移動範圍"按鈕,鼠標就只能在這個窗口的範圍之內活動,離不開這個窗口了,點下"釋放鼠標"按鈕,鼠標就恢復正常了。 知道了如何控制鼠標範圍,該講講如何移動鼠標了。移動鼠標很是簡單,只須要一個API函數SetCursorPos,這個函數有2個參數,第1個參數是屏幕的x座標,第2個參數是屏幕的y座標,它能夠把鼠標移動到指定的座標上去。 模擬鼠標的點擊功能也很是簡單,好比模擬點鼠標右鍵,能夠使用下面兩行代碼: mouse_eventMOUSEEVENTF_RIGHTDOWN0000 mouse_eventMOUSEEVENTF_RIGHTUP0000 mouse_even t函數的功能就是模擬鼠標點擊,第1行代碼是模擬按下鼠標右鍵,第2行代碼是模擬鼠標右鍵彈起,這兩行代碼就模擬了1次點擊鼠標右鍵的操做,若是想模擬點鼠標左鍵的操做,只要以上兩行代碼中的MOUSEEVENTF_RIGHTDOWN和MOUSEEVENTF_RIGHTUP參數換成MOUSEEVENTF_LEFTDOWN和MOUSEEVENTF_LEFTUP就能夠了。 鼠標的模擬操做講完了。以上的程序在Windows98下,使用Visual V++6.0編譯成功,調試正常。 本期知識點:控制鼠標的API函數。
|
||
-- 做者:admin -- 發佈時間:2006-8-11 16:11:00 -- 用VC實現圖象漸顯和漸隱摘 要 圖象的漸顯/漸隱被普遍運用與圖象處理和多媒提娛樂軟件。本文基於windows的調色板動畫和時間碼技術設計了通用的圖象漸顯和漸隱算法,並實現了其visual c++程序編碼。 關鍵詞 漸顯、漸隱、調色板、調色板動畫、時間碼 圖象的漸顯/漸隱是十分重要的圖象效果,普遍運用於圖象處理和多媒提娛樂軟件。漸顯/漸隱算法設計的最大困難是速度控制,包括定時和快速改變圖象中各象素的顏色。如採用普通的全圖掃描算法,則速度較慢,很難真正體現漸顯/漸隱效果。 利用windows(3.x.95/98/nt)操做系統特殊的調色板管理和時間碼定時機制能設計出有效的圖象漸顯/漸隱算法。windows提供一種被稱爲調色板動畫(palette animation)的顏色處理技術,它經過快速改變顏色調色板中所選取的表項中的顏色能模擬顏色的變化。設置時間碼,定時調用該技術使圖象顏色漸變就能實現圖象的漸顯和漸隱。 1、調色板動畫 在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中將變化的表項數。 2、時間碼定時 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函數解釋和分發除指定時間碼消息以外的全部消息,以免丟失消息。 3、漸顯 漸顯就是將顯示顏色由黑色(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); } 4、漸隱 漸隱就是將顯示顏色由圖象各象素的顏色逐漸變化爲黑色(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);
|
||
n 做者:admin -- 發佈時間:2006-8-11 16:11:00 n -- 用VC進行屏幕截取編程 n ---- 屏幕截取是使人比較感興趣的事情.雖然如今有很多應用程序如HYPERSNAP等能夠用來截取你所喜歡的屏幕畫面,可是若是能把這個功能加到本身的程序中,就更能利用它強大的做用. n ---- 下面用VC來逐步介紹在Windows95下的實現過程.首先咱們要肯定屏幕截取的區域,用LPRECT結構來定義.能夠截取一個窗口,或整個屏幕.如下代碼把選定的屏幕區域拷貝到位圖中. n HBITMAP CopyScreenToBitmap(LPRECT lpRect) n //lpRect 表明選定區域 { HDC hScrDC, hMemDC; n // 屏幕和內存設備描述表 HBITMAP hBitmap, hOldBitmap; n // 位圖句柄 int nX, nY, nX2, nY2; n // 選定區域座標 int nWidth, nHeight; n // 位圖寬度和高度 int xScrn, yScrn; n // 屏幕分辨率 n // 確保選定區域不爲空矩形 n if (IsRectEmpty(lpRect)) return NULL; n //爲屏幕建立設備描述表 n hScrDC = CreateDC("DISPLAY", NULL, NULL, NULL); n //爲屏幕設備描述表建立兼容的內存設備描述表 n hMemDC = CreateCompatibleDC(hScrDC); n // 得到選定區域座標 n nX = lpRect- >left; n nY = lpRect- >top; n nX2 = lpRect- >right; n nY2 = lpRect- >bottom; n // 得到屏幕分辨率 n xScrn = GetDeviceCaps(hScrDC, HORZRES); n yScrn = GetDeviceCaps(hScrDC, VERTRES); n //確保選定區域是可見的 n if (nX 〈0) nX = 0; n if (nY 〈 0) n nY = 0; n if (nX2 > xScrn) n nX2 = xScrn; n if (nY2 > yScrn) n nY2 = yScrn; n nWidth = nX2 - nX; n nHeight = nY2 - nY; n // 建立一個與屏幕設備描述表兼容的位圖 n hBitmap = CreateCompatibleBitmap (hScrDC, nWidth, nHeight); n // 把新位圖選到內存設備描述表中 n hOldBitmap = SelectObject(hMemDC, hBitmap); n // 把屏幕設備描述表拷貝到內存設備描述表中 n BitBlt(hMemDC, 0, 0, nWidth, nHeight, hScrDC, nX, nY, SRCCOPY); n //獲得屏幕位圖的句柄 n hBitmap = SelectObject(hMemDC, hOldBitmap); n //清除 DeleteDC(hScrDC); DeleteDC(hMemDC); n // 返回位圖句柄 n return hBitmap; } n 獲得屏幕位圖句柄之後,咱們 能夠把屏幕內容粘貼到剪貼板上. n if (OpenClipboard(hWnd)) n //hWnd爲程序窗口句柄 n { n //清空剪貼板 n EmptyClipboard(); n //把屏幕內容粘貼到剪貼板上, hBitmap 爲剛纔的屏幕位圖句柄 n SetClipboardData(CF_BITMAP, hBitmap); n //關閉剪貼板 n CloseClipboard(); } n 咱們也能夠把屏幕內容以位圖格式存到磁盤文件上. n int SaveBitmapToFile(HBITMAP hBitmap , LPSTR lpFileName) n //hBitmap 爲剛纔的屏幕位圖句柄 n { n //lpFileName 爲位圖文件名 n HDC hDC; n //設備描述表 n int iBits; n //當前顯示分辨率下每一個像素所佔字節數 n WORD wBitCount; n //位圖中每一個像素所佔字節數 n //定義調色板大小, 位圖中像素字節大小 , 位圖文件大小 , 寫入文件字節數 n DWORD dwPaletteSize=0, dwBmBitsSize, dwDIBSize, dwWritten; n BITMAP Bitmap; n //位圖屬性結構 n BITMAPFILEHEADER bmfHdr; n //位圖文件頭結構 n BITMAPINFOHEADER bi; n //位圖信息頭結構 n LPBITMAPINFOHEADER lpbi; n //指向位圖信息頭結構 n HANDLE fh, hDib, hPal,hOldPal=NULL; n //定義文件,分配內存句柄,調色板句柄 //計算位圖文件每一個像素所佔字節數 n hDC = CreateDC("DISPLAY",NULL,NULL,NULL); n iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES); n DeleteDC(hDC); n if (iBits 〈 = 1) wBitCount = 1; n else if (iBits 〈 = 4) n wBitCount = 4; n else if (iBits 〈 = 8) wBitCount = 8; n else if (iBits 〈 = 24) wBitCount = 24; n //計算調色板大小 n if (wBitCount 〈 = 8) n dwPaletteSize = (1 〈 〈 wBitCount) * sizeof(RGBQUAD); n //設置位圖信息頭結構 n GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap); n bi.biSize = sizeof(BITMAPINFOHEADER); n bi.biWidth = Bitmap.bmWidth; n bi.biHeight = Bitmap.bmHeight; n bi.biPlanes = 1; n bi.biBitCount = wBitCount; n bi.biCompression = BI_RGB; n bi.biSizeImage = 0; n bi.biXPelsPerMeter = 0; n bi.biYPelsPerMeter = 0; n bi.biClrUsed = 0; n bi.biClrImportant = 0; n dwBmBitsSize = ((Bitmap.bmWidth * wBitCount+31)/32)* 4 *Bitmap.bmHeight ; n //爲位圖內容分配內存 n hDib = GlobalAlloc(GHND,dwBmBitsSize+ dwPaletteSize+sizeof(BITMAPINFOHEADER)); n lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib); n *lpbi = bi; n // 處理調色板 n hPal = GetStockObject(DEFAULT_PALETTE); n if (hPal) { n hDC = GetDC(NULL); n hOldPal = SelectPalette(hDC, hPal, FALSE); RealizePalette(hDC); n } n // 獲取該調色板下新的像素值 n GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER) +dwPaletteSize, (BITMAPINFOHEADER *) lpbi, DIB_RGB_COLORS); n //恢復調色板 n if (hOldPal) { n SelectPalette(hDC, hOldPal, TRUE); n RealizePalette(hDC); n ReleaseDC(NULL, hDC); } n //建立位圖文件 n fh = CreateFile(lpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_ FLAG_SEQUENTIAL_SCAN, NULL); n if (fh == INVALID_HANDLE_VALUE) n return FALSE; n // 設置位圖文件頭 n bmfHdr.bfType = 0x4D42; n // "BM" n dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize; n bmfHdr.bfSize = dwDIBSize; n bmfHdr.bfReserved1 = 0; n bmfHdr.bfReserved2 = 0; n bmfHdr.bfOffBits = (DWORD)sizeof (BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize; n // 寫入位圖文件頭 n WriteFile(fh, (LPSTR)&bmfHdr, sizeof (BITMAPFILEHEADER), &dwWritten, NULL); n // 寫入位圖文件其他內容 n WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL); n //清除 n GlobalUnlock(hDib); n GlobalFree(hDib); n CloseHandle(fh);
|
||
-- 做者:admin -- 發佈時間:2006-8-11 16:12:00 -- 用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; 哎呀,腰痠背疼,眼球發澀,手背奇麻(不會吧)!不過,相信你必定會火燒眉毛地按下Ctrl+F5, 看着一幅幅圖片在你面前輪番展現,啊,本身的屏幕保護程序!趕快趕快,換上自制的屏保,感受就是不同:圖片任你挑,時間間隔任你改,鼠標?鍵盤?我想響應誰就響應誰……哎呀,誰扔的紙團:(。 其實,上面的程序還有不少能夠改進的地方,好比圖片老是單一地顯示;bmp 文件太大,致使生成的屏幕保護程序也很大,遠沒有jpg合算;沒有密碼,沒有可直接控制的界面。因爲InitInstance()函數的簡單處理(直接調用CMyWnd類),你會發現當你在桌面上右擊,選擇「屬性」、「屏幕保護程序」頁、「屏幕保護程序」下拉菜單、選中MyScreensaver時,MyScreensaver就直接預覽了(或是直接運行了);假設你肯定MyScreensaver做爲你的屏幕保護程序,等你第二次進入「屏幕保護程序」頁時,就直接預覽。Why? 回頭看看InitInstance()函數就明白了。爲了讓它更聽話地工做,可修改InitInstance()函數: LPTSTR lpszArgv = __argv[1]; if (lpszArgv[0] ==‘/’) lpszArgv++; if (lstrcmpi(lpszArgv, _T(「s」))==0) { CMyWnd* pWnd=new CMyWnd; pWnd->Create(); m_pMainWnd=pWnd; return TRUE; } return FALSE; 不過如今你要是再在VC中運行這個程序,「該程序執行了非法操做,即將關閉。若是仍有問題,請與我聯繫(??)」將會伴隨着一超重低音供你欣賞。(啊?)緣由是咱們加了一句return FALSE; 還有,別忘了還有一個CMyScreensaverDlg類沒有用上,用它來與你的屏保直接對話再好不過了。例如,爲了方便地肯定時間間隔,選取圖片,加上一個編輯框和幾個按鈕就能夠了。重申一點,因爲生成文件較大,佔用的內存也多,若是不能運行,極可能是開的窗口太多了。這時你能夠換較小的圖片。有任何問題請來信:toxyz@163.net。晚些時候我將在家門口 http://yxz.163.net 放置上述例子的整個項目,有一些新玩意和你們共同探討。(全文完)
|
||
-- 做者:admin -- 發佈時間:2006-8-11 16:13:00 -- 在VC中自建操做BMP位圖文件類有編程經驗的程序員都知道:要使應用程序的界面美觀不可避免的要使用大量位圖。如今流行的可視化編程工具對位圖的使用提供了很好的支持,被稱爲三大可視化開發工具的VB、VC、Delphi經過封裝位圖對象對位圖使用提供了很好的支持:VB提供了兩個功能很強的對象:PictureBox及Image,經過使用它們,裝載、顯示位圖變得很是容易。Delphi中也提供了一個位圖對象:TImage,它的功能與用法與VB中的Image相似。在VC中經過使用設備相關類CDC與GDI對象類CBitmap來完成位圖的操做。 然而在VC中使用CBitmap類必須將BMP位圖裝入資源中,而後經過類 CBitmap的成員函數使用它,在經過CDC類的成員函數操做它。這樣作有兩點缺陷:將位圖裝入資源致使可執行文件增大,不利於軟件發行;只能使用資源中有限的位圖,沒法選取其它位圖。並且BMP位圖文件是以DIB(設備無關位圖)方式保存,BMP位圖裝入資源後被轉換爲DDB(設備相關位圖),類CBitmap就是對一系列DDB操做的API函數進行了封裝,使用起來有必定的侷限性,不如DIB能夠獨立於平臺特性。 要彌補使用資源位圖的兩點不足,就必須直接使用BMP位圖文件。VC的示例中提供了一種方法讀取並顯示BMP位圖文件,但使用起來至關的麻煩。首先使用API函數GlobalAlloc分配內存並建立HDIB位圖句柄,全部操做只能直接讀寫內存,而後經過StrechDIBits及SetDIBsToDevice函數來顯示於屏幕上,操做起來費時費力。 所以筆者經過研究類CBitmap的封裝與DIB結構,使用Win32中提供的新函數,創建了一個專用於操做BMP文件的類,並且徹底仿照類CBitmap的實現:從類CGdiObject派生,新類的全部接口與類CBitmap 的部分接口徹底相同。這樣對於習慣使用CBitmap類接口用法的程序員來講二者的接口在使用上沒有什麼分別。 首先咱們先簡單介紹一下DIB的結構。DIB位圖既能夠存在於內存,也能夠以文件形式保存在磁盤上(BMP文件)。全部DIB都包含兩部分信息:位圖信息(BITMAPINFO),包括位圖信息頭和顏色表;位圖數據。對於內存中DIB的只要有上述兩部分就行,而對於DIB文件則還要加上位圖文件頭。 其次,Win32中提供了一個新函數CreateDIBSection,經過它能夠建立一個存儲DIB位的內存區域,既能夠執行相應的GDI操做,又能夠直接經過指向DIB位區域的指針方位DIB位區域。這是一個很是有用的函數,經過它咱們能夠用DIB替代DDB。 在瞭解了相應的知識後,咱們能夠本身由類CGdiObject派生一個操做BMP文件的類:CBitmapFile。 在本身編寫類時有兩點值得注意: 在BitmapFile.h文件中定義類CBitmapFile,首先必須聲明類CBitmapFile是從類CGdiObject中公有派生。而後在類中首先使用宏DECLARE_DYNAMIC(CBitmapFile)代表新類的最高父類是類CObject,是符合MFC的類庫規範。緊接着宏DECLARE_DYNAMIC的是聲明靜態函數FromHandle,這兩個聲明必須放在類定義的最前面。 在BitmapFile.cpp文件中類的成員函數的實現前加上IMPLEMENT_DYNAMIC(CBitmapFile,CGdiObject);代表類CBitmapFile直接派生於類CGdiObject。 在類CBitmapFile的聲明中有三個函數與類Cbitmap中的定義稍有不一樣: 在類CbitmapFile中LoadBitmap函數的參數是LPCTSTR型,保存的是BMP文件的文件名。 在類CbitmapFile中CreateBitmap函數的參數中少了參數nPlanes,在函數內部默認爲1。 在類CbitmapFile中CreateBitmapIndirect函數的參數中多了參數lpBits,它指向指定位圖DIB位的內存區域。 在成員函數中最重要的是函數CreateBitmapIndirect和函數LoadBitmap: 在函數CreateBitmapIndirect中使用函數CreateDIBSection建立了一個以兼容DC爲基礎的HBITMAP句柄,並用繼承自類CGdiObject 的函數Attach把它與類CGdiObject的句柄m_hObject關聯起來。而後將指定位圖的DIB位圖數據拷貝到由函數CreateDIBSection建立的DIB位的內存區域。 在函數LoadBitmap中首先從指定文件名的文件中讀取以結構BITMAPFILEHEADER爲大小的數據塊,而後由文件頭標誌判斷文件是否爲BMP位圖文件,而後由BITMAPFILEHEADER中bfSize保存的文件大小與文件的真實大小比較文件是否有損壞,再由BITMAPFILEHEADER中bfOffBits與BITMAPFILEHEADER結構大小相減計算出位圖信息頭和顏色表一共的大小,動態申請一塊空間保存位圖信息頭和顏色表信息,再由BITMAPFILEHEADER中bfSize與bfOffBits相減計算出DIB位圖數據的大小,動態申請一塊空間保存DIB位圖數據,最後調用成員函數CreateBitmapIndirect來建立DIB位圖。 在應用程序的OnPaint()事件中繪製DIB位圖的方法與使用類CBitmap時繪製位圖的方法徹底相同,但有一點要注意的是因爲CDC類沒有提供返回新類CBitmapFile指針類型的將DIB位圖選入內存的SelectObject函數,因此在使用SelectObject時要將返回類型強制轉換爲CbitmapFile *類型。 至此,關於新類CBitmapFile編寫中的一些要點和使用時一些要注意的問題就介紹這麼多了。 附源文件 // // 文件描述:定義類CBitmapFile,此類是用於讀取BMP文件,涉及讀取、 // 創建及一系列經常使用的操做。 // 文件名: BitmapFile.h // 時間: 1999-2-11 // 做者: 賈暾 // #ifndef _CBITMAPFILE_H_ #define _CBITMAPFILE_H_ class CBitmapFile : public CGdiObject { DECLARE_DYNAMIC(CBitmapFile) public: static CBitmapFile* PASCAL FromHandle(HBITMAP hBitmap); // Constructors CBitmapFile(); BOOL LoadBitmap(LPCTSTR lpszFileName); BOOL CreateBitmap(int nWidth, int nHeight, UINT nBitCount, const void* lpBits); BOOL CreateBitmapIndirect(LPBITMAPINFO lpBitmapInfo, const void* lpBits); // Attributes operator HBITMAP() const; int GetBitmap(BITMAP* pBitMap); protected: // Attributes int GetColorNumber(WORD wBitCount); public: // Operations DWORD SetBitmapBits(DWORD dwCount, const void* lpBits); DWORD GetBitmapBits(DWORD dwCount, LPVOID lpBits); // Implementation public: virtual ~CBitmapFile(); }; #endif // // 文件描述:類CBitmapFile內成員函數的實現 // 文件名: BitmapFile.cpp // 時間: 1999-2-11 // 做者: 賈暾 // #include "BitmapFile.h" #include <memory.h> IMPLEMENT_DYNAMIC(CBitmapFile,CGdiObject); CBitmapFile* PASCAL CBitmapFile::FromHandle(HBITMAP hBitmap) { return (CBitmapFile*) CGdiObject::FromHandle(hBitmap); }
CBitmapFile::CBitmapFile() { }
BOOL CBitmapFile::LoadBitmap(LPCTSTR lpszFileName) { CFile file; if(!file.Open(lpszFileName,CFile::modeRead|CFile::shareDenyWrite)) { MessageBox(NULL,"BMP file open error!","warning",MB_OK); return FALSE; } BITMAPFILEHEADER bfhHeader; file.Read(&bfhHeader,sizeof(BITMAPFILEHEADER)); if(bfhHeader.bfType!=((WORD) (/'M/'<<8)|/'B/')) { MessageBox(NULL,"The file is not a BMP file!","warning",MB_OK); return FALSE; } if(bfhHeader.bfSize!=file.GetLength()) { MessageBox(NULL,"The BMP file header error!","warning",MB_OK); return FALSE; } UINT uBmpInfoLen=(UINT) bfhHeader.bfOffBits-sizeof(BITMAPFILEHEADER); LPBITMAPINFO lpBitmap=(LPBITMAPINFO) new BYTE[uBmpInfoLen]; file.Read((LPVOID) lpBitmap,uBmpInfoLen); if((* (LPDWORD)(lpBitmap))!=sizeof(BITMAPINFOHEADER)) { MessageBox(NULL,"The BMP is not Windows 3.0 format!","warning",MB_OK); return FALSE; } DWORD dwBitlen=bfhHeader.bfSize - bfhHeader.bfOffBits; LPVOID lpBits=new BYTE[dwBitlen]; file.ReadHuge(lpBits,dwBitlen); file.Close(); BOOL bSuccess=CreateBitmapIndirect(lpBitmap, lpBits); delete lpBitmap; delete lpBits; if(!bSuccess) return FALSE; return TRUE; }
BOOL CBitmapFile::CreateBitmap(int nWidth, int nHeight, UINT nBitCount, const void* lpSrcBits) { ASSERT(nBitCount==1||nBitCount==4||nBitCount==8 ||nBitCount==16||nBitCount==24||nBitCount==32); LPBITMAPINFO lpBitmap; lpBitmap=(BITMAPINFO*) new BYTE[sizeof(BITMAPINFOHEADER) + GetColorNumber(nBitCount) * sizeof(RGBQUAD)]; lpBitmap->bmiHeader.biSize=sizeof(BITMAPINFOHEADER); lpBitmap->bmiHeader.biWidth=nWidth; lpBitmap->bmiHeader.biHeight=nHeight; lpBitmap->bmiHeader.biBitCount=nBitCount; lpBitmap->bmiHeader.biPlanes=1; lpBitmap->bmiHeader.biCompression=BI_RGB; lpBitmap->bmiHeader.biSizeImage=0; lpBitmap->bmiHeader.biClrUsed=0; BOOL bSuccess=CreateBitmapIndirect(lpBitmap, lpSrcBits); delete lpBitmap; if(!bSuccess) return FALSE; return TRUE; } BOOL CBitmapFile::CreateBitmapIndirect(LPBITMAPINFO lpBitmapInfo, const void* lpSrcBits) { DeleteObject(); LPVOID lpBits; CDC *dc=new CDC; dc->CreateCompatibleDC(NULL); HBITMAP hBitmap=::CreateDIBSection(dc->m_hDC,lpBitmapInfo,DIB_RGB_COLORS, &lpBits,NULL,0); ASSERT(hBitmap!=NULL); delete dc; Attach(hBitmap); BITMAP bmp; GetBitmap(&bmp); DWORD dwCount=(DWORD) bmp.bmWidthBytes * bmp.bmHeight; if(SetBitmapBits(dwCount,lpSrcBits)!=dwCount) { MessageBox(NULL,"DIB build error!","warning",MB_OK); return FALSE; } return TRUE; } CBitmapFile::operator HBITMAP() const { return (HBITMAP)(this == NULL ? NULL : m_hObject); } int CBitmapFile::GetBitmap(BITMAP* pBitMap) { ASSERT(m_hObject != NULL); return ::GetObject(m_hObject, sizeof(BITMAP), pBitMap); }
int CBitmapFile::GetColorNumber(WORD wBitCount) { ASSERT(wBitCount==1||wBitCount==4||wBitCount==8 ||wBitCount==16||wBitCount==24||wBitCount==32); { case 1: return 2; case 4: return 16; case 8: return 256; default: return 0; } }
DWORD CBitmapFile::SetBitmapBits(DWORD dwCount, const void* lpBits) { if(lpBits!=NULL) { BITMAP bmp; GetBitmap(&bmp); memcpy(bmp.bmBits,lpBits,dwCount); return dwCount; } else return 0; }
DWORD CBitmapFile::GetBitmapBits(DWORD dwCount, LPVOID lpBits) { if(lpBits!=NULL) { BITMAP bmp; GetBitmap(&bmp); memcpy(lpBits,bmp.bmBits,dwCount); return dwCount; } else return 0; }
CBitmapFile::~CBitmapFile() { CGdiObject::DeleteObject();
|
||
-- 做者:admin -- 發佈時間:2006-8-11 16:16:00 -- VC實現多格式圖像的轉換色彩鮮豔漂亮的高品質圖像,一個個形象生動的Windows圖標,高速運動、活靈活現的三維動畫,這些無一不顯示出程序設計者的藝術才華。在程序設計中,圖像處理已經成爲每一個程序員的必修課。 在VC中編程顯示一幅位圖,下列步驟是不可缺乏的: 裝入位圖、得到位圖的大小信息、啓用設備環境、位傳輸等,所需的程序代碼通常比較冗長並且複雜。若是想將裝入的位圖另存爲其餘格式的圖像文件,代碼就更長了。這一切都是由於GDI自己的侷限性形成的。 GDI+技術 隨着Windows 2000的推出,上述狀況有了極大的改觀: 程序員沒必要了解每種圖像格式的具體含義,照樣能夠寫出多格式圖像瀏覽或轉換程序,這一切全都依賴於Windows 2000及後繼版中所使用的GDI+技術。 和傳統的GDI不一樣,GDI+中引入了對COM(組件對象模型)技術的支持,經過COM技術,GDI+簡化了對圖像文件的訪問(打開、保存)。它是經過調用COM組件來實現的,GDI+扮演的只是指揮者,而非操做員。對於圖像文件,GDI+所關心的不是圖像文件的文件頭信息,不論要打開的文件格式是什麼類型,GDI+首先要作的是在註冊表中查看該圖像格式的編碼(或解碼)信息是否已經註冊(HKEY_CLASSES_ROOT//MIME//Database//Content Type)。若是已經註冊,就經過該編碼信息調用COM組件,就這麼簡單。這種技術早就在微軟的其餘軟件中使用了(如IE)。「體驗」過Nimda病毒的朋友可能對「audio/wav」這段代碼並不陌生,Nimda就是靠它來假裝本身的:讓IE認爲附件是WAV文件而自動打開可執行程序,這其實也是IE使用COM技術的一個突出表現。 配合GDI+的推出,微軟也同時發佈了相應的SDK,若是已經安裝了最新的Microsoft PlatForm SDK或已經開始使用VS.NET,GDI+的SDK就已經在系統中了。若是沒有的話,能夠到http: //noner.top263.net/progtool上去下載GDI+的頭文件和庫文件。有了GDI+以後,只需簡單地建立一個圖形對象(Graphics object),而後直接調用該對象的方法(methods)進行繪圖便可。圖形對象是GDI+中的核心,正如DC之於GDI那樣。圖形對象和DC有許多類似的地方,在使用上遵循着相同的使用規則,可是二者在本質上已經有很大的區別。一個是基於句柄的GDI,一個是基於組件對象模型的GDI+。使用GDI+的SDK編程,必須按照下面的規範來進行:使用GDI+的命名空間(namespace Gdiplus),在使用GDI+函數時必須進行GDI+的初始化,使用完畢要銷燬GDI+,這些規範在下面所列的程序中有詳細的說明。 訪問註冊表編碼信息 上面說到GDI+是經過在註冊表中查看編碼信息來訪問圖像文件的,在GDI+的SDK中,編碼信息是存儲在 ImageCodecInfo類中的,在這個類中,有編碼的CLSID(COM組件的GUID標識碼)、編碼方式描述等。對於GDI,在註冊表中訪問編碼信息一般使用如下兩個函數來實現: 1. 查看系統中可用的圖像編碼信息(數量及大小) Status GetImageEncodersSize( UINT* numEncoders, //存儲編碼器數量的地址 UINT* size //存儲編碼信息所需內存大小 ); 2. 獲得全部的編碼信息 Status GetImageEncoders( UINT numEncoders, //可用編碼器數量 UINT size, //存儲編碼器信息所需內存(由ImageCodecInfo類組成的數組的大小) ImageCodecInfo* encoders //編碼器信息指針 ); 在GetImageEncoders函數中,參數numEncoders和size都是由GetImageEncodersSize返回的。下面的代碼在註冊表中查找具體格式圖像的編碼方式: int GetImageCLSID(const WCHAR* format, CLSID* pCLSID) { //獲得格式爲format的圖像文件的編碼值,訪問該格式圖像的COM組件的GUID值保存在pCLSID中 UINT num = 0; UINT size = 0; ImageCodecInfo* pImageCodecInfo = NULL; GetImageEncodersSize(&num, &size); if(size == 0) return FALSE; // 編碼信息不可用 //分配內存 pImageCodecInfo = (ImageCodecInfo*)(malloc(size)); if(pImageCodecInfo == NULL) return FALSE; // 分配失敗 //得到系統中可用的編碼方式的全部信息 GetImageEncoders(num, size, pImageCodecInfo); //在可用編碼信息中查找format格式是否被支持 for(UINT i = 0; i < num; ++i) { //MimeType:編碼方式的具體描述 if( wcscmp(pImageCodecInfo[i] .MimeType, format) == 0 ) { *pCLSID = pImageCodecInfo[i] .Clsid; free(pImageCodecInfo); return TRUE; } } free(pImageCodecInfo); return FALSE; } 實現多格式的圖像瀏覽和轉換 有了前面的知識,實現多格式的圖像的瀏覽與轉換就不是什麼難事了。 1. 在VC中建立一個SDI項目ImageShow,對GDI+聲明和初始化及銷燬進行代碼編制,具體代碼以下: #include 「Gdiplus.h」 using namespace Gdiplus; CImageShowView::CImageShowView() { //初始化GDI+ GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); } CImageShowView::~CImageShowView() { //銷燬GDI+ ULONG_PTR gdiplusToken; GdiplusShutdown(gdiplusToken); } 2. 經過類嚮導(Class Wizard),重載「文件」菜單中的「打開」和「另存爲」兩項。爲了編程的簡單,本程序只將當前打開的圖像文件直接存爲BMP文件(實際上保存爲其餘格式的文件也很簡單,只不過是對文件名進行分析而已)。另外,爲了在打開和保存文件時進行文件名的傳遞,還應在CImageShowView類中加入一全局變量「CString strOpenFileName」。「打開」和「另存爲」兩菜單的對應代碼以下: WCHAR* ToWChar(char * str) { //在GDI+中,有關字符的參數類型所有都是WCHAR類型 //該函數是將傳統字符串進行轉換 static WCHAR buffer[1024]; wcsset(buffer,0); MultiByteToWideChar(CP_ACP,0,str,strlen(str),buffer,1024); return buffer; }
void CImageShowView::OnFileOpen() { //本程序可以打開各種常見格式的圖像文件 static char szFilter[]=「常見格式圖像文件(*.*)|*.*|」; CFileDialog dlgChoseImage(1,NULL,NULL,NULL,szFilter); if(dlgChoseImage.DoModal()==IDOK) { strOpenFileName=dlgChoseImage .GetPathName(); //打開文件後當即在窗口中顯示(重繪客戶窗口) this->Invalidate(); } }
void CImageShowView::OnFileSaveAs() { if(strOpenFileName.IsEmpty()) { AfxMessageBox(「當前沒有打開圖像文件,不能進行保存!」); return; } //創建圖形對象 Graphics graphics(GetDC()->m_hDC); //裝入當前已經打開的圖像文件 Image image(ToWChar(strOpenFileName.GetBuffer(strOpenFileName.GetLength()))); CString strFileSave; //將其餘格式的圖像所有另存爲BMP文件 static char szFilter[]=「位圖(*.BMP)|*.BMP|」; CFileDialog dlgChoseImage(0,「BMP」,NULL,NULL,szFilter); if(dlgChoseImage.DoModal()==IDOK) { strFileSave=dlgChoseImage.GetPathName(); CLSID clsid; if(GetImageCLSID(L「image/bmp」,&clsid)) { image.Save(ToWChar(strFileSave.GetBuffer(strFileSave.GetLength())), &clsid, NULL); //將保存後的圖像進行顯示 strOpenFileName=strFileSave; this->Invalidate(); } } } 2. 爲了瀏覽圖像轉換先後的效果,還應該在窗口中分別繪製轉換先後的圖像,這須要在OnDraw函數中添加繪製代碼,以下所示: void CImageShowView::OnDraw(CDC* pDC) { CImageShowDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); //若是沒有選擇顯示圖像文件,則不用重繪 if(strOpenFileName.IsEmpty()) return; //顯示當前打開的圖像文件的全名 this->GetParent()->SetWindowText(strOpenFileName); //創建圖像對象 Graphics graphics(pDC->m_hDC); //裝入圖形文件 Image image(ToWChar(strOpenFileName.GetBuffer(strOpenFileName.GetLength()))); Point destPoints[3] = { Point(0, 0), Point(image.GetWidth(), 0), Point(0, image.GetHeight()) }; Point* pdestPoints = destPoints; //在指定區域pdestPoints顯示圖像 graphics.DrawImage(&image, pdestPoints, 3); } 在編譯上面的程序以前,應該將Gdiplus.lib文件連編到項目中去,不然將會出現「LINK 2001」編譯錯誤。上述程序在Visual Studio 6.0、Windows 2000/XP下調試經過,它可以顯示或轉換的圖像格式有BMP、GIF、JPEG、Exif、PNG、TIFF、ICON、WMF、EMF等等。須要說明的是,本文只就GDI+編程的基本原理進行了闡述,事實上,GDI+的應用遠不止此。 結束語 若是對本程序進行些改進,還能夠編制出功能更增強大的圖像處理程序。本文中所提到的程序,在筆者的主頁「國稅之家」(http://nationaltax.home.chinaren.com)的「我的世界」中能夠下載到。有關GDI+編程的幫助信息,你們能夠到微軟的MSDN網站去查閱
|
||
-- 做者:admin -- 發佈時間:2006-8-11 16:17:00 -- ado方式訪問帶密碼數據庫的方法主要代碼: CString strSql; TCHAR FileName[MAX_PATH],errMsg[MAX_PATH]; ::CoInitialize(NULL); //初始化Com IADORecordBinding *picRs = NULL; _RecordsetPtr pRs("ADODB.Recordset"); _ConnectionPtr pConn("ADODB.Connection" ); //CFootballTeamRs rsFootballTeam; GetModuleFileName(NULL,FileName,MAX_PATH); (_tcsrchr(FileName,/'/////'))[1] = 0; lstrcat(FileName,_T("pass.mdb")); strSql = strSql + "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source = " + FileName + ";Persist Security Info=False;Jet OLEDB:Database Password=cd;"; //pConn->ConnectionString = chDataSource; //pConn->Provider = _T("Microsoft.Jet.OLEDB.4.0"); CString sqlSentence = _T("select * from pass"); try { pConn->Open((_bstr_t)strSql, "", "", adModeUnknown); pRs->QueryInterface( __uuidof(IADORecordBinding), (LPVOID*)&picRs); pRs->Open( (_variant_t)sqlSentence, // 查詢DemoTable表中全部字段 pConn.GetInterfacePtr(), // 獲取庫接庫的IDispatch指針 adOpenDynamic, adLockOptimistic, adCmdText); } catch (_com_error &e) { sprintf(errMsg,_T("Code meaning = %s//n"), (char*) e.ErrorMessage()); AfxMessageBox(errMsg); pRs->Close(); pConn->Close(); ::CoUninitialize(); return; } try { while(!(pRs->EndOfFile)) { CString pass = (LPCTSTR)(_bstr_t)pRs->GetCollect("pass"); if(pass.IsEmpty()) return; SetDlgItemText(IDC_PASS,pass); pRs->MoveNext(); //break; } pRs->Close(); pConn->Close(); } catch (_com_error &e) { sprintf(errMsg,_T("Code meaning = %s//n"), (char*) e.ErrorMessage()); AfxMessageBox(errMsg); pRs->Close(); pConn->Close(); ::CoUninitialize(); return; } CoUninitialize();
|
||
-- 做者:admin -- 發佈時間:2006-8-11 16:18:00 -- VC編程實現文本語音轉換 內容簡介 文本語音(Text-to-Speech,如下簡稱TTS),它的做用就是把經過TTS引擎把文本轉化爲語音輸出。本文不是講述如何創建本身的TTS引擎,而是簡單介紹如何運用Microsoft Speech SDK 創建本身的文本語音轉換應用程序。 Microsoft Speech SDK簡介 Microsoft Speech SDK是微軟提供的軟件開發包,提供的Speech API (SAPI)主要包含兩大方面: 1. API for Text-to-Speech 2. API for Speech Recognition 其中API for Text-to-Speech,就是微軟TTS引擎的接口,經過它咱們能夠很容易地創建功能強大的文本語音程序,金山詞霸的單詞朗讀功能就用到了這寫API,而目前幾乎全部的文本朗讀工具都是用這個SDK開發的。至於API for Speech Recognition就是與TTS相對應的語音識別,語音技術是一種使人振奮的技術,但因爲目前語音識別技術準確度和識別速度不太理想,還未達到普遍應用的要求。 Microsoft Speech SDK能夠在微軟的網站免費下載,目前的版本是5.1,爲了支持中文,還要把附加的語言包(LangPack)一塊兒下載。 爲了在VC中使用這SDK,必需在工程中添加SDK的include和lib目錄,爲免每一個工程都添加目錄,最好的辦法是在VC的 Option->Directoris立加上SDK的include和lib目錄。 一個最簡單的例子 先看一個入門的例子: #include <sapi.h> #pragma comment(lib,"ole32.lib") //CoInitialize CoCreateInstance須要調用ole32.dll #pragma comment(lib,"sapi.lib") //sapi.lib在SDK的lib目錄,必需正確配置 int main(int argc, char* argv[]) { ISpVoice * pVoice = NULL; //COM初始化: if (FAILED(::CoInitialize(NULL))) return FALSE; //獲取ISpVoice接口: HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice); if( SUCCEEDED( hr ) ) { hr = pVoice->Speak(L"Hello world", 0, NULL); pVoice->Release(); pVoice = NULL; } //千萬不要忘記: ::CoUninitialize(); return TRUE; } 短短20幾行代碼就實現了文本語音轉換,夠神奇吧。SDK提供的SAPI是基於COM封裝的,不管你是否熟悉COM,只要循序漸進地用CoInitialize(), CoCreateInstance()獲取IspVoice接口就夠了,須要注意的是初始化COM後,程序結束前必定要用CoUninitialize()釋放資源。 IspVoice接口主要函數 上述程序的流程是獲取IspVoice接口,而後用ISpVoice::Speak()把文本輸出爲語音,可見,程序的核心就是IspVoice接口。除了Speak外IspVoice接口還有許多成員函數,具體用法請參考SDK的文檔。下面擇要說一下幾個主要函數的用法: HRESULT Speak(const WCHAR *pwcs,DWORD dwFlags,ULONG *pulStreamNumber); 功能:就是speak了 參數: *pwcs 輸入的文本字符串,必需爲Unicode,若是是ansi字符串必需先轉換爲Unicode。 dwFlags 用來標誌Speak的方式,其中SPF_IS_XML 表示輸入文本含有XML標籤,這個下文會講到。 PulStreamNumber 輸出,用來獲取去當前文本輸入的等候播放隊列的位置,只有在異步模式纔有用。 HRESULT Pause ( void ); HRESULT Resume ( void ); 功能:一看就知道了。 HRESULT SetRate(long RateAdjust ); HRESULT GetRate(long *pRateAdjust); 功能:設置/獲取播放速度,範圍:-10 to 10 HRESULT SetVolume(USHORT usVolume); HRESULT GetVolume(USHORT *pusVolume); 功能:設置/獲取播放音量,範圍:0 to 100 HRESULT SetSyncSpeakTimeout(ULONG msTimeout); HRESULT GetSyncSpeakTimeout(ULONG *pmsTimeout); 功能:設置/獲取同步超時時間。因爲在同步模式中,電泳Speak後程序就會進入阻塞狀態等待Speak返回,爲免程序長時間沒相應,應該設置超時時間,msTimeout單位爲毫秒。 HRESULT SetOutput(IUnknown *pUnkOutput,BOOL fAllowFormatChanges); 功能:設置輸出,下文會講到用SetOutput把Speak輸出問WAV文件。 這些函數的返回類型都是HRESULT,若是成功則返回S_OK,錯誤有各自不一樣的錯誤碼。 使用XML 我的認爲這個TTS api功能最強大之處在於可以分析XML標籤,經過XML標籤設置音量、音調、延長、停頓,幾乎能夠使輸出達到天然語音效果。前面已經提過,把Speak參數dwFlags設爲SPF_IS_XML,TTS引擎就會分析XML文本,輸入文本並不須要嚴格遵照W3C的標準,只要含有XML標籤就好了,下面舉個例子: …… pVoice->Speak(L"<VOICE REQUIRED=/'/'NAME=Microsoft Mary/'/'/>volume<VOLUME LEVEL=/'/'100/'/'>turn up</VOLUME>", SPF_IS_XML, NULL); …… <VOICE REQUIRED=/'/'NAME=Microsoft Mary/'/'/> 標籤把聲音設爲Microsoft Mary,英文版SDK中一共含有3種聲音,另外兩種是Microsoft Sam和Microsoft Mike。 …… <VOLUME LEVEL=/'/'100/'/'> 把音量設爲100,音量範圍是0~100。 另外:標誌音調(-10~10): <PITCH MIDDLE="10">text</PITCH> 注意:" 號在C/C++中前面要加 // ,不然會出錯。 標誌語速(-10~10): <RATE SPEED="-10">text</RATE> 逐個字母讀: <SPELL>text</SPELL> 強調: <EMPH>text</EMPH> 停頓200毫秒(最長爲65,536毫秒): <SILENCE MSEC="200" /> 控制發音: <PRON SYM = /'/'h eh - l ow 1/'/'/> 這個標籤的功能比較強,重點講一下:全部的語言發音都是由基本的音素組成,拿中文發音來講,拼音是組成發音的最基本的元素,只要知道漢字的拼音,即便不知道怎麼寫,咱們可知道這個字怎麼都,對於TTS引擎來講,它不必定認識全部字,可是你把拼音對應的符號(SYM)給它,它就必定可以讀出來,而英語發音則能夠用音標表示,/'/'h eh - l ow 1/'/'就是hello這個單詞對應的語素。至於發音與符號SYM具體對應關係請看SDK文檔中的Phoneme Table。 再另外,數字、日期、時間的讀法也有一套規則,SDK中有詳細的說明,這裏不說了(懶得翻譯了),下面隨便拋個例子: <context ID = "date_ ymd">1999.12.21</context> 會讀成 "December twenty first nineteen ninety nine" XML標籤能夠嵌套使用,可是必定要遵照XML標準。XML標籤確實好用,效果也不錯,可是……缺點:一個字―――"煩",若是給一大段文字加標籤,簡直痛不欲生。 把文本語音輸出爲WAV文件 #include <sapi.h> #include <sphelper.h> #pragma comment(lib,"ole32.lib") #pragma comment(lib,"sapi.lib") int main(int argc, char* argv[]) { ISpVoice * pVoice = NULL; if (FAILED(::CoInitialize(NULL))) return FALSE; HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice); if( SUCCEEDED( hr ) ) { CComPtr<ISpStream> cpWavStream; CComPtr<ISpStreamFormat> cpOldStream; CSpStreamFormat OriginalFmt; pVoice->GetOutputStream( &cpOldStream ); OriginalFmt.AssignFormat(cpOldStream); hr = SPBindToFile( L"D:////output.wav",SPFM_CREATE_ALWAYS, &cpWavStream,&OriginalFmt.FormatId(), OriginalFmt.WaveFormatExPtr() ); if( SUCCEEDED( hr ) ) { pVoice->SetOutput(cpWavStream,TRUE); WCHAR WTX[] = L"<VOICE REQUIRED=/'/'NAME=Microsoft Mary/'/'/>text to wave"; pVoice->Speak(WTX, SPF_IS_XML, NULL); pVoice->Release(); pVoice = NULL; } } ::CoUninitialize(); return TRUE; } SPBindToFile把文件綁定到輸出流上,而SetOutput把輸出設爲綁定文件的流上。 最後 看完本文後,是否是以爲很簡單,微軟把強大的功能封裝的太好了。其實SDK中另一個API,SR(語音識別)更有趣,有興趣不妨試試,你會有意外的收穫的
|
||
-- 做者:admin -- 發佈時間:2006-8-11 16:20:00 -- VC++5.0下MIDI、WAV及CD的播放加入音樂是加強應用程序功能的全部方法中最簡單的一個。幾乎每一個計算機遊戲或多 媒體程序都以某種MIDI或CD音樂爲背景。音樂能夠使用戶心情愉快;在合適的場合播 放恰當的音樂可以使程序員和他的VC++程序煥發光彩。 第一部分 MIDI的播放 ---- 樂器數字化接口(MIDI)是由音樂界的一些大公司(包括生產電子音樂合成器的公司) 制訂的一項協議,後來被計算機產業所採用併成爲多媒體音樂文件的標準格式。MIDI文件 通常較小,對硬件設備的要求高。 ---- 1、 原理 ---- 雖然MicroSoft支持MIDI文件,然而Visual C++或MFC並無建立任何組件來實現 這種支持,可是MicroSoft API提供了三種不一樣的方法來實現MIDI的播放: MCI(The Media Control Interface)。這是最基本的方法,本文將詳細討論這種方法。 流緩衝器。這種格式容許應用程序爲MIDI數據分配緩衝器。在須要精確控制MIDI播放的時候,流緩衝器將頗有用處。 低級MIDI設備。須要徹底控制MIDI數據的應用程序能夠使用這種方法。 ---- MCI能夠經過mciSendCommand()和mciSendString()來完成,本文僅使用mciSendCommand()函數。 ---- 原型: DWORD mciSendCommand(UINT wDeviceID,UINT wMessage,DWORD dwParam1,DWORD dwParam2); 參數: wDeviceID:接受消息的設備 ID wMessage:MCI命令消息 dwParam1:命令的標誌位 dwParam2:所使用參數塊的指針 ---- 返值:調用成功,返回零; 不然,返回雙字中的低字存放有錯誤信息。 二MIDI的播放控制 ---- 1. 打開設備 MCI_OPEN_PARMS OpenParms; OpenParms.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_SEQUENCER; //MIDI類型 OpenParms.lpstrElementName = (LPCSTR) Filename; OpenParms.wDeviceID = 0; mciSendCommand (NULL, MCI_OPEN, MCI_WAIT | MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID | MCI_OPEN_ELEMENT, (DWORD)(LPVOID) &OpenParms) ---- MCI設備ID指明打開了哪一個設備,當發送了MCI_OPEN命令時,這個值在參數塊中返回——應被保存備用。 ---- 2. 關閉設備 mciSendCommand (m_wDeviceID, MCI_CLOSE, NULL, NULL); ---- 3. 播放 MCI_PLAY_PARMS PlayParms; PlayParms.dwFrom = 0; // 指定從什麼地方(時間)播放 mciSendCommand (m_wDeviceID, MCI_PLAY, MCI_FROM, (DWORD)(LPVOID) &PlayParms)); ---- 4. 暫停 MCI_PLAY_PARMS PlayParms; mciSendCommand (m_wDeviceID, MCI_PAUSE, 0, (DWORD)(LPVOID) &PlayParms); ---- 5. 中止 mciSendCommand (m_wDeviceID, MCI_STOP, NULL, NULL); ---- 6. 跳躍 * 跳轉到任意地方 MCI_SEEK_PARMS SeekParms; SeekParms.dwTo = (nMinute * 60 + nSecond) * 1000; //跳轉的目標時間,時間單位爲毫秒 mciSendCommand (m_wDeviceID, MCI_SEEK, MCI_TO | MCI_WAIT,(DWORD)(LPVOID) &SeekParms); 跳到文件頭 mciSendCommand (m_wDeviceID, MCI_SEEK, MCI_SEEK_TO_START, NULL); l 跳到文件尾 mciSendCommand (m_wDeviceID, MCI_SEEK, MCI_SEEK_TO_END, NULL); ---- 7. 查詢當前信息
MCI_STATUS_PARMS StatusParms; StatusParms.dwItem = MCI_SEQ_STATUS_DIVTYPE; mciSendCommand (m_wDeviceID, MCI_STATUS, MCI_WAIT | MCI_STATUS_ITEM, (DWORD)(LPVOID) &StatusParms); 返回信息存放於StatusParms.dwReturn中。 MCI_STATUS標誌 MCI_STATUS_LENGTH 得到文件長度 MCI_STATUS_MODE 得到文件播放的當前狀態 MCI_STATUS_POSITION 得到文件播放的當前位置 MCI_STATUS_TIME_FORMAT 得到當前的時間格式 MCI_SEQ_STATUS_DIVTYPE 判斷文件是PPQN類型仍是SMPTE類型 MCI_SEQ_STATUS_TEMPO 得到當前播放速度,PQRN類型, 此值爲節拍/分,SMPTE類型,此值爲禎/秒 ---- 8. 設置時間格式及播放速度 MCI_SET_PARMS SetParms; SetParms.dwTimeFormat = MCI_FORMAT_MILLISECONDS; |