技术开发 频道

Symbian屏幕双缓冲和直接屏幕访问

  

       【IT168技术】双缓冲是先将显示图形放在缓冲区,再一起显示到屏幕上,以防止屏幕上的东西一个个出现或是发生闪烁。 双缓冲是基于“空间换时间”和“功能分块”的思想。

       ⑴图形设置和图形上下文

  应用程序绘制图形时,必须要使用图形设备和图形上下文。

  图形设备是绘制操作的对象(如屏幕、打印机等),图形设备上下文提供了一种设备的抽象机制从而完全屏蔽了具体的图形设备,应用程序在使用这些图形设备时只需要和这些抽象的图形设备类交互即可,而不必考虑其具体的设备。

  图形上下文是绘画平台及绘画所需要工具的集合体,它还包括平台的尺寸、方向、颜色和所有能实现绘画想象力的附件。

  下面为Symbian中使用的图形设备及其功能,它们是实现绘画的基础。

  图形设备描述

  CGraphicsDevice图形设备的基类

  CBitmapDevice位图化图形设备的基类

  CFbsDevice使用字体位图服务器的图形设备基类

  CPrinterDevice具有打印功能的设备基类

  CWsScreenDevice使用窗口服务器的屏幕设备

  CFbsBitmapDevice使用字体位图服务器的设备具体实现

  CFbsScreenDevice使用直接屏幕访问(DSA),而不通过窗口服务器

  这些设备类的继承关系如下:

  CWindowsGc用于屏幕绘制,CFbsBitGc则用于内存绘制!

  ⑵双缓冲

  应用程序在屏幕上的描画一般是使用CWsScreenDevice图形设备来完成,与CWindowGc图形上下文相关联。CONE提供了一个CWindowGc实例作为描画控件的标准图形上下文。它被CCoeEnv创建并且可以使用CCoeControls::SystemGc()方法访问。 CWindowGc的描画方法在客户端窗口服务器缓冲区上进行缓冲。

1 void CExampleControl::Draw( const TRect& /*aRect*/ ) const
2
3
4
5   {
6
7
8
9   // Get the system graphics context
10
11
12
13   CWindowGc& gc = SystemGc();
14
15
16
17   // Set drawing settings
18
19
20
21   gc.SetBrushStyle( CGraphicsContext::ESolidBrush );
22
23
24
25   gc.SetBrushColor( KRgbRed );
26
27
28
29   // Draw
30
31
32
33   gc.DrawLine( TPoint(10,10), TPoint(30,10) );
34
35
36
37   }
38

DrawNow()强制控件立即重画自己,而DrawDeferred()导致一个重画事件使用低优先级操作!

  如果一个游戏的图形由多个需要被经常更新的运动对象组成,窗口服务器的客户端缓冲可能被充满并且可能会在所有对象都更新的时候溢出,用户可能会发现屏幕出现闪烁。如果一个视图仍然在更新的时候,可能会出现闪烁或者其他不希望的效果。这些问题的解决方案是双缓冲,图形先被画在一个屏外位图上,然后被画到屏幕上作为一个单一窗口服务器操作。尤其是对于那种在一秒钟内重画几次屏幕的游戏,使用屏外位图可以改善它们的性能

  一个屏外位图可以使用位图化的图形上下文和图形设备类来创建:CFbsBitGc和CFbsBitmapDevice。它们使用其他的上下文和设备类来创建和使用。为了获得额外的性能,位图自己就应该是一个CWsBitmap位图。在屏外位图更新之后,它可以使用正常的窗口服务器的描画方法画在窗口中。

  当一个应用程序在一个窗口画位图时,它转化为和窗口相同的显示模式。这是一个很消耗时间的操作,实质上可能降低描画的速度。因此把位图用于动画的游戏应该在动画开始之前就完成转化。转化可以通过使用一个屏外位图来执行,如下面的示例方法演示:

1 CFbsBitmap* CExampleControl::LoadAndConvertBitmapL( Const TDesC& aFileName, TInt aBitmapId )
2
3
4
5   {
6
7
8
9   // Load the bitmap
10
11
12
13   CFbsBitmap* originalBitmap = new ( ELeave ) CFbsBitmap();
14
15
16
17   CleanupStack::PushL( originalBitmap );
18
19
20
21   User::LeaveIfError( originalBitmap->Load( aFileName, aBitmapId, EFalse ) );
22
23
24
25   // Create a new bitmap, graphics device and context
26
27
28
29   CFbsBitmap* newBitmap = new ( ELeave ) CFbsBitmap();
30
31
32
33   CleanupStack::PushL( newBitmap );
34
35
36
37   newBitmap->Create( originalBitmap->SizeInPixels(), Window()->DisplayMode() );
38
39
40
41   CFbsBitmapDevice* graphicsDevice = CFbsBitmapDevice::NewL(newBitmap);
42
43
44
45   CleanupStack::PushL( graphicsDevice );
46
47
48
49   CFbsBitGc* graphicsContext;
50
51
52
53   User::LeaveIfError( graphicsDevice->CreateContext( graphicsContext ) );
54
55
56
57   TPoint zero(0,0);
58
59
60
61   // Blit the loaded bitmap to the new bitmap
62
63
64
65   graphicsContext->BitBlt( zero, originalBitmap );
66
67
68
69   CleanupStack::Pop(3);
70
71
72
73   delete graphicsContext;
74
75
76
77   delete graphicsDevice ;
78
79
80
81   delete originalBitmap;
82
83
84
85   return newBitmap;
86
87
88
89   }
90

