前些时间做过一个ddraw显示来代替原来的gdi显示,显示的速度快了许多,在不断的探索中,终于明白了ddraw的用法。现在记下来。本人文字功底欠佳,如有不明白的地方,一起讨论。
1.ddraw 与 gdi的区别
两者都可以用来显示,gdi是windows比较抵触的绘图方式,ddraw是用gdi来实现的,但是ddraw利用了硬件加速。ddraw更多的应用于游戏编程当中,在wince6.0上是可以使用ddraw的,所以手机编程中用draw会提程序速度。
2。 我的开发环境是Windows Mobile 6.0 ,用ddraw blt方式来实现显示。
ddraw采用了COM的形式架构,如果实在 .c 的文件名下编译会有链接错误,这个注意,关于COM这个还没有顾上研究。
第一步: 创建一个DirectDraw : DirectDrawCreate(NULL, &mini_suf_ddraw_api, NULL);
第二步:设置显示模式为全屏独占模式: mini_suf_ddraw_api->SetCooperativeLevel(suf_hwnd, DDSCL_FULLSCREEN);
第三步:创建一个主表面:
mini_suf_dds_desc.dwSize = sizeof(mini_suf_dds_desc) ;
mini_suf_dds_desc.dwFlags = DDSD_CAPS;
mini_suf_dds_desc.ddsCaps.dwCaps =DDSCAPS_PRIMARYSURFACE;
//创建主表面
ret = mini_suf_ddraw_api->CreateSurface(&mini_suf_dds_desc, &mini_suf_dds_ptr, NULL);
主表面是和显存直接相关的,可以说你往这个表面写任何东西都会直接的反映到显示屏幕上。先前我直接往这个主表面写数据,发现有刷新的问题,原因就是在这里。所以我的程序中采用了向附表面写数据然后copy到主表面,这样做是稍微有点损失效率。
第四步: 创建一个副表面:
int systemWidth = GetSystemMetrics(SM_CXSCREEN);
int systemHeight = GetSystemMetrics(SM_CYSCREEN);
mini_suf_dds_desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT ;//|DDSD_CAPS ;//| DDSD_CAPS;
//mini_suf_dds_desc.ddsCaps.dwCaps = ;
mini_suf_dds_desc.dwWidth = systemWidth; //mini_suf_dds_desc.dwWidth;
mini_suf_dds_desc.dwHeight = systemHeight; //mini_suf_dds_desc.dwHeight;
ret = mini_suf_ddraw_api->CreateSurface(&mini_suf_dds_desc, &mini_suf_dds_buf, NULL);
这个地方又有玄机:我好不容易研究出来的。为啥副表面非得指定设备的宽和高。因为主表面的创建是完全和设备相关的,显示屏啥样就创建成啥样,但是附表面只是一个缓存区,需要这些宽高。
第五步: 加锁 :
mini_suf_dds_buf->Lock(0, &mini_suf_dds_desc, DDLOCK_WAITNOTBUSY, 0) ;
我老觉得这个Lock太害人了,我原来以为是和互斥锁差不多呢,其实根本不是那么回事。其实调用lock的时候,返回了好多参数给DDSURFACEDESC结构体,包括显存地址(这个是最关键的),包括显示的宽高。
加锁是加了副表面的锁,所以获得的是副表面的显存地址,我们就是要往这个副表面的显存里写东西。加锁加到主表面那就傻了,这个还和flip有关系
flip是创建了两个相关表面,当一个表面数据写满后交换这两个表面的地址,交换地址就是通过每次的lock来实现的。这个在后边的flip实现有详细介绍。
第六步 显示
mini_suf_dds_ptr->Blt(NULL, mini_suf_dds_buf, NULL, NULL, NULL);
这就没啥说的了,直接将副表面的数据copy到主表面,实现显示,和bitblt类似。
3. ddrwa的flip显示实现。
第一步. 创建
基本和前面类似,参数不同而已,
mini_suf_dds_desc.dwSize = sizeof(mini_suf_dds_desc) ;
mini_suf_dds_desc.dwFlags = DDSD_CAPS|DDSD_BACKBUFFERCOUNT;//|DDSD_HEIGHT|DDSD_WIDTH;
mini_suf_dds_desc.ddsCaps.dwCaps =DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP;
mini_suf_dds_desc.dwBackBufferCount = 1; //一个后台缓存
/*mini_suf_dds_desc.dwWidth = 480;
mini_suf_dds_desc.dwHeight = 640;
mini_suf_dds_desc.ddpfPixelFormat.dwRGBBitCount = 16;*/
//创建主表面
ret = mini_suf_ddraw_api->CreateSurface(&mini_suf_dds_desc, &mini_suf_dds_ptr, NULL);
创建了带一个后台缓存的主表面,在真正的游戏编程当中,有很多的后台缓存表面,贴上位图,不断的进行切换,windows自带的例子有这样的。
第二步. 加锁 的时候要切换表面的显存地址的, 前面的代码是没有切换的注意啊。
int ret;
//mini_suf_dds_buf->Restore();
ret = mini_suf_dds_buf->Lock(0, &mini_suf_dds_desc, 0, 0) ;
if(ret != DD_OK)
InitFail(_T("lock field"));
g_pddrawBitmapBuf = (U8 *) mini_suf_dds_desc.lpSurface;
第三步. 显示: 就简单了。
mini_suf_dds_ptr->Flip( NULL, 0 );
4.横屏
横盘的时候,和手机本身和Windwos Mobile 的版本有相当大的关系。
这是我写的自动横屏的部分,刚开始我旋转90度,死活不行,改成270度就好了,不知道微软是咋滴了,换个手机就好了。
总结:
手机软件开发,一定不能忘记手机本身的局限性。
还要在一个问题无法解决的时候,多多尝试,自己写写demo程序,这个提高自己编码能力最快的办法。