从事MTK平台的工作者很多,做高仿机的人也很多,MTK的界面变得越来越花哨,但是对于MTK平台实现进行研究的文章并不多,希望这篇文章和以后的几篇BLOG能为MTK的初学者带来一些帮助。
怎么使用MTK的函数显示图片相信大家都会,但是图片的解码过程就不是人人都去研究了,由于我们做JVM移植层的需要,最近对MTK的图形解码进行了一些研究。
MTK在绘制图形的时候使用的是gdi_image_codec_draw这个函数,在这个函数中,MTK会根据img_type这个参数从gdi_image_codecs这个全局数据结构数组中取得相应绘制回调函数。gdi_image_codecs全局变量在gdi_image.c文件中定义。
(1)BMP解析过程
当img_type为GDI_IMAGE_TYPE_BMP_FILE_OFFSET的时候,MTK会调用gdi_image_bmp_draw_handler作为BMP文件解析的处理函数。在这个函数中,MTK会根据程序员指定的显示大小对bmp文件进行放大或者缩小,由于这里我们只是简单研究一下,就不以图片的放大或者缩小为例子了,如果要研究图片的放大或者缩小,请各位自己研究。我们只研究不对bmp进行放大或者缩小的过程——gdi_image_bmp_draw_file函数。
gdi_image_bmp_draw_file这个函数在gdi_image_bmp.c文件中。这个函数一开始首先建立了一个比特流,在我看来就是把这个BMP文件打开,并把BMP的文件的数据放到gdi_bytestream_buffer这个全局数组中,为以后的解码做准备。接下来大家可能就要迷糊了,MTK使用的是gdi_image_bmp_draw_internal_from_file这样一个函数,这个函数是什么呢!说出来下一条,这个函数是个宏!确切的说这个曾经是函数调用的语句实际上被GDI_IMAGE_BMP_DRAW_INTERNAL这个宏所替代,而这个宏在gdi_image_bmp_codec.h中有一个同名函数,这样gdi_image_bmp_draw_internal_from_file就被指示成了GDI_IMAGE_BMP_DRAW_INTERNAL。我第一次就晕菜了,搞不懂为什么MTK要费这么大劲,绕这么多弯。
GDI_IMAGE_BMP_DRAW_INTERNAL就是解码过程,我们可以看到这里面有1,4,8,16,24,32位BMP的解码函数,如果我们解码的不是这些标准的BMP呢!对不起,MTK会重启!
(2)静态GIF图片的解码过程
当img_type为GDI_IMAGE_TYPE_GIF_FILE_OFFSET的时候,MTK会调用gdi_image_gif_draw_handler函数进行解码。在这个函数中同样分为可变大小和不可变大小,数据来源于文件还是来源与内存资源,这4种情况,我们还是只讨论数据来源于文件而且不进行缩放操作的情况——gdi_image_gif_draw_file。
gdi_image_gif_draw_file这个函数有两个分支。在整个函数的实现中,会有一个do{}while(0)的循环,在定义了硬件解码宏之后,在硬件解码结束以后使用一个break;语句跳出循环。软解码的代码在硬解码的代码后面,没有宏控制。当硬解码宏打开后,完全靠硬解码结束后的break来区分软硬解码。这样,如果硬解码出现问题,可能会造成严重的后果。我不清楚MTK为什么这样做,可能是他们认为硬解码的所有返回都已经处理过了,所以才使用这种方式的吧!
在6226,6228等高端的MTKCPU上,图像都是硬解码的。在这些CPU内部有一组专门的解码电路来对图形进行解码。在硬解码的时候,MTK最终会调用gif_decode_hw这个函数对CPU内部的寄存器进行相关的设置,最后调用GIF_START这个宏函数开始对GIF图形进行解码。
在6225以下的CPU中,所有的图像都是软解码的,这时MTK使用了ret = gdi_image_gif_draw_file_decoders[gdi_act_layer->cf] 这个语句。gdi_image_gif_draw_file_decoders是一个函数指针数组。这个数组定义在gdi_image_gif.c中,这个数组中的元素(例如gdi_image_gif_draw_internal_from_file_8等)其实都指向GDI_IMAGE_GIF_DRAW_INTERNAL这个宏,这个宏又在Gdi_image_gif_codec.h中对应一个同名函数。好像在哪里见过哈!对!BMP就是这样指来指去的。MTK的风格就是这样奇怪而统一。
GDI_IMAGE_GIF_DRAW_INTERNAL就是GIF的解码过程。 一开始,MTK向我们展示了一个catch机制,MTK把最后的2张GIF图片保存在一个gdi_gif_cache的数据结构中,首先检查程序员需要的是不是这两张图片。如果是就不用解析了,不是就要重新解析。解析的算法我们就不讨论了,喜欢研究的可以看看GDI_IMAGE_GIF_CODEC这个函数的实现,GIF由于有动画的设定,所以解析起来要比BMP复杂多了!
(3)JPEG图片解码过程
当img_type为GDI_IMAGE_TYPE_JPG_FILE_OFFSET的时候,MTK会调用gdi_image_jpeg_draw_handler函数进行解码。这个函数最终会调用gdi_image_hwjpeg_draw_internal。MTK把JPEG的软解码和硬解码的接口在这个函数层次统一了起来,gdi_image_hwjpeg_draw_internal会调用gdi_image_hwjpeg_start_decode,gdi_image_hwjpeg_start_decode再调用jpeg_decode_process函数。jpeg_decode_process函数再负责调用JPEG解码核心对JPEG图片进行解码。最后的解码函数为decode_jpeg_file这个函数。无论是软解码还是硬解码都要调用decode_jpeg_file这个函数。在JPEG图形解码这部分,MTK做的还是比较符合中国工程师习惯的。
还有一点,如果我们只想取得JPEG图像的数据,那么我们只要调用gdi_image_jpeg_draw_file_to_buffer或者gdi_image_jpeg_draw_to_buffer这两个函数好了。我们测试过,MTK对这两个函数的封装,封装的比较好。这样就给了我们一个通过JPEG图像传递某种信息的渠道。哦!搞的想干坏事一样,不说了,不说了!
(4)PNG图片解码过程
当img_type为GDI_IMAGE_TYPE_PNG_FILE_OFFSET的时候,MTK会对PNG进行解码,经过了上面的分析,我相信大家都能对MTK的图形解码的流程有了比较深刻的理解了,各位可以自己分析这个过程,我这边就不赘述了!
以上我自己是对MTK图形解码过程的大致分析,有不当之处还希望大家能够指正。
下一篇BLOG我准备谢谢如何在MTK平台下建立静态TASK,以及如何从MMITASK中接管控制权。这部分很简单同样是对应初学者的,希望老鸟不要拍砖!!!
MTK界面设计中消除PNG图片的杂色背景
PNG图片因为体积小,背景可以透明,并且由于采用矢量图像技术可以制作无锯齿的圆角类型的图形等优势。曾经在网页和一些领域得到广泛应用。近来可能是由于MTK的热门提供了更多的工作机会,导致不少以前从事网站创作的美术人员也转入到手机行业。这些同志们设计的UI充斥了大量的PNG图片。给MTK的MMI工程师提供了不小的挑战。
确切的说,我认为在MTK手机平台上,PNG并不具有显著优势。MTK会把PNG转化为一种BMPA的中间格式,该格式并不比同样效果的BMP小,有时还会大上许多。至于背景透明,其实在MTK平台,是可以把一幅BMP显示为背景透明的。这样PNG存在的唯一意义大约就在于显示一些更精致的需要流线型图像效果的UI里了。而PNG在使用过程中,在某些方面,比如叠加,比如在层中使用,往往会存在一些显示效果不尽如人意的地方。这些原因导致许多MMI工程师不喜欢PNG,除非在非常必要的情况下,否则会直接到PNG用ASD转为BMP使用。虽然许多时候我也不建议使用PNG,但有些必要场合,使用PNG毕竟也能为手机显示增色不少。
一次帮客户设计UI时,遇到在层中显示PNG图片显示乱屏,图的背景有杂色,图标边缘通透等问题,严重影响了显示效果。本可转为BMP重新设计,后来抱着学习的态度搜了一下52RD,竟然有人提供了一个解决了函数gdi_image_abm_set_source_layer,后来研究了一下,果然解决了这个问题。
附上样例程序:(由于该层在移动过程中只是位置改变,然后刷在屏上,所以没有把把创建层的代码与使用代码分开,图片IMG_IDLE_TIME_BG为PNG,背景上显示的字符也为PNG图片,能够消除PNG层乱屏杂色问题的函数是gdi_push_and_set_alpha_blending_source_layer,当然gdi_image_abm_set_source_layer也能达到)
void DrawDateTimeLayer(U16 x, U16 y)
{
#ifdef WIN32
buf_ptr = (PU8)malloc(180*61*2);
#else
buf_ptr = (PU8)med_alloc_ext_mem(180*61*2);
#endif
if (buf_ptr == NULL)
{
MMI_ASSERT(0);
}
gdi_layer_create_using_outside_memory(
0,
0,
180,
61,
&mylayer,
(PU8) buf_ptr,
(S32) 180*61*2);
gdi_layer_push_and_set_active(mylayer);
gdi_layer_set_position(x, y); //设置激活层的位置
gdi_push_and_set_alpha_blending_source_layer(mylayer); //把mylayer层作为blending层
gdi_layer_clear_background(GDI_COLOR_WHITE);
gdi_layer_set_source_key(TRUE, GDI_COLOR_WHITE);
gdi_image_draw_id(x, y, IMG_IDLE_TIME_BG); //绘制时间背景
gdi_pop_and_restore_alpha_blending_source_layer(); //恢复blending层
gdi_layer_pop_and_restore_active();
gdi_layer_set_blt_layer(dm_get_wallpaper_layer(), GDI_LAYER_MAIN_BASE_LAYER_HANDLE, mylayer, NULL);
gdi_layer_blt_previous(0, 0, UI_device_width-1, UI_device_height - 1);
}
楼的朋友,这些函数25平台上是有的,在文件gdi_layer.c中可以看到该函数实现过程,如果你看不到gdi_layer.c,可能你所在的单位为保护代码外流,对MTK代码做了封装,但应该不影响使用。 2楼的朋友,函数gdi_image_abm_set_source_layer用法与gdi_push_and_set_alpha_blending_source_layer一样,传入要设置层就可以了,不过gdi_image_abm_set_source_layer在设置新层为alpha_blending_source_layer时会返回老的alpha_blending_source_layer层的HAND,你要保存好,使用完后一定要记着把老的层还原。 3楼的朋友的回复又让人受益良多,熟悉各种图像格式的优缺点和MTK与之对应的API和处理机制,对我们的工作是有莫大好处的。
3楼 usstwonder 发表于 2009-6-25 10:39:00
PNG图片可以定义alpha通道,有了alpha通道,可以定义每个像素点的透明度,能做出很炫的图片效果来。当前MTK平台的PNG解码器用的是一个开源的PNG解码器:libpng,但是mtk对libpng进行了封装,在解码的时候需要传入blending层,mtk解码的时候,会跟据图片的alpha通道值,自动把解码出来的像素的和blending层的像素进去混合叠加,然后把混合叠加后的效果输出。gdi_push_and_set_alpha_blending_source_layer()是设置blending层,也就是png解码时用来当作背景的层。