天天看點

Visual C++多媒體設計及圖形、圖像處理

在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控件的支援。

在螢幕上任何地方畫圖

可以用你的程式在螢幕上的任何地方畫圖,效果就象電子寵物一樣很簡單,隻要兩個API

The first one is getdesktopwindow and the other is getwindowdc

the detail you can see the Win32 help which was plused in Delphi4

For example: you paste a bitmap on the screen

var wnd:longint;

mydc,bitmapdc:Hdc;

mybitmap:Tbitmap;

mybitmap:=Tbitmap.create();

mybitmap.loadfromfile(c:/windows/waves.bmp);

bitmapdc:=getwindowdc(mybitmap.handle);

wnd:=getdesktopwindow();

mydc:=getwindowdc(wnd);

bitblt(my....... //I forget the detail if you have any questions can

// send email to me at bbs

引入湍流概念實作對大理石紋理的造型模拟

 摘要:本文通過在物體表面紋理的造型算法中引入流體力學中的湍流概念而成功實作了對大理石表面紋理的模拟仿真。

  前言

  計算機仿真模拟技術在生物、醫學以及國防等諸多科研和應用領域得到了廣泛的應用。仿真的過程也就是把仿真對象從物體提煉成數學模型的過程,是以其内在數學模型的提煉和從數學模型設計出相應的仿真算法在整個仿真過程中是很關鍵的兩個環節。本文以大理石為例,通過引入流體力學中湍流概念從數學角度提出了一個仿真效果比較好的紋理仿真算法,并給出了關鍵部分的VC++編碼。

  過程紋理造型技術

  過程紋理造型技術在功能同二維紋理管理技術比較相似。但由于二維紋理管理技術采用固定的圖像來描述表面細節,而客觀世界中的自然物體表面紋理卻往往具有較強的不規則性和随機性,是以這種采用固定圖像對表面細節進行描述的紋理造型技術效果并不好,不能對自然物體進行較好的造型和模糊。在20世紀80年代中後期提出的過程紋理造型技術采用代碼段或算法來編碼抽取模型細節,并且允許高層控制和規範,從根本上克服了二維紋理管理技術的上述缺陷。而且該技術是通過控制參數來管理紋理資料的放大,是以在仿真過程中隻需簡單地設定若幹個控制參數就可以動态産生大量豐富的幾何細節。

   1985年由Peachey和Perlin所提出的三維紋理映射方法将三維紋理函數直接定義在三維紋理空間中,數學形式為:

  T=T(x,y,z)(T:顔色的RGB值; (x,y,z):空間點三維坐标)。現在用來構造三維紋理的基本方法常用的主要有兩種:

  基于高頻采樣的數字化紋理和采用數學模型動态計算生成紋理。基于高頻采樣的數字化紋理由于需要有三維數組的支援,在處理高分辨率的紋理時占用空間将急劇增大,對空間的要求比較苛刻。一些真實感較強的圖形合成系統多采用數學模型動态計算生成紋理的紋理定義方法。經過多年的研究實踐已經開發積累了許多過程疊代函數以産生各種複雜的紋理,這些過程紋理(procedural texture)函數被證明是非常有效的,已經可以對木材、大理石、雲彩、火焰和石闆等許多自然物體的紋理進行逼真的模拟仿真。這些函數從本質上來說都是一種經驗模型。

  在算法中引入湍流概念

  湍流(turbulence)本是流體力學中的重要研究對象,但在此不準備從湍流的精确實體模型出發對其做任何讨論,而是提出一種近似描述該實體現象的經驗模型。這種技術最早在1985年由Perlin提出,曾成功用于對大理石、火焰以及雲彩等紋理的仿真。這種經驗模型是由一系列的三維噪聲函數疊加而成,根據流體力學中湍流的有關概念可以得出可以展現上述經驗模型的數學表達式:

  turbulence(p)=∑|Noise(pow(2,i)*p)/(pow(2,i))|

  其中,p為一紋理空間中的點(x,y,z)。求和區間為i從0到k,上限k是滿足下列不等式的最小整數:

1/(pow(2,k+1))<像素通長。通過選取滿足上述條件的求和上限值 k,可以避免采樣湍流函數時的走樣現象。該函數表達式任取和式的相鄰兩項,後一項噪聲函數的變化頻率總是前一項函數的變化頻率的兩倍,幅度則為前者的一半,也就是對湍流函數的貢獻率減小了一半。是以,上述的構造方法從數學上保證了湍流函數本身就具有一定的自相似特性。從信号分析的角度看,則其功率譜分布滿足1/f的規律。該函數并不能直接對紋理進行仿真,我們所借鑒的隻是其随機性和自相關特性,這些特性在紋理定義時可以很好的描述各種自然紋理的不規則細節。是以湍流函數在紋理造型算法中還是能起到重要作用的。紋理的描述過程大緻分兩步:首先選取一個簡單的合适的函數來描述自然物體如大理石的基本紋理結構特征,其中選取的這個描述函數一般應當是連續的且其一階導數應具有較大的變化。在標明描述函數後用湍流函數來對描述函數的某些基本參數進行擾動,以産生複雜的不規則紋理細節。

  對于本文的研究對象--大理石,可以注意到大理石漂亮的紋理其實是其内部不同組成材質的顔色的反映,是以可以定義一個函數來描述這種構造。大理石差別于普通石材的一個比較明顯的特點是其紋理多呈周期分布,對此特性可用某方向的正弦波函數作為彩色濾波:

  marble(p) = marble_color(sin(x))

  p仍為空間點(x,y,z),marble_color()為彩色濾波函數,把[-1,1]區間内任一函數值映射成為RGB顔色之間的顔色值,通常将其表達為三條各自獨立的樣條曲線。在采取了紋理描述的第二步引入了湍流擾動後,上述函數變為最終的描述函數:

  marble(p) = marble_color(sin(x+turbulence(p)))

  算法的實作

  根據前面總結的結論,筆者經過實驗發現,如果定義的樣條曲線在某些點存在較大的一階導數,那麼用該樣條曲線模拟出來的大理石表面紋理将存在較明顯的尖銳邊界。在下面采取的描述算法中樣條曲線為線性函數(一次樣條),效果還是适中的:

COLORREF marble(float px,float py,float pz)

{

float y=py+3.0*turbulence(px,py,pz,0.0125);

y=sin(y*M-PI);

return (marble_color(px));

}

……

COLORREF marble_color(float px);

{

COLORREF col;

float x=sqrt(px+1.0)*0.7071;

int G=(int)(0.30+0.8*x);

x=sqrt(x);

int R=(int)(0.30+0.6*x);

int B=(int)(0.60+0.4*x);

return col;

}

以上兩段代碼可以較好地對大理石紋理做仿真模拟處理。同其他紋理一樣,湍流函數也可以用來表達各種表面顔色屬性的不規則性如凹凸紋理、透明度等。

  小結

  本文通過在過程紋理造型技術引入流體力學的湍流概念,成功對大理石的表面紋理作了仿真模拟。通過本文對大理石紋理的計算機仿真的實作過程,可對此類仿真模拟程式有一個基本的認識。湍流函數算法也适用于需要對參數進行擾動處理的其他一些仿真環境。本文程式算法在Windows 98 SE下,由Microsoft Visual C++ 6.0編譯調試通過。

用VC實作桌面文字背景透明擺脫黑白

Module name : Transparent.cpp

*

* Module description :

* To make desktop icon text background transparent.

*

* Project :

*

* Target platform : Win32

*

* Compiler & Library : Visual C++ 6.0

*

* Author : Richard Shen

*

* Creation date : 19 June, 1999

*

#include <windows.h>

int main(void)

{

HWND hWnd;

hWnd = GetDesktopWindow();

if ((hWnd = FindWindowEx(hWnd, 0, "Progman", "Program Manager")) == 0)

return 1;

if ((hWnd = FindWindowEx(hWnd, 0, "SHELLDLL_DefView", NULL)) == 0)

return 1;

if ((hWnd = FindWindowEx(hWnd, 0, "SysListView32", NULL)) == 0)

return 1;

// Change icon text attributes

SendMessage(hWnd, 0x1026, 0, 0xffffffff); // Turn background to transparent

SendMessage(hWnd, 0x1024, 0, 0x00ffffff); // Turn foregound to white

InvalidateRect(hWnd, NULL, TRUE); // Repaint

return 0;

} // main()

用漸變色填充背景

Windows的圖形界面為我們提供了無窮的友善和視覺上的快感,由淺及深的顔色給我們無盡的遐想。漸變色的實作有多種方法,好多資料上介紹了利用調色闆的方法實作,其過程及其的複雜,需要我們具有一定的圖形程式設計的基礎,下面我将向大家介紹一種比較簡單的方法,即使你一點都不了解圖形程式設計和調色闆的概念。

  第一步:建立單文檔工程,一切參數都取預設值。

  第二步:在shadowview.h中定義變量如下:

private:

int ColorR;

int ColorG;

  第三步:在shadowview.cpp的構造函數中初始化變量如下:

CShadowView::CShadowView()

{

 // TODO: add construction code here

 ColorR = 255;

 ColorG = 255;

}

  第四步:在Ondraw()中添加如下的實作代碼:

void CShadowView::OnDraw(CDC* pDC)

{

 CShadowDoc* pDoc = GetDocument();

 ASSERT_VALID(pDoc);

 // TODO: add draw code for native data here

 CRect m_rcClient;

 file://得到客戶區域的填充矩形

 GetClientRect(&m_rcClient);

 int nWidth = m_rcClient.Width();

 int nHeight = m_rcClient.Height();

 CRect rectangle;

 file://分割客戶區域成小矩形,逐個填充

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

 {

  rectangle.SetRect(i, 0, i+1, nHeight);

  pDC->FillSolidRect(&rectangle, RGB(ColorR, ColorG, 255-MulDiv(i, 255, nWidth)));

  }

}

  現在編譯、運作程式,我們可以發現單文檔界面的背景已經被黃漸變色填充。接下來,我們實作在界面上點選滑鼠左鍵,實作背景顔色的改變。

  第五步:在ClassWizard中添加滑鼠左鍵的消息處理映射函數,并添加如下代碼:

void CShadowView::OnLButtonDown(UINT nFlags, CPoint point)

{

 file://生成小于255的随機數,給ColorR和ColorG指派

 int nRand = rand();

 float fMap = (float)255/RAND_MAX;

 ColorR = (UINT)(float)nRand*fMap + 0.5f;

 nRand = rand();

 fMap = (float)255/RAND_MAX;

 ColorG = (UINT)(float)nRand*fMap + 0.5f;

 file://更新界面

 Invalidate();

 CView::OnLButtonDown(nFlags, point);

}

  好了,所有的功能都實作了,在界面上點選滑鼠左鍵,我們可以發現,背景以不同的漸變色填充。

繼續閱讀