现在的位置: 首页 > 综合 > 正文

MTK 层详解

2013年01月18日 ⁄ 综合 ⁄ 共 9391字 ⁄ 字号 评论关闭

最近工作中经常要涉及到对层的修改,特在网上搜集了一些资料,特分享于此。

对于层,有个核心理念:代表屏幕上像素点阵的数据集合

一般来说,MTK上一个像素由两个字节表示

 

 

 

一,mtk平台上旋转图像的代码,可以任意角度旋转。

void gdi_image_Rotate_Slope(double x,double y ,double dx ,double dy ,U16 IMAGE_ID,double cosn,double sinn)
{
S32 i,
j,
n,
imge_width,
imge_width2,
imge_height,
imge_height2,
transit_layer_buff_size1,
transit_layer_buff_size2;
U16* transit_layer_buff_ptr1,
* transit_layer_buff_ptr2;
U16 a,r,g,b;
gdi_handle      transit_layer1,transit_layer2,last_active_layer;
S32 j2,i2;
double wb,x0,y0,x1,y1,u,v;

gdi_layer_multi_layer_enable();

gdi_image_get_dimension_id(IMAGE_ID,&imge_width,&imge_height);
transit_layer_buff_size1=((imge_width)*(imge_height)*16)>>3;
transit_layer_buff_ptr1=(U16*)mmi_frm_scrmem_alloc(transit_layer_buff_size1);
gdi_layer_create_using_outside_memory(0,0,imge_width,imge_height,&transit_layer1,(PU8)transit_layer_buff_ptr1,transit_layer_buff_size1);
gdi_layer_push_and_set_active(transit_layer1);
gdi_layer_clear(GDI_COLOR_BLUE);
gdi_layer_set_source_key(TRUE, GDI_COLOR_BLUE);
gdi_image_draw_id(0,0,IMAGE_ID);
gdi_layer_pop_and_restore_active();

imge_width2=240;
imge_height2=320;
transit_layer_buff_size2=(imge_width2*imge_height2*16)>>3;
transit_layer_buff_ptr2=(U16*)mmi_frm_scrmem_alloc(transit_layer_buff_size2);
gdi_layer_create_using_outside_memory(0,0,imge_width2,imge_height2,&transit_layer2,(PU8)transit_layer_buff_ptr2,transit_layer_buff_size2);
gdi_layer_push_and_set_active(transit_layer2);
gdi_layer_clear(GDI_COLOR_BLUE);
gdi_layer_set_source_key(TRUE, GDI_COLOR_BLUE);

for(i=0;i<imge_height2;i++)
{
for(j=0;j<imge_width2;j++)
   {
   x0=((double)j-x)*cosn+((double)i-y)*sinn+dx;
   y0=((double)i-y)*cosn-((double)j-x)*sinn+dy;
   //x0=(j-x)*cosn+(i-y)*sinn;
   //y0=imge_height+(i-y)*cosn-(j-x)*sinn;
   j2=(S32)x0;
   i2=(S32)y0;

   if((i2<(imge_height-1))&&(j2<(imge_width-1))&&(i2>0)&&(j2>0))
    {

    v=y0-i2;
    u=x0-j2;
    if(u>0.5)
     j2++;
    if(v>0.5)
     i2++;
    *(transit_layer_buff_ptr2+(i)*imge_width2+j) = *(transit_layer_buff_ptr1+i2*imge_width+j2);

   }
   }
}

gdi_layer_pop_and_restore_active();
gdi_layer_get_active(&last_active_layer);
gdi_layer_flatten(last_active_layer,transit_layer2,NULL,NULL);
//gdi_layer_set_blt_layer(last_active_layer,transit_layer2,NULL,NULL);
gdi_layer_free(transit_layer1);
gdi_layer_free(transit_layer2);
mmi_frm_scrmem_free(transit_layer_buff_ptr1);
mmi_frm_scrmem_free(transit_layer_buff_ptr2);
gdi_layer_multi_layer_disable();
}

二,近日高仿机大行其道,丰富的视觉效果,方便的触摸操作成了MMI设计和模仿的重中之中
当人们在尽情享受这种创意带来的便利的同时MMI工作者却不得不去设计维护一个个功能越来越复杂,
界面越来越炫丽的令人头痛的窗口,没有别的办法,稍一失误,就能导到整个窗口界面凌乱,功能失效,
最终使程序员不得不不把大量的时间和精力花在令人头痛的界面维护中,在那些复杂的代码中寻找着可能的BUG层的运用也许是解决界面刷新,复杂界面的堆叠,代码维护最好的方法

