Symbian OS中,在可顯示表單上畫圖是通過圖形裝置(graphics device)和圖形上下文(graphics context)來實現。
一、圖形裝置
圖形裝置是對呈現圖形的裝置(螢幕、印表機等)的抽象。它提供了一組獨立於裝置的介面,應用程式使用這些介面,通過圖形上下文(graphics context)來畫圖。圖形裝置是畫圖的中介,圖形上下文在畫圖時畫在這些圖形裝置上。在Symbian中圖形裝置通過圖形裝置類來體系,其繼承結構如下:
CgraphicsDevice 圖形裝置的基類
CbitmapDevice 位元影像化圖形裝置的基類
CFbsDevice 使用字型位元影像伺服器的圖形裝置基類
CPrinterDevice 具有列印功能的裝置基類
CWsScreenDevice 使用視窗伺服器的螢幕裝置,View中預設採用的圖形裝置
CFbsBitmapDevice 針對記憶體的圖形裝置,可用於雙緩衝
CFbsScreenDevice 使用直接螢幕訪問(DSA),而不通過視窗伺服器,比較快
二、圖形上下文
圖形上下文為圖形裝置提供了上下文,也就是畫圖環境。包括畫筆、畫刷的顏色格式設定以及字型字形等設定,這些設定都是裝置無關的。
圖形上下文還定義了畫圖地區,通常情況下不能將圖形畫在定義的地區外;它同時還包括了很多主要的畫圖操作。大部分操作都在抽象類別CGraphicsContext中定義。這些畫圖操作包括繪畫和填充各種圖形如點、直線、曲線、弧形、矩形、多邊形、圓形及橢圓形、字型及位元影像等。
其繼承結構如下:
CwindowGc:視窗圖形上下文,View中預設提供
CfbsBitGc:位元影像圖形上下文,可繪畫和填充各種圖形,及其對位元影像的操作。
CanimGc:與動畫相關的圖形上下文。
三、位元影像
Symbian中通過CFbsBitmap來操作位元影像,包括建立和載入等,同時提供按位操作位中資訊的功能。為了提高位元影像的描畫速度,視窗伺服器提供自己的位元影像類CWsBitmap。它通過取得位元影像處理的所有權來除去視窗伺服器和FBS之間多餘的上下文轉換。CWsBitmap繼承自CFbsBitmap類,
四、描畫方法
1、View預設描畫
在view類中,我們可以通過調用SystemGc擷取應用程式架構為我們建立好的視窗圖形上下文,並通過該類完成圖形的描畫以及位元影像的顯示。其採用的圖形裝置類為CWsScreenDevice,圖形裝置上下文為CWindowGc。
/* 建立位元影像 */
iwsBitmap = new (ELeave)CFbsBitmap();
CleanupStack::PushL(iwsBitmap);
iwsBitmap->Create(TSize(aRect.Width(), aRect.Height()),
iCoeEnv->ScreenDevice()->DisplayMode());
CleanupStack::Pop(iwsBitmap);
/* 填充位元影像 */
iwsBitmap->LockHeap(); // 必須鎖住位元影像記憶體,否則產生記憶體異常
TUint32 * bitMapAddr = iwsBitmap->DataAddress();
TSize bitMapSize = iwsBitmap->SizeInPixels();
Mem::Fill(bitMapAddr,
bitMapSize.iHeight * iwsBitmap->ScanLineLength(bitMapSize.iWidth, iwsBitmap->DisplayMode()),
170);
iwsBitmap->UnlockHeap();
/* 描畫位元影像 */
CWindowGc& gc = SystemGc(); //擷取圖形上下文
TRect drawRect(Rect());
gc.BitBlt(TPoint(0, 0), iwsBitmap);
2、雙緩衝描畫
view預設描畫環境(CWsScreenDevice與CWindowGc)資料在用戶端視窗伺服器緩衝區上進行緩衝,當描畫多個運動的對象時,視窗伺服器的用戶端緩衝可能被充滿並且有可能在所有對象都更新的時候溢出,從而倒是螢幕閃爍等現象,可通過雙緩衝來解決該問題。
所謂雙緩衝是指先在記憶體中建立位元影像空間,並在該空間中完成描畫,之後整體切換到螢幕上顯示。屏外位元影像可以使用位元影像化的圖形上下文和記憶體配置圖形裝置類來建立:CFbsBitGc和CFbsBitmapDevice。
iBufBmp = new(ELeave)CWsBitmap(CCoeEnv::Static()->WsSession());
CleanupStack::PushL(iBufBmp);
User::LeaveIfError(iBufBmp->Create(Rect().Size(),
CeikonEnv::Static()->ScreenDevice()->DisplayMode()));
iBufDevice = CfbsBitmapDevice::NewL(iBufBmp);
CleanupStack::PushL(iBufDevice);
User::LeaveIfError(iBufDevice->CreateBitmapContext(iBufGc)); CleanupStack::Pop(2);
3、直接螢幕繪圖
直接螢幕繪圖是指採用 CfbsScreenDevice圖形裝置進行繪圖,不經過視窗伺服器,節省了裝置驅動與視窗伺服器間的環境切換,提高了速度。建立直接螢幕圖形裝置及其相關的位元影像圖形內容相關的代碼如下:
CFbsScreenDevice* iFbsScreenDevice; /* 直接螢幕裝置 */
CFbsBitGc* iFbsBitGc; /* 位元影像圖形上下文 */
iFbsScreenDevice = CfbsScreenDevice::NewL(0,
CCoeEnv::Static()->ScreenDevice()->DisplayMode());
iFbsScreenDevice->CreateContext((CGraphicsContext*&) iFbsBitGc);
iFbsBitGc->SetUserDisplayMode(
CcoeEnv::Static()->ScreenDevice()->DisplayMode());
4、直接存取螢幕記憶體
我們可以擷取螢幕相關的記憶體位址(尚不確定是否為顯存),並通過操作該地址完成圖形描畫,(照此看來,Symbian並不支援在顯存中申請地址)。擷取螢幕相關記憶體位址代碼如下:
TPckgBuf<TScreenInfoV01> infoPckg;
TScreenInfoV01& screenInfo = infoPckg();
UserSvr::ScreenInfo(infoPckg);
TUint16* screenMemory = (Tuint16*)(
(int)screenInfo.iScreenAddress + 16);
5、DSA繪圖
由於螢幕直接繪圖和直接存取螢幕相關記憶體的方法繞過了視窗伺服器,因此它不能通知應用程式是否出現另一個視窗或者視窗組。即使當應用程式失去焦點的時候得到一個事件,它們也不能停止直接描畫,從而導致螢幕內容有可能被弄亂。尤其在突然有電話打進來的情況下。
為瞭解決該問題,Symbian退出了CDirectScreenAccess訪問方式,該方式也採用CfbsScreenDevice直接存取螢幕,但是需要view類繼承MDirectScreenAccess,重寫Restart和AbortNow虛方法,當產生需要切換畫面的事件時視窗伺服器回調相應虛方法,解決了上述兩種直接繪圖方法中存在的問題。建立代碼如下:
CCoeEnv* env = CCoeEnv::Static();
iDirectScreenAccess = CdirectScreenAccess::NewL(
env->WsSession(), *(env->ScreenDevice()),
this->Window(), *this);
iDirectScreenAccess->StartL();