示例方法使用一个文件名和位图ID作为参数,并且从一个MBM文件中装载相应的位图。如果一个游戏有许多位图应该转化,那么应该在游戏或者等级的初始化阶段转化,因此用户就不会看到这个操作了。

  ⑵直接屏幕访问(DSA)

  使用窗口服务器在屏幕上描画需要一个上下文转换,这会减慢描画速度。为了绕过窗口服务器省去繁琐的上下文转换,可以直接访问屏幕。这被称作直接屏幕访问。

  在Symbian OS中有两种方法来直接在屏幕上描画:

  ①CFbsScreenDevice是一个可以被发送到屏幕驱动程序SCDV.DLL的图形设备。在创建一个CFbsBitGc图形上下文之后,它能像任何其他的图形设备一样使用。然而,可以直接在屏幕上描画,而不需要使用窗口服务器。

  ②直接在屏幕上描画的另一种方法是从系统中查询屏幕内存地址,这可以使用UserSrv类来实现:

  TPckgBuf infoPckg;

  TScreenInfoV01& screenInfo = infoPckg();

  UserSvr::ScreenInfo(infoPckg);

  TUint16* screenMemory = screenInfo.iScreenAddress + 16;

  屏幕内存有一个32字节的头。

  即使在屏幕内存内写数据比CFbsScreenDevice稍微快一点,但是功能可能根据硬件和屏幕的设备驱动程序的不同而有差异。在一些基于Symbian OS的终端中,屏幕在内存变化的时候自动从屏幕内存中更新,而在其他的终端中描画需要明确的激活。屏幕内存地址只对目标硬件有效,因此描画代码需要分为硬件和模拟器两部分。在模拟器环境中,可以描画到一个屏外位图中,而不是屏幕内存中,然后使用正常的窗口服务器描画方法位块传送到屏幕上。环境可以通过使用__WINS__定义来检测出来。

  #ifdef __WINS__ // Emulator environment

  // Draw to an off-screen bitmap

  #else // Hardware environment

  // Draw directly to the screen memory

  #endif

  这两种直接描画方法的一个共同的问题是窗口服务器不了解描画,因此它不能通知应用程序是否出现另一个窗口或者窗口组。 即使当应用程序失去焦点的时候得到一个事件,它们也不能停止直接描画,因为直接描画实在太快了,并且屏幕内容有可能被弄乱。 这可能发生在玩游戏的时候,突然有电话打进来的情况下。

  新近的GT 6.1版本提供了一个应用编程接口用于直接描画,将能解决前面提到的问题。

  这个应用编程接口由两个类组成:一个MDirectScreenAccess类,提供用于应用程序的回调方法,还有一个CDirectScreenAccess类处理与窗口服务器的通讯。 下面的代码说明CDirectScreenAccess实例是如何构造的,以及直接描画支持是如何激活的。

  iDrawer = CDirectScreenAccess::NewL(iEikonEnv->WsSession(), *iEikonEnv->ScreenDevice(), Window(), *this);

  iEikonEnv->WsSession().Flush();

  iDrawer->StartL();

  iDrawer->ScreenDevice()->SetAutoUpdate(ETrue);

  CDirectScreenAccess的NewL方法获得一个窗口服务器会话、CONE的图形设备、应用程序窗口和一个到MDirectedScreenAccess导出类的指针作为参数。 在CDirectScreenAccess::StartL被调用来激活直接描画支持之前,客户端窗口服务器缓冲应该溢出。 为了能自动更新屏幕,屏幕设备的SetAutoUpdate方法需要使用ETrue参数,否则gc draw指令不会在模拟器中立即显示,而是在下一次冲刷(Flush)视窗服务器时才显示。 当直接描画支持激活的时候,CDirectScreenAccess产生一个CFbsBitGc图形上下文,可以被应用程序用来在屏幕上绘画。

  iDrawer->Gc()->BitBlt( TPoint(0,0), iBitmap );

  当另一个窗口出现在应用程序窗口上时,CDirectScreenAccess从窗口服务器取得一个事件来中断描画。 CDirectScreenAccess然后调用MDirectScreenAccess派生类的AbortNow方法,这个方法必须被应用程序重载以便中断描画。 为了防止屏幕被弄乱,窗口服务器直到中断描画事件被处理的时候才画重叠窗口。

0
相关文章