近日有不少朋友和我讨论层的使用,而MTK有关这方面的资料也确实比较少,少数几个掌握了的人又视之如珍宝不愿轻易示人,这里举一个简单的例子说明层的创建和运用,在这里我们创建两个层,加上基层一共三个,基层作为背景层,MTK最多支持四个层合成,层的创建最好放在窗口里或者窗口控件里,在退出窗口时释放,要不会出问题的。,当然在别的地方创建也是可以的,但要注意不能是窗口需要频繁调用的函数,因为每次创建窗口都要分配内存,频繁调用会导致内存耗尽,退出时切记释放。该代码省略了一些与层无关的内容,比如按按键处理,触屏处理,请知悉
gdi_handle my_layer1, my_layer2, my_base_layer;
PU8 buf_ptr;
void exit_myapp_screen(void)
{
    if (buf_ptr != NULL)
    {
        med_free_ext_mem((void **)&buf_ptr);
    }
    if (my_layer1 != NULL)
    {
        gdi_layer_free(my_layer1);
    }
    if (my_layer2 != NULL)
    {
        gdi_layer_free(my_layer2);
    }
}
static void entry_myapp_screen(void)
{
    U8 *gui_buffer;
    BOOL isEnable;
    gdi_color base_color;

    EntryNewScreen (SCR_ID_XXXX,exit_myapp_screen,entry_myapp_screen, NULL);

    gui_buffer = GetCurrGuiBuffer(SCR_ID_XXXX);
    //设置窗口不带状态栏
    entry_full_screen();
    //开启多层
    gdi_layer_multi_layer_enable();

    /*该处的2等于((GDI_LCD_WIDTH*GDI_LCD_HEIGHT*GDI_MAINLCD_BIT_PER_PIXEL)>>3)
    一个像素使用2个字节表示,即65536色*/
      buf_ptr = med_alloc_ext_mem(GDI_LCD_WIDTH*GDI_LCD_HEIGHT*2);
    if (buf_ptr == NULL)
    {
    //加一些错误提示
        return;
    }
    /*从外部内存创建窗口,因为MTK提供的直接创建函数只能创建
    一个层和屏幕大小一样的层*/
    gdi_layer_create_using_outside_memory(
                                            0,
                                            0,
                                            UI_device_width,
                                            UI_device_height,
                                            &my_layer1,
                                            (PU8) buf_ptr,
                                            (S32) GDI_LCD_WIDTH*GDI_LCD_HEIGHT*2);
    //激活新层1
    gdi_layer_set_active(my_layer1);
    //把背景全刷成红色
    gdi_layer_clear_background(GDI_COLOR_RED);
    //设置红色为通透色
    gdi_layer_set_source_key(TRUE, GDI_COLOR_RED);
       /*在层1上画图,位置是相对层的位置,因为层大小和基层一样,
       且原点和基层重合,故该座标也是相对屏幕原点的位置*/
    gdi_image_draw_id(50, 100, IMG_ID_PHOART_ICON_6);
       //创建层2,使用系统提供的直接创建函数从层内存直接创建
    gdi_layer_create(0, 0, UI_device_width, UI_device_height, &my_layer2);
    //激活新层2
    gdi_layer_set_active(my_layer2);
    gdi_layer_clear_background(GDI_COLOR_TRANSPARENT);//刷色
    gdi_layer_set_source_key(TRUE, GDI_COLOR_TRANSPARENT);//设置通透
    //在层2上输出字串
    gui_move_text_cursor(50, 200);
    gui_print_bordered_text(L"hellow word");

    //在最后要激活基层
       gdi_layer_get_base_handle(&my_base_layer);
    gdi_layer_set_active(my_layer1);
    //得到当前活动层的通透属性
    gdi_layer_get_source_key(&isEnable, &base_color);
    //设置通透
    gdi_layer_set_source_key(TRUE, base_color);
    //指定合并层,注意叠加的顺序
    gdi_layer_set_blt_layer(my_base_layer, my_layer1, my_layer2, 0);
    //刷新到屏幕上
    gdi_layer_blt_previous(0, 0, UI_device_width - 1, UI_device_height - 1);
}

