Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1534871
  • 博文数量: 114
  • 博客积分: 10010
  • 博客等级: 上将
  • 技术积分: 1357
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-19 18:13
文章分类
文章存档

2010年(8)

2009年(9)

2008年(27)

2007年(62)

2006年(8)

我的朋友

分类: WINDOWS

2007-12-22 02:59:33

北京理工大学  20981  陈罡
原本想和win mobile 5播放mp3专题一样详细写一下关于win mobile 5平台
上显示各种格式图片的方法来着。后来觉得太麻烦,还是让代码来说话吧。

在前面的文章中,我曾经介绍过在win mobile 5平台上利用direct show
技术完成直接写屏操作的方法,对这部分有疑问的朋友可以参照一下。
就把这篇文章做为直接写屏的下一篇文章吧。做到了直接写屏以后,该如何
在屏幕上显示出图片呢?答案很简单,只要把图片通过某种方式转换成由像素
组成的数组,然后再利用直接写屏技术就可以显示到屏幕上了。
 
看上去像是废话,怎么把图片转换成“由像素组成的数组”呢?这就需要我们
移植图片的解码库了。网上有两个非常著名的图片解码库,一个叫做cximage,
另外一个叫做magic lib。这里我们只讨论接口相对清晰一些的cximage库,个人
感觉magic那套弄得比较混乱。
 
cximage库号称可以支持包括bmp, jpeg, png, gif等20多种图片文件格式。我
具体的看了一下,其实还只是上述这3种格式最流行,最有可能在手机这种存储
空间极为有限的介质中使用。我们这里主要讨论jpg,png,gif三种格式的图片
解码器。
 
往win mobile 5上面移植cximage可是一个很辛苦的工作了,不过为了
支持5mbox的跨平台开发,完全采用独立的图片解码库是非常必要的。
移植过程难以尽述,总之是一个很痛苦的过程,需要处理字节对齐问题、c语法
与c++语法不兼容问题、库的定义形式问题等等。这些都好解决,最让人恶心的
是cximage中对于gif图片的处理上。gif是由多帧图片组合而成的,最新的gif
往往是由一张底图,然后若干张图叠加在上面组成的。cximage处理gif的时候,
每次调用只能够取出来一帧的gif图像,而且采用比较的方法,读出一帧,看看
是不是用户指定的,如果不是就继续再读出来一帧,然后再看。。。
这种思路太慢了,对于gif的处理上,我全部重写了一遍。可以支持一次性读取
一张gif图片的所有帧到内存帧序列中,然后使用的时候请求哪一帧就显示哪一
帧, 速度提高了很多。
 
下面简述一下5mbox封装库的方法:
(1)新建ppc工程,把ezfbcore和imagecore两个目录拷贝到ppc工程中
 
(2)在“Solution Explorer”中添加Filter,具体方法请参考“win mobile 5
播放mp3音乐的方法(4)”,其中有详细描述。结果如下图所示:
(3)修改project properties的属性,如下图所示:
这里是添加额外需要包含的路径了
这是设定不使用预编译头文件
添加直接写屏的direct draw的lib
这些都设置好,应该就可以把库编译到项目里面去了。
 
(4)开始修改代码了:
#include "ezfb.h"     // 这个用于直接写屏
#include "M5ImgDec.h" // 这个是对cximage的封装
声明全局的对象(不是全局的也行,这里只是为了方便)
// Global Variables:
HINSTANCE   g_hInst;   // current instance
struct ezfb   g_fb ;    // direct draw object
CM5ImgDec   g_dec ;    // image decode core object
去掉什么menubar,那玩意影响全屏显示。
由于直接写屏最好是全屏操作的,所以需要把当前的程序设置成全屏显示的模式
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
 HWND hWnd;
    TCHAR szTitle[MAX_LOADSTRING];  // title bar text
    TCHAR szWindowClass[MAX_LOADSTRING]; // main window class name
    g_hInst = hInstance; // Store instance handle in our global variable
    // SHInitExtraControls should be called once during your application's initialization to initialize any
    // of the device specific controls such as CAPEDIT and SIPPREF.
    SHInitExtraControls();
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_WM5IMGDEMO, szWindowClass, MAX_LOADSTRING);
    //If it is already running, then focus on the window, and exit
    hWnd = FindWindow(szWindowClass, szTitle); 
    if (hWnd)
    {
        // set focus to foremost child window
        // The "| 0x00000001" is used to bring any owned windows to the foreground and
        // activate them.
        SetForegroundWindow((HWND)((ULONG) hWnd | 0x00000001));
        return 0;
    }
    if (!MyRegisterClass(hInstance, szWindowClass))
    {
     return FALSE;
    }
 hWnd = CreateWindowEx(WS_EX_TOPMOST,
  szWindowClass,
  szTitle,
  WS_POPUP,
  0,
  0,
  // 这里用GetSystemMetrics来获取全屏的窗口显示区
  GetSystemMetrics(SM_CXSCREEN),
  GetSystemMetrics(SM_CYSCREEN),
  NULL,
  NULL,
  hInstance,
  NULL);
 if (!hWnd) return FALSE;
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    // 这里的SetFocus只是为了保险起见加入的
 SetFocus(hWnd);
 // 这一句就是初始化直接写屏库了
 ezfb_init(&g_fb, hWnd) ;
    return TRUE;
}
然后把函数:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
的case WM_CREATE消息下面的关于menu_bar的代码都去掉。
好了现在应该就是全屏显示效果了。
大概说明一下CM5ImgDec类的主要函数和使用方法:(具体的参见代码)
u_short * img = NULL ; 
// 这个img用于存放解码后的结果的,需要分配内存
img = new u_short [g_fb.scr_width * g_fb.scr_height] ;
if(img == NULL) {
 PostMessage(hWnd, WM_CLOSE, 0, 0);
 break ;
}
// 每次使用CM5ImgDec对象之前需要先ResetImgDec一下
// 主要是多次使用的时候把上一次的数据清空(这里只是保险用的)
g_dec.ResetImgDec() ;
// 这句就是传入图片路径名称了
g_dec.Decode("/Program Files/WM5ImgDemo/a.jpg") ;
// 这一句是将解码出来的图片读取到img缓冲区中
g_dec.GetFrame1((u_char *)(img)) ;
//这一句就是利用直接写屏的库进行写屏操作了
ezfb_blt_screen(&g_fb, (u_char *)(img), MODE_BPP16,
    0, 0, g_dec.GetWidth(), g_dec.GetHeight()) ; 
