北京理工大学 20981 陈罡
5mbox几乎所有的库都是采用标准c++编写的,因此移植起来非常得心应手,几乎是没什么障碍就编译过去了。现在到了图形显示这块了,5mbox在symbian和mobilinux系统里面均采用直接写屏的方法尽量加快图形图像的显示速度,如果win mobile也可以支持直接写屏就太方便不过了,所有的库的代码几乎不用修改就可以直接放到win mobile上面跑。
于是乎,广查资料,找到了win mobile系统上面对于屏幕二维、三维图形图像处理的方法,总结起来,主要有5种,罗列如下:
(1)raw framebuffer法
通过如下代码直接调用ExtEscape来获得raw的frame buffer,这种方法是目前所有方法里面速度最快的
方法,我在网上看到国外的视频开发高手,利用这个方法获得了win mobile2003上面24-30fps的h264视频播放效果呢!但是很不幸,微软已经明确表态,这种方法以后将不被支持。现在在win mobile 5.0/6.0上
根本无法调用这个函数。
RawFrameBufferInfo rfbi;
HDC hdc;
bool retval;
retval=false;
hdc=GetDC(m_hwnd);
if(hdc)
{
if(ExtEscape(hdc, GETRAWFRAMEBUFFER, 0, 0, sizeof(RawFrameBufferInfo), (char *) &rfbi))
{
if(rfbi.wFormat==FORMAT_565)
{
m_framebufwidth=rfbi.cxPixels;
m_framebufheight=rfbi.cyPixels;
m_xpitch=rfbi.cxStride;
m_ypitch=rfbi.cyStride;
m_cbpp=rfbi.wBPP;
m_framebuf=rfbi.pFramePointer;
retval=true;
}
}
ReleaseDC(m_hwnd,hdc);
(2)gui绘图方法
这个绘图就是最简单的形如win32 api的直接调用,可以使用诸如GetDC, ReleaseDC,BitBlt之类的函数进行块内存的直接贴图显示。该方式看上去似乎效率不高,多数游戏都没有采用这种绘图方式,只有少数的诸如扑克牌什么的对屏幕刷新的实时性要求不高的地方才会用到。
(3)GAPI方法
GAPI全称是(Game API),顾名思义就是用来编写游戏的api集合。如果我们需要用使用屏幕显存的话,可以很方便的通过如下代码实现:
添加头文件部分
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include
在当前工程属性的Linker->Input选项卡下面的Additional Dependencies填入
gx.lib
然后就可以调用GAPI了,GAPI的基本函数如下:
GXBeginDraw : Prepares the display for writing.
GXCloseDisplay : Releases any display resources used by GAPI.
GXCloseInput : Releases any input-related resources used by GAPI and returns button-press messages to their filtered mode.
GXEndDraw : Called when a display drawing cycle is finished.
GXGetDefaultKeys : Returns a list of virtual key codes that defines the best control layout for a game.
GXGetDisplayProperties : Returns a structure with detailed information about the display hardware.
GXIsDisplayDRAMBuffer : Returns if the device is a nonstandard display device.
GXOpenDisplay : Opens the display for use and ensures exclusive access to the video frame buffer memory.
GXOpenInput :Turns on the unfiltered button message mode.
GXResume : Resumes GAPI operations.
GXSetViewport : Defines a GAPI view port.
GXSuspend : Suspends GAPI operations.
这里通过GXOpenDisplay打开显示,并初始化到全屏显示状态;然后可以通过GXOpenInput获得按键和触摸屏的动作;在绘图的时候,通过GXBeginDraw和GXEndDraw来获得屏幕显存的指针;通过GXGetDisplayProperties函数获得屏幕显存的bpp,屏幕大小等等信息。
总的来说,GAPI是目前最方便的实现framebuffer的方法,但是也是微软明确声明过了,不再对其支持,也不提供升级服务。未来的win mobile 6.x或者下一代win mobile系统也许会淘汰掉GAPI,转而用direct 3d以及direct draw库来实现。
(3)Direct3D方法
这个方法可以说是很强的了,它既可以支持win mobile 5/6以及以上的版本,又可以进行二维和三维的绘图
操作,非常方便。但是网上评论它的速度慢、效率低。而且我大概扫了一眼它的函数,感觉挺复杂的。毕竟偶是坚定的open gl api派的,对这个不是很感冒。(幸好目前5mbox仅仅需要二维贴图操作)
(4)DirectDraw方法
这个方法微软已经明确表态将要大力推广下去,所以我也把开发的重点放在它的上面。
从它的over view里面看出,它是主要用于需要硬件加速的2维图像显示方面的库。
DirectDraw支持如下特性:
a)支持Bit-块的转换(blits)
b)支持页替换,支持多个后台缓冲
c)支持多个层的叠加,可以把一个图片层放到一个视频层上显示
d)支持透明图像混合
e)支持视频的YUV像素格式色彩空间转换
f)支持直接存取显示frame buffer
呵呵,我就是要这最后一个特性来实现直接写屏,把linux的frame buffer库移植到win mobile 5.0上。
当然了,类似GAPI,需要加入指定的头文件和lib;directdraw也是一样的需要如下头文件:
#include
需要添加link的附加依赖库ddraw.lib,然后就可以用了。这里顺便说一句,如果希望编写出既支持win mobile 2003又支持win mobile 5/6的程序,就最好不要用静态链接方法。而是采用LoadLibrary载入
ddraw.dll,然后通过GetProcAddress函数取得directdraw的api函数使用。这样,就可以做到无缝的在GAPI和directdraw或者direct3d这几个库之间切换了。只要有一个能用的,你的程序也可以运行。
基本的代码如下:
先定义一个结构,可以用来容纳directdraw必备的数据结构,以及程序中经常使用的变量和常亮还有宏。
#define EZFB_FAIL_CREATE_DD -1
#define EZFB_FAIL_SET_FULLSCREEN -2
#define EZFB_FAIL_GET_DD_INFO -3
#define EZFB_FAIL_CREATE_DDS -4
#define EZFB_TRUE 1
#define EZFB_FALSE 0
#define EZFB_LOCK(fb) (fb->dds_ptr)->Lock(0, &(fb->dds_desc), DDLOCK_WAITNOTBUSY, 0)
#define EZFB_MAP(fb) (fb->vga_ptr) = (unsigned short *)(fb->dds_desc.lpSurface) ;
#define EZFB_UNLOCK(fb) (fb->dds_ptr)->Unlock(0)
typedef unsigned char u_char ;
typedef unsigned short u_short ;
typedef unsigned int u_int ;
struct ezfb {
HRESULT res_handle ; // direct draw api result
LPDIRECTDRAW dd_ptr ; // direct draw object ptr
DDSURFACEDESC dds_desc ; // direct draw surface desc obj
LPDIRECTDRAWSURFACE dds_ptr ; // direct draw surface ptr
u_short* vga_ptr ; // points to frame buffer of screen
u_int scr_width ;
u_int scr_height ;
u_int scr_bpp ;
};
然后开始定义几个必备的函数:
int ezfb_init (struct ezfb* fb, HWND wnd_handle);
int ezfb_clear_screen (struct ezfb* fb);
int ezfb_release (struct ezfb* fb);
然后就是具体的实现过程了:
int ezfb_init(struct ezfb* fb, HWND wnd_handle)
{
DDCAPS dd_caps ;
DDCAPS dd_hel_caps ;
// init fb value
fb->vga_ptr = NULL ;
fb->dd_ptr = NULL ;
fb->dds_ptr = NULL ;
fb->scr_width = 0 ;
fb->scr_height = 0 ;
fb->scr_bpp = 0 ;
// create direct draw object
fb->res_handle = DirectDrawCreate(NULL, &fb->dd_ptr, NULL);
if (fb->res_handle != DD_OK)
return EZFB_FAIL_CREATE_DD ;
// Get exclusive mode
fb->res_handle = (fb->dd_ptr)->SetCooperativeLevel(wnd_handle, DDSCL_FULLSCREEN);
if (fb->res_handle != DD_OK)
return EZFB_FAIL_SET_FULLSCREEN ;
// make sure the direct draw is valid
(fb->dd_ptr)->GetCaps(&dd_caps, &dd_hel_caps) ;
if (!(dd_caps.ddsCaps.dwCaps & DDSCAPS_VALID))
return EZFB_FAIL_GET_DD_INFO ;
// create the main framebuffer surface
memset(&(fb->dds_desc), 0, sizeof(fb->dds_desc)) ;
(fb->dds_desc).dwSize = sizeof(fb->dds_desc) ;
(fb->dds_desc).dwFlags = DDSD_CAPS ;
(fb->dds_desc).ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE ;
fb->res_handle = (fb->dd_ptr)->CreateSurface(&(fb->dds_desc), &(fb->dds_ptr), NULL) ;
if (fb->res_handle != DD_OK)
return EZFB_FAIL_CREATE_DDS ;
if(EZFB_LOCK(fb) != DD_OK) return EZFB_FALSE ;
// save screen necessary params
fb->scr_height = fb->dds_desc.dwHeight ;
fb->scr_width = fb->dds_desc.dwWidth ;
fb->scr_bpp = fb->dds_desc.ddpfPixelFormat.dwRGBBitCount ;
EZFB_UNLOCK(fb) ;
return EZFB_TRUE ;
}
int ezfb_clear_screen(struct ezfb* fb)
{
if(EZFB_LOCK(fb) != DD_OK) return EZFB_FALSE ;
EZFB_MAP(fb) ;
int bytes_need = (int)(fb->scr_bpp / 8) ;
bytes_need = (bytes_need > 0) ? bytes_need : 1 ;
bytes_need *= fb->scr_width ;
bytes_need *= fb->scr_height ;
memset(fb->vga_ptr, 0x00, bytes_need) ;
// set whole screen black
EZFB_UNLOCK(fb) ;
return EZFB_TRUE ;
}
int ezfb_release(struct ezfb* fb)
{
if(fb->dds_ptr != NULL) {
(fb->dds_ptr)->Release() ;
fb->dds_ptr = NULL ;
}
if(fb->dd_ptr != NULL) {
(fb->dd_ptr)->Release() ;
fb->dd_ptr = NULL ;
}
return EZFB_TRUE ;
}
最后需要说明一下的是,我们可以通过LPDIRECTDRAW指针指向的directdraw对象调用directdraw api
中的Lock api,然后获得DDSURFACEDESC对象,该对象的lpSurface就是当前层的内存地址了,直接对其
进行内存操作就相当于frame buffer的操作了。还有千万不要忘记,在绘制完毕后要Unlock。如果没有
Unlock的话,将会死机喔。只有扣电池了!这一点一定要注意。hoho,偶就是抠了好几次电池了。。。
哈哈,不好意思。
贴上个模拟器的图show一下,这个在真机上跟模拟器的效果是一样的,比模拟器要快得多。