有关gdi_layer_create函数再说明一点,如果你通过这个函数创建的层大小比屏幕小,倒是可以创建多个的,但你所创建的层大小的总和,不能超过屏幕大小
还有一点需要注意的是,如果程序运行在仿真上,最好使用MALLOC和FREE替换med_alloc_ext_mem和med_free_ext_mem,也就是写成

#ifdef WIN32
malloc
#else
med_alloc_ext_mem
#endif


#ifdef WIN32
free
#else
med_free_ext_mem
#endif

三,对于MTK平台来说,菜单和屏幕特效曾经在很长的一段时间里,影响甚远。但对于其设计过程和方法,由于各种各样的原因,很少有人提及。

这里介绍一种不同于前几天日志所载之方法的另一种方法,就是复制图层,其核心实现函数如下。

void my_gdi_layer_copy(gdi_layer_struct* dst_layer, int dst_x, int dst_y, int width, int height,
gdi_layer_struct* src_layer, int src_x, int src_y)
{
U16 *dst_buf, *src_buf;
int i;

if (dst_x < 0 || dst_y < 0 || dst_x + width > dst_layer->width || dst_y + height > dst_layer->height)
{
if (dst_x < 0)
{
   width -= -dst_x;
   src_x += -dst_x;
   dst_x = 0;
}
if (dst_y < 0)
{
   height -= -dst_y;
   src_y += -dst_y;
   dst_y = 0;
}
if (dst_x + width > dst_layer->width)
{
   width = dst_layer->width - dst_x;
}
if (dst_y + height > dst_layer->height)
{
   height = dst_layer->height - dst_y;
}
}
if (src_x < 0 || src_y < 0 || src_x + width > src_layer->width || src_y + height > src_layer->height)
{
if (src_x < 0)
{
   width -= -src_x;
   dst_x += -src_x;
   src_x = 0;
}
if (src_y < 0)
{
   height -= -src_y;
   dst_y += -src_y;
   src_y = 0;
}
if (src_x + width > src_layer->width)
{
   width = src_layer->width - src_x;
}
if (src_y + height > src_layer->height)
{
   height = src_layer->height - src_y;
}
}
if (width <= 0 || height <= 0)
return;

dst_buf = (U16 *)dst_layer->buf_ptr + dst_y * dst_layer->width + dst_x;
src_buf = (U16 *)src_layer->buf_ptr + src_y * src_layer->width + src_x;
for (i = 0; i < height; i ++)
{
memcpy(dst_buf, src_buf, width * 2);
dst_buf += dst_layer->width;
src_buf += src_layer->width;
}
}

有兴趣的朋友可以自己利用该函数研发各种各样的变化效果。

 

 

四,MTK 的层在窗口设计中应用十分广泛,对于UI来说,没有一个窗口不是由层构成的,最基本的窗口至少也是在基层上设计的。以至于许多时候,如果对层不甚了解,连阅读代码有时也会遇到困难。我曾经写过一些有关层的使用的基础知识,由于有关这方面资料很少,许多时候只能在模拟器上打断点自己慢慢研究。

层可以在模拟器中调试,在模拟器上的TOOL菜单中的Info Monitor功能窗口,可以看到当前窗口由哪些层和各个层显示的内容,但不包括你虽然创建了,但没有调用函数gdi_layer_blt或者与他gdi_layer_blt相类似函数刷到窗口上的层。

处理PNG有关的函数
gdi_push_and_set_alpha_blending_source_layer
gdi_pop_and_set_alpha_blending_source_layer
gdi_set_alpha_blending_source_layer
gdi_get_alpha_blending_source_layer
这一组函数是在层上绘制PNG图片时使用,是PNG函数的专用函数,可以有效还原PNG图片的效果,前两个函数是成对使用,后一个函数作用同前两个,但使用上更自由一些,如果超过两个层,每个层都要画PNG,无疑使用gdi_set_alpha_blending_source_layer更加方便。最后一个是获取BLENDING层。

设置层的位置
gdi_layer_set_position
如果层上的数据是经过十分复杂的运算过程得出来的,比如要在IDLE上显示一个可以使用手指移动的农历是日历,这时就可以在层上画好要显示的数据,通话移动层的位置实现移动效果,要比每次重绘日历要快捷的多。

