DSA(直接写屏)和双缓冲
来源:互联网 发布:数据库中的sequence 编辑:程序博客网 时间:2024/06/05 00:57
DSA(直接写屏)和双缓冲
http://blog.chinaunix.net/u3/103999/showart_2063360.html
直接写屏就是得到屏幕的显存地址,从而直接对显存进行操作,使用直接写屏是为了加快显示速度。
直接写屏一般会在游戏和视频中被用到。
1.2实现方式
在Symbian OS中有三种方法来直接在屏幕上描画。
1> CFbsScreenDevice
CFbsScreenDevice是一个可以被发送到屏幕驱动程序SCDV.DLL的图形设备。在创建一个CFbsBitGc图形上下文之后,它能像任何其他的图形设备一样使用。然而,可以直接在屏幕上描画,而不需要使用窗口服务器。
以下是class CFbsScreenDevice的描述:
示例代码:
#include <FBS.H> // fbscli.lib
#include <BITSTD.H> // bitgdi.lib
// User::LeaveIfError(RFbsSession::Connect());
CWsScreenDevice* wsScreenDev = CEikonEnv::Static()->ScreenDevice();
CFbsScreenDevice* screenDev = CFbsScreenDevice::NewL(KNullDesC,
wsScreenDev->DisplayMode());
CFbsBitGc * gc = NULL;
User::LeaveIfError(screenDev->CreateContext( gc ));
wsScreenDev->SetAutoUpdate(ETrue); // Sets or unsets auto-update for the screen.
gc->DrawRect(TRect(TPoint (10, 10), TSize (60, 60)) ); // draw a rect by DSA
/* if not call SetAutoUpdate(ETrue) before, it must call Update() manually to update screen. */
// screenDev->Update();
delete gc; gc = NULL;
delete screenDev; screenDev = NULL;
// RFbsSession::Disconnect();
直接在屏幕上描画的另一种方法是从系统中查询屏幕内存地址,这可以使用 UserSrv::ScreenInfo 方法来实现。
屏幕内存有一个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
示例代码:
#include <e32event.h>
#include <e32svr.h>
TUint16 * screenDataAddr = NULL;
#if defined(__WINS__)
CWsBitmap * screenBufBmp = new (ELeave) CWsBitmap(
CCoeEnv::Static()->WsSession() );
CleanupStack::PushL(screenBufBmp);
TSize size = CCoeEnv::Static()->ScreenDevice()->SizeInPixels();
User::LeaveIfError(screenBufBmp->Create(size,
CCoeEnv::Static()->ScreenDevice()->DisplayMode() ));
screenBufBmp->LockHeap();
screenDataAddr = (TUint16*)screenBufBmp->DataAddress();
#else // device
TPckgBuf<TScreenInfoV01> screenInfoPckg;
UserSvr::ScreenInfo(screenInfoPckg);
TScreenInfoV01& screenInfo = screenInfoPckg();
if(screenInfo.iScreenAddressValid)
{
screenDataAddr = (TUint16*)screenInfo.iScreenAddress;
/* skip the palette data in the beginning of frame buffer.
* series 60 devices have 32 Bytes palette at the beginning.
* But be careful: UIQ devices haven't.
*/
screenDataAddr += 16;
}
#endif // __WINS__
/* DSA.
* when you copy bitmap data directly to video memory, it is essential to ensure
* your screen mode is same as bitmap mode!
*/
if(screenDataAddr)
{
Mem::Copy((void *)screenDataAddr, (void *)iOffScreenBmp->DataAddress(),
bmpLenInBytes);
}
#if defined(__WINS__)
screenBufBmp->UnlockHeap();
... // bitblt the screenBufBmp to Screen
CleanupStack::PopAndDestroy(screenBufBmp);
#else // device
/**Once when the drawing is completed, the screen should be updated such that
* the changes made to the screen memory will be reflected on the screen.
* The general way of doing this is by adding a redraw event(TRawEvent::ERedraw)
* to the system queue using UserSvr::AddEvent() API, which updates the screen
* immediately.
*
* But the TRawEvent::ERedraw is a event generated by the host OS ( typically by
* WM_PAINT ) which is meant for emulator environment( as per the S60 SDK help
* document ). However on S60 2nd edition devices, the screen gets updated
* properly by adding this event to the system queue. But S60 3rd Edition
* devices does not have any immediate effect after the Redraw event is added to
* the system queue. The screen is updated only after notifying the screen
* device about the out of date region(s), which is a known issue.
*
* Also accessing the display memory with UserSvr::ScreenInfo() is deprecated
* from S60 3rd Edition onwards.
*/
TRawEvent redraw;
redraw.Set(TRawEvent::ERedraw);
UserSvr::AddEvent(redraw);
/**As a workaround solution, it is still possible to use the old drawing method
* and force the screen to update itself by specifying the out of date region
* using CFbsScreenDevice's Update() API. The code snippet for doing the same is
* as follows:
*/
CFbsScreenDevice* iMyScreenDev = CFbsScreenDevice::NewL(0 ,displayMode);
// the screennumber will be 0 if phone supports single screen where as the
// displaymode can be as per your choice
RRegion iMyregion;
iMyregion.AddRect(TRect(0,0,240,320)); // the out of date rect region.
iMyScreenDev->Update(iMyregion);
iMyregion.Close();
#endif
注意:以上两种直接描画方法的一个共同的问题是窗口服务器不了解描画,因此它不能通知应用程序是否出现另一个窗口或者窗口组。 即使当应用程序得到一个事件而失去焦点的时候,它们也不能停止直接描画,因为直接描画实在太快了,并且屏幕内容有可能被弄乱。这可能发生在当在玩游戏的时候,突然有电话打进来的情况下。
3> CDirectScreenAccess
新近的GT 6.1版本提供了一个应用编程接口用于直接描画,将能解决前面提到的问题。
这个应用编程接口由两个类组成:一个MDirectScreenAccess类,提供用于应用程序的回调方法,还有一个CDirectScreenAccess类处理与窗口服务器的通讯。
CDirectScreenAccess的NewL方法获得一个窗口服务器会话、CONE的图形设备、应用程序窗口和一个到MDirectedScreenAccess导出类的指针作为参数。 在CDirectScreenAccess::StartL被调用来激活直接描画支持之前,客户端窗口服务器缓冲应该溢出。为了能自动更新屏幕,屏幕设备的SetAutoUpdate方法需要使用ETrue参数(CFbsScreenDevice::SetAutoUpdate(ETrue) ),否则gc的draw指令不会在模拟器中立即显示,而是在下一次冲刷(Flush)视窗服务器时才显示。当直接描画支持激活的时候,CDirectScreenAccess产生一个CFbsBitGc图形上下文,可以被应用程序用来在屏幕上绘画。
当另一个窗口出现在应用程序窗口上时,CDirectScreenAccess从窗口服务器取得一个事件来中断描画。 CDirectScreenAccess然后调用MDirectScreenAccess派生类的AbortNow方法,这个方法必须被应用程序重载以便中断描画。为了防止屏幕被弄乱,窗口服务器直到中断描画事件被处理的时候才画重叠窗口(回调MDirectScreenAccess::Restart)。
以下是class CDirectScreenAccess的描述:
头文件
#include <W32STD.H> // ws32.lib
class CMyDsaAppContainer: public CCoeControl,
public MDirectScreenAccess
{
// new functions ++
... // other functions
public:
virtual ~CMyDsaAppContainer();
void Repaint(const TRect & aRect);
protected:
void ConstructL(const CCoeControl* aParent, const TRect& aRect);
private:
void PaintUsingDsa(CBitmapContext & aGc, const TRect & aRect);
// new functions --
// from MDirectScreenAccess ++
public:
/** This function is called by the window server when direct screen access must
* stop (for example because a dialogue is moved in front of the area where direct
* screen access is taking place).
* In response to this, direct screen access must stop immediately. In simple cases,
* this will involve cancelling the active object that is driving the drawing to the
* screen.
* No attempt to call a Window Server API function can be made from
* AbortNow(), because then a temporary deadlock will occur. This is because WSERV
* is waiting to receive the client's acknowledgment that it has aborted, and so will
* not be able to service the call. As soon as the restriction no longer applies,
* the function Restart() will be called.
*
* @param aReason The reason why direct screen access was terminated.
*/
void AbortNow(RDirectScreenAccess::TTerminationReasons aReason);
/** This function is called by the window server as soon as direct screen access
* can resume.
* This function should call CDirectScreenAccess::StartL() within a trap harness.
* If this leaves, e.g. through lack of memory, direct screen access cannot be
* restarted. StartL() re-calculates the clipping region, so that if direct screen
* access was aborted because another window appeared in front of it, that window
* will not be overwritten when direct screen access resumes.
* In this function, you can resume calls to Window Server Client Side API functions.
*
* @param aReason Provides the reason why direct screen access was terminated.
*/
void Restart(RDirectScreenAccess::TTerminationReasons aReason);
// from MDirectScreenAccess --
... // other functions
private:
CDirectScreenAccess * iDsa;
... // other member data
};
源文件
CMyDsaAppContainer::~CMyDsaAppContainer()
{
...
iDsa->Cancel();
delete iDsa;
...
}
void CMyDsaAppContainer::Repaint(const TRect & /*aRect*/)
{
TRect rect = Rect();
PaintUsingDsa(*iDsa->Gc(), rect); // drawing with DSA
iDsa->ScreenDevice()->Update();
}
void CMyDsaAppContainer::ConstructL(const CCoeControl* aParent,
const TRect& aRect)
{
...
iDsa = CDirectScreenAccess::NewL(CEikonEnv::Static()->WsSession(),
*(CEikonEnv::Static()->ScreenDevice()), Window(), *this);
iDsa->StartL();
...
}
void CMyDsaAppContainer::PaintUsingDsa(CBitmapContext & aGc,
const TRect & aRect)
{
// draw with aGc
aGc.DrawLine(...);
...
}
void CMyDsaAppContainer::AbortNow(
RDirectScreenAccess::TTerminationReasons aReason)
{
...
iDsa->Cancel();
...
}
void CMyDsaAppContainer::Restart(
RDirectScreenAccess::TTerminationReasons aReason)
{
TRect rect = Rect();
...
iDsa->StartL();
Repaint(rect);
...
}
参考资料:
1) Implementing Direct Screen Access.pdf
//////////////////////////////////////////////////////////////////////
2.双缓冲
2.1介绍
当图片直接绘制到屏幕时,产生动画时。屏幕可能会有抖动闪屏。
常用的一个方法是将图片绘制在off-screen缓冲上,然后在绘制完成后拷贝它的内容到屏幕上。
在绘图量上,双缓冲并不比直接画到屏幕上小,但就显示接口的瓶颈来说,双缓冲比直接调用系统设备绘图要小。使用双缓冲的一个主要目的就是为了减小显示的瓶颈问题,从而达到图像不闪烁。如果频繁使用多缓冲,而不加以组织和限制的话,那么图像还是要闪烁的,而瓶颈问题还是存在的。绘图相对于CPU来说还是很慢的,所以要使用双缓冲。
直接往屏幕上画图的速度是很慢的。这个问题在制作动画的时候尤其明显,因为我们可能要在一秒中之内刷新屏幕几十次。如果按每秒二十五屏的速度来刷新的话,就意味着我们要在一秒中之内画一千多个小方块!
而Double Buffering的办法可以解决这个问题。这是因为:
1、写内存远比写屏(IO)快多了
2、无论所画图形有多复杂,我们只需要做一次IO操作
Symbian中的图形设备:
图形设备描述CGraphicsDevice图形设备的基类CBitmapDevice位图化图形设备的基类CFbsDevice使用字体位图服务器的图形设备基类CPrinterDevice具有打印功能的设备基类CWsScreenDevice使用窗口服务器的屏幕设备CFbsBitmapDevice使用字体位图服务器的设备具体实现CFbsScreenDevice使用直接屏幕访问(DSA),而不通过窗口服务器
Symbian中的绘图上下文的关系:
其中CWindowsGc用于屏幕绘制,CFbsBitGc则用于内存绘制。
只要能配合显示设备的速度,抵消显示瓶颈问题,就不会引起屏幕闪烁,双缓冲只是一个比较好的技术办法而已。当在运动物体动态模糊处理上,可能使用的就不是双缓冲了,而是多缓冲并存了,不过目前Symbian游戏中好像还没有用到这个技术,也许是这个技术对CPU要求过高的原因。
2.2实现方式
#include <W32STD.H> // ws32.lib
// protected
void CMyOffScreenPainter::ConstructL()
{
iOffScreenBmp = new (ELeave) CWsBitmap(CEikonEnv::Static()->WsSession());
CWsScreenDevice* screenDev = CEikonEnv::Static()->ScreenDevice();
User::LeaveIfError(iOffScreenBmp->Create(screenDev->SizeInPixels(),
screenDev->DisplayMode()));
iOffScreenDev = CFbsBitmapDevice::NewL(iOffScreenBmp);
// iOffScreenGc is an instance of CFbsBitGc
User::LeaveIfError(iOffScreenDev->CreateContext(iOffScreenGc));
... // other code
}
void CMyOffScreenPainter::SizeChanged()
{
TRect rect = Rect();
User::LeaveIfError(iOffScreenBmp->Resize( rect.Size() ));
User::LeaveIfError(iOffScreenDev->Resize( rect.Size() ));
iOffScreenGc->Resized();
... // other code
}
void CMyOffScreenPainter::DrawWithOffScreenBmp()
{
iOffScreenGc->DrawArc(...);
... // some code about drawing
}
void CMyOffScreenPainter::Draw(const TRect & /*aRect*/)
{
CWindowGc & gc = SystemGc();
// draw the offscreen bmp to srcreen
gc.BitBlt( Position(), iOffScreenBmp);
... // other code
}
CMyOffScreenPainter::~CMyOffScreenPainter()
{
delete iOffScreenGc; iOffScreenGc = NULL;
delete iOffScreenDev; iOffScreenDev = NULL;
delete iOffScreenBmp; iOffScreenBmp = NULL;
... // other code
}
- DSA(直接写屏)和双缓冲
- Symbian屏幕双缓冲和DSA
- Symbian屏幕双缓冲和DSA
- Symbian屏幕双缓冲和DSA
- Symbian屏幕双缓冲和DSA
- Symbian屏幕双缓冲和DSA
- Symbian屏幕双缓冲&&DSA
- Symbian屏幕双缓冲&&DSA
- Symbian屏幕双缓冲&&DSA
- Symbian屏幕双缓冲&&DSA
- Symbian屏幕双缓冲&&DSA
- Symbian屏幕双缓冲&&DSA
- Symbian屏幕双缓冲&&DSA
- Symbian屏幕双缓冲&&DSA
- Symbian屏幕双缓冲&&DSA
- Symbian屏幕双缓冲&&DSA
- Symbian屏幕双缓冲&&DSA
- Symbian屏幕双缓冲&&DSA
- 用hosts文件来禁止访问网址
- error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
- 最近自己有点蒙
- 《MEF程序设计指南》博文汇总
- JDBC数据库链接
- DSA(直接写屏)和双缓冲
- 寻求帮助
- 下拉框赋值问题
- 更改SERVLET里的方法中的传递参数的名称为request,response(原先的为:arg0,arg1)的方法
- zju2857图像转换Image Transformation
- 查看linux 版本命令
- 新人贴,求分
- 在S60加载Bitmap和Mask的效率
- CnPack VCL组件库中 TCnButtonEdit 组件的BUG修正