g_dec.ResetImgDec() ;
delete [] img ;
对于jpg和png的操作都是基本相同的,这里需要说明的是,CM5ImgDec类
在封装的时候,我写了两个GetFrame,一个是普通的GetFrame另一个是
GetFrame1,这两个是有区别的,在颜色从高往低转换的时候(24位到16位)
GetFrame1的显示效果要远远强于GetFrame,但是速度较慢;GetFrame的
速度非常快,但是颜色从高往低转换的时候会有色差现象。该现象在过渡
色中尤为明显。
对于gif图片的显示代码如下:
int i ;
u_short * img = NULL ; 
img = new u_short [g_fb.scr_width * g_fb.scr_height] ;
if(img == NULL) {
 PostMessage(hWnd, WM_CLOSE, 0, 0);
 break ;
}
g_dec.ResetImgDec() ;
g_dec.Decode("/Program Files/WM5ImgDemo/a.gif") ;
// 到这里就不一样了,需要调用GetFrameCount获取总共有多少帧的图像
// 然后给GetFrame传入需要取出第几帧的帧序号才行(序号从0开始)
for(i = 0 ; i < g_dec.GetFrameCount() ; i++) {
 g_dec.GetFrame1((u_char *)(img), i) ;
 ezfb_blt_screen(&g_fb, (u_char *)(img), MODE_BPP16,
    0, i*g_dec.GetHeight(), g_dec.GetWidth(), g_dec.GetHeight()) ; 
}
g_dec.ResetImgDec() ;
delete [] img ;
其它的就是关于alpha通道了,这里我把所有的alpha都变成一个字节的灰度了数组,
0代表不透明,1代表透明。可以用CM5ImgDec的HasAlpha函数得知当前图片是否支持
透明。用GetAlpha获得图片的透明图。透明图是每个像素占一个字节的灰度图片。
具体处理的时候要与解码出来的图片做异或运算或者判别才可以。
要是对CM5ImgDec解码器还有不清楚的,直接到imagecore\wrapper\目录下看
M5ImgDec.h和M5ImgDec.cpp两个源文件就一切都明白了。呵呵,这个库可是精华中的
精华啊,花费了我不少时间,从win32移植到symbian、再移植到mobilinux、再移植
到win mobile,竟然都可以用;5mbox的diy工具就是用这个库加上vc编写出来的。
为了个大家一个直观的印象,这次吧编译好的二进制文件也贴出来,供大家研究
直接写屏库在开发过程中,我顺便移植了个点阵gb2312字库显示模块,以便于在
直接写屏的模式下也可以显示中英文混排的字符串。
最后的模拟器运行效果如下图所示:
好了,闲话少说,代码拿来!哈哈,希望能够对后来者开发win mobile或者其它跨平台
的嵌入式应用有些帮助。
 
源代码包下载:
文件: WM5ImgDemo_src.rar
大小: 1152KB
下载: 下载
 
二进制包下载:
文件: WM5ImgDemo_bin.rar
大小: 412KB
下载: 下载
安装方法:
解压后,需要将WM5ImgDemo这个目录整个地拷贝到win mobile 5手机存储的
/program files目录下面,然后可以通过资源管理器去启动该程序看效果。
基本功能:
左:清屏并显示汉字
上:显示png
下:显示gif
右:显示jpg
OK键:退出程序
 
用了好用的要评论一下喔!呵呵,发这个帖子竟然一直发到了2:55,汗。。。睡了。。。
阅读(5929) | 评论(10) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2009-11-23 14:33:21

在画PNG图像的时候不行! 透明的地方会默认为白色!

chinaunix网友2009-06-30 14:47:53

如何正确绘出带alpha的PNG图片?要如何异或操作? 是不是GetFrame的图片与GetFrameAlpha得到的图片异或?

chinaunix网友2009-06-30 11:22:20

兄弟,你这工程少了一个文件呀,打不开,你能邮一份完整的给我吗?谢谢: email: 357545146@qq.com

chinaunix网友2009-06-20 20:53:39

测试了你的源码,发现解码gif版本为 89a的有问题.希望陈生能完善,谢谢!

chinaunix网友2008-05-19 16:03:08

多谢阿.正需要mobile下的直接写屏的方法,在您这里看到了,受益匪浅,也希望您继续贴一些mobile下的开发技巧,万分感谢