合并到BASE层
gdi_layer_flatten_previous_to_base
类似的函数还有
gdi_layer_flatten
前者是把当前窗口数据合并到基层,后者是把指定的层合的激活层上,该函数常常在退出窗口时使用,如果你见有些窗口在弹出对话框时该窗口背景突然消失,那大约就是因为未使用该函数的原因。因为MTK的很多窗口都不是单一层,更关键的是在创建新窗口时有时会释放老窗口的层资源,导到在老窗口非基层的显示数据丢失。该函数使用我们可以使用超过四个层来创建更为复杂的效果。

添加DM层
dm_add_blt_layer
有时你创建了层,激活了层,在层上也绘上了图片,也调用刷屏函数了,但你总是看不到屏上显示你的层,在模拟器中的Info Monitor窗口也看不到你的层,有时是你的层闪了一下就没影了,这时你就要研究dm_redraw_category_screen了,该函数做为大部分窗口的刷新函数,当窗口有变化时,就会执行该函数,而该函数中有一段类似如下的函数调用:
            gdi_layer_blt(
                dm_layers[0],
                dm_layers[1],
                dm_layers[2],
                dm_layers[3],
                dm_cat_scr_info.x1,
                dm_cat_scr_info.y1,
                dm_cat_scr_info.x2 - dm_cat_scr_info.x1 - 1,
                dm_cat_scr_info.y2 - dm_cat_scr_info.y1 - 1);
该调用会重新刷屏,很遗憾的是,你新创建的层不在数组dm_layers中,也就不在他刷新的范围内。要么调用dm_add_blt_layer把你的层加入,要么在dm_redraw_category_screen之后再把你自己的层刷到屏上。

使用外部内存创建层med_alloc_ext_mem当然类似函数也有很多,如mmi_frm_scrmem_alloc
确切的说,这两个函数都不能算是LAYER的函数,但如果你要使gdi_layer_create_using_outside_memory创建层,却少不了这两个函数。在这两个函数中,我们常常调用med_alloc_ext_mem来分配内存,是因为该函数能提供大约400K左右内存可供调用,而 mmi_frm_scrmem_alloc可以提供200~300K内存可供调用。但随着MTK手机沦为SP赚钱工具后,使用 med_alloc_ext_mem分配内存越来越不安全。在之前的一次设计中,出了问题,多亏公司一高手指点,才消灭了这一处使用的隐患。

保存层到文件
gdi_layer_save_jpeg_file
gdi_layer_save_pbm_file
gdi_layer_save_bmp_file

还有一类函数,设计窗口时可以处理层,但却不属于层函数,见一个同事使用,顺手拉来,以防忘记
dm_register_category_controlled_callback
dm_register_category_controlled2_callback
该函数会为创建的层指定绘定函数,并且这两个函数只能分别处理DM管理的层,DM管理的层及相关控件可以在结构 mmi_dm_control_ids_enum找到,这两个函数可以在DM指定的层上绘图,既可以处理不同的层,也可以处理相同的层,如果不同的层,可以定义在g_categories_controls_map中的control_set_p中定义如下,control_set_p中一般会包含基层,有时也包含新层,定义如下格式
const U8 categoryXXXX[] =
{
    4,
    DM_BASE_LAYER_START,
    DM_CATEGORY_CONTROLLED_AREA,
    DM_NEW_LAYER_START,
    DM_CATEGORY_CONTROLLED_AREA2,
};但是在该结构中只能定义两个层,如果你想创建第三个层并使用DM管理,就要使用上面提到的dm_add_blt_layer,层的处理函数也要自己写,维护。

刷屏函数
gdi_layer_blt_previous
gui_BLT_double_buffer
gdi_layer_blt
gdi_layer_set_blt_layer
前三个函数都是刷屏函数,前两个功能一模一样,只能指定刷新的区域,使用前需要用第四个函数指定参与操作的层。第三个可以自己指定要刷到屏的层和区域。需要说明的是,执行gui_BLT_double_buffer和执行gdi_layer_blt_previous不仅效果一样,执行过程也是一样的,gui_BLT_double_buffer只是个函数指针,指向gdi_layer_blt_previous,调用他最后执行的仍然是 gdi_layer_blt_previous

抱歉!评论已关闭.