MTK 功能机 MMI绘画

MMI架构及基础知识

MMI全称Man Macheine Interface(人机接口或人机界面)。人机界面分为文件界面(如DOS)和图形界面(如Windows)两种类型,功能机的平台属于简单的图形界面。下图为我们MMI简洁架构图


最上层的Application Layer在MMI基础中已经详细介绍,我们通常要做的也就是这一层的开发。Application Layer往下就是平台的图形子系统,图形子系统一般由系统提供商提供,MMI绘画介绍的都是图形子系统的内容。图形子系统再往下就是硬件驱动。

  • 图形系统(分为三个子系统)

GUI(Graphical User Interface)即图形用户界面或图形用户接口。原始意义的GUI是针对最终用户来说的,也就是一种供用户使用的图像操作界面。而从开发的意义来说,这里的用户应该是开发Application Layer的程序员(二次开发程序员),GUI也变成了操作系统提供给应用层的一整套绘画函数接口,也就是说从开发意义上来说GUI与图形系统是同一个概念。

模版子系统:将与用户交流的功能界面分类整理,取其原型归类后形成模版。如Windows系统中的窗口模版(对话框,单文档或多文档等),控件模版(按钮,文本框等)等。当然mtk平台的Category Screen,WGUI Controls等都属于模版类接口。简而言之,能自行绘制并且能自行与用户交流的元件其原型都可以称之为模版。
绘画子系统:此系统之接口只负责绘画,不负责与用户交流,通常模版类元件都有其相应的绘画类接口。
设备子系统: GDI(Graphical Device Interface),图形设备接口。此类接口负责与设备协调,并对上层形成一套简洁统一的接口。一般情况下此类接口只能被图形系统内部使用。

  • WGUI Layer

WGUI,其字面意义是Wrap GUI,也即包装后的GUI,我们可以认为WGUI就是图形系统中的模版子系统。WGUI分为四个部分。

Category Screen:可以认为是屏幕模版集,与Windows系统中窗口模版类型。
WGUI Controls:控件模版,我们系统中控件的概念与Windows系统中控件的概念一致。
Touch Screen: Touch Screen可以认为是图形系统最核心控制模块。在mtk平台早期版本时,WGUI中的各种元件在系统运行都是各自为政,这样虽然可以保证元件的灵活性,但是造成了公共资源协调困难。以前公共资源都是焦点元件(系统任何时候只有一个元件属于激活状态)独占,当触摸屏出现以后,因为触摸屏事件的随意性极大,一般地元件很难全盘掌控,所以后来在WGUI中加了一个Touch Screen模块,用以全盘接手触摸屏事件的管理。
**Draw Manager:**Draw Manager是伴随Touch Screen一起出现的,因Touch Screen模块为方便控制,会记录Screen中所有的元件的属性及状态,为减轻图形系统代码冗余,将Category Screen中的元件也统一一起交由Draw Manager绘制,Draw Manager会依据每一个元件的属性及状态依次将其绘制出来,

  • GUI Layer

这里介绍GUI Layer应该是前面讲的图形系统中的绘画子系统,一般绘画子系统的接口会分为以下几种类型。

图形:即通过程序计算,以画点为基础绘制出各种几何图形。
图像:以外部的图像资源为基础,通过各种图像解码器绘制出相应的点阵图。
文本:即文字或文本串的输出。
填充:将图形与图像整合到一起,绘制出相应的填充区域,通常用来做为各种元器件背景。
控件:每种控件都会有很多相应的绘制接口以控制其在不同的交互模式下显示状态,这里的接口只负责绘制各种状态,不负责与用户交互,一般此类接口都是给WGUI Controls调用的。

有的平台的绘画子系统与设备子系统分的不是很清楚,上述几种类型中控件,填充及文本是专属GUI的,图形与图像虽然在GUI中也有接口,但我们在应用层中更多的是直接使用GDI中的接口,虽然这样不太合理,但目前状态下也只能这样(因GDI中功能不太完善)。
在早期版本中,GUI接口名称都以pixtel_UI_为前缀,后来都改为gui_为前。另GUI Layer中加了一个Theme模块,主要用来控制系统显示风格。

  • GDI Layer

Graphic:图形类接口,GDI中的图形绘制一般会有相应的硬件加速。
Image:图像分为静态图片与动画两种,另每种格式图像在GDI中都有其相应的解码器,GDI会依据图像格式自动寻找相应的解码器。
Font:即字库与字体管理,用以绘制单个文字。
LCD&Layer:控制每个硬件屏幕(目前主要是主屏及副屏),以及与屏幕想辅的缓冲区Layer管理。

  • 排版常量
    下图为一些绘画常用的尺度常量

    以上常量可能会与一些宏,如MMI_MENUITEM_HEIGHT也表示菜单项的高度,但建议使用MMI_menuitem_height,因为MMI_MENUITEM_HEIGHT是菜单项高度初始值,这个值可能会变化,所以在使用排版常量时建议使用图示的这些值。

  • 常用颜色值
    下图为一些系统常用的颜色:

    颜色也可以自己进行定义:
    GUI:gui_color_my_color={255,103,102,100} 最后一个参数表示透明度
    GDI:gdi_color_my_color={255,255,103,102} 第一个参数表示透明度

文本

  • 字体
    常用字体属性:
//以上几种属性都可以在结构体stFontAttribute中设置,使用方式如下例:
void mmi_myapp_entry(void)
{
    setFontAttribute f = {0};
    f.size = LARGE_FONT;
    ......
    gui_move_text_cursor(50,100);
    gui_set_text_color(UI_COLOR_RED);
    //gui_set_font将属性值传给系统
    gui_set_font(&f);
    gui_print_text((UI_string_type)GetString(STR_MYAPP_HELLO));
    //设置带边框的文字
    //gui_set_text_border_color(UI_COLOR_GREEN);
    //gui_print_border_text((UI_string_type)GetString(STR_MYAPP_HELLO));
    ......
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 精确排版
void mmi_myapp_entry(void)
{
    S32 x,y,w,h;
    stFontAttribute f = {0};
    f.size = LARGE_FONT;
    ......
    gui_set_text_color(UI_COLOR_RED);
    gui_set_font(&f);
    gui_set_text_border_color(UI_COLOR_GREEN);
    //测量字串宽高
    gui_measure_string((UI_string_type)GetString(STR_MYAPP_HELLO),&w,&h);
    x=(UI_device_width-w)/2;
    y=(UI_device_height-h)/4;
    //移动到相应位置
    gui_move_text_cursor(x,y);

}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

效果如下:

图形

图形都是以点为基础画出来的,下面的代码将在屏幕正中间画一个黑点

void mmi_myapp_entry(void)
{
    ......
    gui_putpixel(UI_device_width/2,UI_device_height/2,UI_COLOR_BLACK);
    gui_BTL_double_buffer(0,0,UI_device_width-1,UI_device_height-1);
    ......
    //gdi_draw_point也可以达到同样的效果
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 线

函数gui_line可以画一个像素宽的直线:

void mmi_myapp_entry(void)
{
    ......
    //x轴初始值,x轴结束值;y轴初始值,y轴结束值
    gui_line(30,100,150,140,UI_COLOR_BLACK);
    gui_BLT_doublebuffer(0,UI_device_width-1,UI_device_height-1);
    ......
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

画线还有一些其他函数:

void mmi_myapp_entry(void)
{
    ......
    //起始点坐标,结束点坐标
    gui_draw_rectangle(x-4,y-4,x+w+4,y+h+4,UI_COLOR_RED);
    gui_print_border((UI_string_type)GetString(STR_MYAPP_HELLO));
    ......
    //gui_draw_rectangle用来画边框,函数gdi_draw_rect与gui_draw_rectangle效果一样
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 填充矩形

填充矩形是一个实心的框,使用方式如下

void mmi_myapp_entry(void)
{
    ......
    gui_draw_rectangle(x-4,y,4,x+w+4,y+h+4,UI_COLOR_RED);
    gui_fill_rectangle(x,y,x+w,y+h,UI_COLOR_GREY);
    gui_print_border((UI_string_type)GetString(STR_MYAPP_HELLO));
    ......
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

其他填充矩形:

  • 带框填充矩形
void mmi_myapp_entry(void)
{
    ......
    //红色边框,填充色(255,204,255,102),边框为3
    gdi_draw_frame_rect(x-4,y-4,x+w+4,y+h+4,gdi_act_color_from_rgb(255,204,255,102),GDI_COLOR_RED,3);
    gui_print_border_text((UI_string_type)GetString(STR_MYAPP_HELLO));
    ......
    //gdi_draw_round_rect圆角矩形
    //gdi_draw_button_rect按钮风格矩形
    //gdi_draw_shadow_rect带阴影的矩形
    //gdi_draw_gradient_rect递进色填充矩形
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

图像

  • 图像分类

图像按显示方式可分为静态图像与动画两种。
按格式可分为BMP,JPG,GIF,PNG等等。

按存储方式可分为以下三种:
1. 资源:图像资源是使用最多的一种存储方式。资源又分为两种使用方式,一是资源ID,一时资源Buffer,资源Buffer即以GetImage(IMAGE_ID)方式由资源ID转换过来。
2. 文件:即在系统运行时从文件系统中动态获取的图像。
3. Buffer:与资源存储方式不同,资源存储的图像内容加入了平台(RDA,展讯,MTK)自定义的格式数据,而Buffer只有纯粹的图像数据(如网络在线下载的临时图像数据等)。

  • 静态图像

暂时借用主菜单中ORGANIZER的图标:

#include "MainMenuDef.h"
...
void mmi_myapp_entry(void)
{
    ......
    gdi_image_draw_id(30,110,MAIN_MENU_MATRIX_ORGANIZER_ICON);
    //gdi_image_draw(30,110,(U8*)GetImage(MAIN_MENU_MATRIX_ORGANIZER_ICON));
    gdi_image_draw_resized_id(30,100,20,30,AIN_MENU_MATRIX_ORGANIZER_ICON);
    gui_BLT_double_buffer(0,0,UI_device_width-1,UI_device_height-1);
    ......
}
//gdi_image_draw_id是以资源ID方式显示图像,gdi_image_draw则是以资源Buffer方式显示图像。

//gdi_image_draw_resized_id可以对图片进行缩放,除了要传入x,y坐标外,还要传入输出图片宽高。

当图像放在文件系统中的时候(假设路径为:"D:\\MM_OR.gif")
此时显示图片的方式也可以为:
gdi_image_draw_file(100,100,(S8*)L"D:\\MM_OR.gif");

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

常用的静态图像显示函数有如下一些(除Buffer图像其他存储图像方式不需要传入格式参数):

  • 动画

动画的显示方式与静态图像类似,借用ORGANIZER中的动画图标

......
gdi_handle my_anim;

void stop_my_anim(void)
{
    //参数my_anim主要用来停止动画
    gdi_anim_stop(my_anim);
}


void mmi_myapp_entry(void)
{
    ......
    gdi_anim_draw_id(50,100,MAIN_MENU_MATRIX_ORGANIZER_ANIMATION,&my_anim);
    //左软键停止动画
    SetKeyHandler(stop_my_anim,KEY_LSK,KEY_EVENT_UP);
    ......
}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

动画资源的播放还有一些其他方式:

  • 精确排版

图像都可以用gdi_image_get_dimension_id测量出宽高(动画与静态图像都用此函数)

void mmi_myapp_entry(void)
{
    ......
    //量出动画的宽高
    gdi_image_get_dimension_id(MAIN_MENU_MATRIX_ORGANIZER_ICON,&w,&h);
    x = (UI_device_width-w)/2;
    y = (UI_device_height-h)/2;
    gdi_image_draw_id(x,y,MAIN_MENU_MATRIX_ORGANIZER_ICON);
    ......
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

背景

图形系统中每种可视化的元件都可以为其加上背景,背景是元件显示风格的最大衬托,如果将所有元件背景统一变化,则很容易使整个系统的显示风格产生变化,所以通常的图形系统会将所有的元件的背景集中起来,再加上一些别的容易控制又显眼的元素,就形成了整个系统的主题(Theme)。

系统中大部分主题是以背景为基础,熟悉了背景的使用方式,也就差不多熟悉了主题的修改方法。

  • 填充矩形

每个背景就是一个图形或图像填充起来的矩形,我们只要初始化结构体UI_fill_area,然后交由函数gui_draw_filled_area画出来即可。

UI_fill_area的定义如下:
typedef struct_UI_filled_area
{
    dword                       flags;           //总控制标志
    UI_image_type;              b;               //背景图像
    gradient_color*             gc;              //递进颜色
    color                       c;             //背景颜色
    color                       ac;            //替换颜色
    color                       border_color;    //边框颜色
    color                       shadow_color;    //阴影颜色
    UI_transparent_color_type   transparent_color;//透明色
}
//参数flags是结构体的总控制中心,通常按一下方式组合而成:
flags = 类型标志|边框标志|阴影标志
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

类型标志有如下一些:

边框标志有如下一些:

阴影标志有如下一些:

  • 颜色
//下面演示以颜色为背景的使用方法
void mmi_myapp_entry(void)
{
    ......
    UI_filled_area_filler={0};
    EntryNewScreen(SCR_MYAPP_MAIN,NULL,mmi_myapp_entry,NULL);
    entry_full_screen();
    clear_screen();

    filler.flags=UI_FILLED_AREA_TYPE_COLOR|UI_FILLED_AREA_BORDER|UI_FILLED_AREA_SHADOW;
    filler.c=UI_COLOR_GREY;
    filler.border_color=UI_COLOR_DARK_GREY;
    filler.shadow_color=UI_COLOR_3D_FILLER;
    gui_draw_filled_area(20,20,156,150,&filler);
    ......
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

效果图如下:

  • 递进颜色
//下面演示递进色的使用方法:
void mmi_myapp_entry(void)
{
    ......
    UI_filled_area_filler={0};
    static color g_colors[3]={{255,0,0},{0,255,0},{0,0,255}};
    static U8 perc[2]={30,70};
    gradient_color gc={g_colors,perc,3};

    EntryNewScreen(SCR_MYAPP_MAIN,NULL,mmi_myapp_entry,NULL);
    entry_full_screen();
    clear_screen();

    filler.flags=UI_FILLED_AREA_TYPE_GRADIENT_COLOR;
    filler.c=&gc;
    gui_draw_filled_area(20,20,156,150,&filler);
    ......
}

//递进色需要用到一个结构体gradient_color,其定义如下:
typedef struct_gradient_color
{
    color *c;       //颜色列表,数量由最后一个参数n决定
    byte *p;        //百分比列表,个数为n-1,依次表示两个相邻颜色递进宽度占整个宽度百分比
    byte n;         //颜色数量
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 图像
使用图像作为背景
void mmi_myapp_entry(void)
{
    ......
    filler.flags=UI_FILLED_AREA_TYPE_BITMAP;
    filler.b=GetImage(IMG_GLOBAL_SUB_MENU_BG);
    gui_draw_filled_area(20,20,156,150,&filler);
    ......
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 纹理
//纹理就是不停地用一副图填充背景,直到填满为止
void mmi_myapp_entry(void)
{
    ......
    filler.flags=UI_FILLED_AREA_TYPE_TEXTURE;
    filler.b=GetImage(IMG_FLEXIBLE_TITLEBAR_BG);
    gui_draw_filled_area(20,20,156,150,&filler);
    ......
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

效果图如下:

  • 3D效果
void mmi_myapp_entry(vodi)
{
    ......
    filler.flags=UI_FILLED_AREA_TYPE_3D_BORDER;
    //UI_FILLED_AREA_TYPE_CUSTOMFILL_TYPE1 其他类型3D效果背景
    //UI_FILLED_AREA_TYPE_CUSTOMFILL_TYPE2
    filler.c=UI_COLOR_GREY;
    gui_draw_filled_area(20,20,156,150,&filler);
    .....
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

效果图如下:

  • 百页窗及十字纹

下面是百页窗及十字纹的显示效果:

  • 动画背景
//动画背景无法使用填充域来实现
#include "Mmi_phnest_dischar.h"
......
gdi_handle_my_anim;
void mmi_myapp_entry(void)
{
    ......
    EntryNewScreen(SCR_MYAPP_MAIN,NULL,mmi_myapp_entry,NULL);
    entry_full_scren();
    clear_screen();
    gdi_anim_draw_id(0,0,IMG_ID_PHNSET_ON_0,&my_anim);
    ......
    //使用这个方法实现,当画面跳到第二帧以后,动画上面的文本及图像都会被盖住
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

层主要有两个作用:
缓冲:在某些频繁更新的界面中,如果某些显示元素一直不变化,我们就可以将这些元素提取出来画到一个模拟的屏幕中,当界面需要更新时,只需要将刷新的元素更新到另一个模拟屏幕上,而后将两个模拟屏幕合并到真正屏幕上,这样就省掉了不变元素的重画事件,从而减轻了系统复旦及加速画面更新。这样的模拟屏幕就称为层,也可以说层就是屏幕的缓冲空间。
特效:因为层的格式简单且统一,并且一般的图形系统中都会用硬件来加速层合并,所以在层合并是加上些特效会很方便并且实现极快。系统中的特效有通透,半透明,剪切等等。

//将不变的文本及图像放到新建的一个层中,将动画放到背景层中,每当有动画换帧时,只需要将新的帧画到背景层中,然后合并两个层到屏幕(动画刷新时自动会合并),这样we呢吧那就不会被动画覆盖住了。

//为了方便演示层特效,画一副大的浅色图
void mmi_myapp_entry(void)
{
    ......
    EntryNewScreen(SCR_MYAPP_MAIN,NULL,mmi_myapp_entry,NULL);
    entry_full_screen();
    clear_screen();
    gdi_image_draw_id(0,0,IMG_ID_PHNSET_WP_3);
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

效果图如下:

  • 创建层,激活层

创建层需要先建一个层句柄(可以把层句柄当作一个指向层的索引),我们都是通过层句柄来控制层的。函数gdi_layer_create用来创建层,其前四个参数指出层的位置及大小(位置是以实际屏幕左上角为原点的),最后一个参数是刚创建的层句柄地址,用以返回所创建的层。(创建层时系统要为其分配动态内存空间,而系统保留的内一般只够创建一个UI_device_width*UI_device_height,如果调用gdi_layer_create内存不足,需要调用gdi_layer_create_using_outside_memory,自己申请内存,作为参数传递基金年传递进去。

gdi_handle my_layer;
void mmi_myapp_entry(void)
{
    ......
    gdi_image_draw_id(0,0,IMAGE_ID_PHNSET_WP_3);
    //创建一个宽136,高130的层
    gdi_layer_create(20,20,136,130,&my_layer);
    //激活创建的层
    gdi_layer_set_active(my_layer);
    ......
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 基础层

系统开机的时候会为每个硬件屏幕创建一个基础层,基础层有以下几个特点:
1. 基础层由系统创建,无法删除。
2. 与硬件屏幕完全重合(位置大小都一样)。
3. 系统默认的激活层,EntryNewScreen时系统会自动将基础层激活。
4. 显示更加快速,基础层存储于芯片内的flash中,所以在其上面绘画极快,一般我们会将刷新频繁的内容放在基础层。

由以上几个特点看出,在使用多层的情况下,我们完全可以将基础层当成是硬件的屏幕来看待,也就是说普通程序完全可以忽略层的概念。
尽管可以创建多个层,但基础层肯定是不能浪费的,这里我们将在其上绘制背景图(在激活新层之前我们的东西都默认画到基础层中),在新建的层上才绘制文字及图标。
因系统一般只在EntryNewScreen时才会自动将基础层激活,为了避免特殊情况下使用层混乱,通常在新层绘画完毕后,主动将基础还原为激活状态:

void mmi_myapp_entry(void)
{
    ......
    gdi_handle base_layer;
    ......
    gdi_image_draw_id(0,0,IMG_ID_PHNSET_WP_3);

    //创建my_layer层
    gdi_layer_create(20,20,136,130,&my_layer);
    gdi_layer_set_active(my_layer);
    ......
    //获取基础层的句柄
    gdi_layer_get_base_handle(&base_layer);
    gdi_layer_set_active(base_layer);

    //指定需要合并的层,base_layer在最下面
    gdi_layer_set_blt_layer(base_layer,my_layer,NULL,NULL);
    //合并层
    gui_BLT_double_buffer(0,0,UI_device_width-1,UI_device_height-1);
    //gdi_layer_blt(base_layer,my_layer,NULL,NULL,0,0,UI_device_width,UI_device_height);
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

合并结果如下:

上面的显示结果出现两个问题,一是文本与图标向右下角偏移了一定的距离,二是背景图片被乱码遮盖住了。

  • 层坐标

在我们的图形系统中,所有描绘函数所使用的坐标参数原点并不是硬件屏幕的左上角,而是当前激活层的左上角,如下图所演示:


之前对文本图标的精确排版都是针对硬件屏幕来说,因以前都是在基础层上绘制,而基础层与屏幕重叠,所以不会出现问题。当转移到新层上之后,我们就需要将这个偏移反向抵消掉。

gdi_handle my_layer;
void mmi_myapp_entry(void)
{
    ......
    gdi_handle base_layer;
    S32 layer_offset_x=20,layer_offset_y=20;
    ......
    gdi_image_draw_id(0,0,IMG_ID_PHNSET_WP_3);
    //创建层
    gdi_layer_create(layer_offset_x,layer_offset_y,136,130,&my_layer);
    gdi_layer_set_active(my_layer);

    gui_set_text_color(text_color);
    gui_set_font(&f);
    gui_set_text_border_color(UI_COLOR_GREEN);
    gui_measure_string((UI_string_type)GetString(STR_MYAPP_HELLO),&w,&h);
    //减去偏差
    x=(UI_device_width-w)/2-layer_offset_x;
    y=(UI_device_height-h)/4-layer_offset_y;
    gui_move_text_cursor(x,y);

    gdi_draw_frame_rect(x-4,y-4,x+w+4,y+h+4,gdi_act_color_from_rgb(255,204,255,102),
    GDI_COLOR_RED,3);
    gdi_print_border_text((UI_string_type)GetString(STR_MYAPP_HELLO));
    gdi_image_get_dimension_id(MAIN_MENU_MATRIX_ORGANIZER_ICON,&w,&h);
    x=(UI_device-width-w)/2-layer_offset_x;
    y=(UI_device-height-h)/2-layer_offset_y;
    gdi_image_draw_id(x,y,MAIN_MENU_MATRIX_ORGANIZER_ICON);
    ......
    gdi_layer_get_base_handle(&base_layer);
    gdi_layer_set_active(base_layer);
    gdi_layer_blt(base_layer,my_layer,NULL,NULL,0,0,UI_device_width,UI_device_height);
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

显示结果如下,排版正确:

  • 通透

为了解决背景被遮住的问题
首先要将新层清理一下,用函数gdi_layer_clear将层刷成单一的颜色(层激活后立即执行)
但是这样背景图会被其他颜色覆盖,所以需要使用层特效”通透”,只要用函数gdi_layer_set_source_key将某一颜色设为层的通透色,在合并的时候,系统会自动将层中与通透色相同的颜色忽略掉(就是这些颜色看不到直接看到的是底层的颜色)。

void mmi_myapp_entry(void)
{
    ......
    gdi_image_draw_id(0,0,IMG_ID_PHNSET_WP_3);
    gdi_layer_create(layer_offset_x,layer_offset_y,136,130,&my_layer);
    gdi_layer_set_active(my_layer);

    gdi_layer_clear(GDI_COLOR_BLUE);
    gdi_layer_set_source_key(TRUE,GDI_COLOR_BLUE);
    ......
    gdi_layer_get_base_handle(&base_layer);
    gdi_layer_set_active(base_layer);
    gdi_layer_blt(base_layer,my_layer,NULL,NULL,0,0,UI_device_width,UI_device_height);
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

显示结果为

  • 剪切

剪切就是在层中设一个限制区域,只有在这个区域中的绘制才是有效的,否则就会被自动忽略。
剪切有两个特点:
1. 每个层一定有而且只能有一个剪切区域
2. 剪切区域一经设置,永久生效。所以剪切区域用完后须使用gdi_layer_reset_clip还原(不还原可能什么东西都画不上来)。

void mmi_myapp_entry(void)
{
    ......
    gdi_image_draw_id(0,0,IMG_ID_PHNSET_WP_3);
    gdi_layer_create(layer_offset_x,layer_offset_y,136,130,&my_layer);
    gdi_layer_set_active(my_layer);
    gdi_layer_clear(GDI_COLOR_BLUE);
    gdi_layer_set_source_key(TRUE,GDI,COLOR_BLUE);
    //剪切
    gdi_layer_set_clip(40,25,100,100);
    //半透明 0-全透明
    //gdi_layer_set_opacity(TRUE,128);
    ......
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

//剪切效果

  • 释放层
//创建层需要为其分配内存空间,所以层用完需要手动释放(如果不释放无法创建别的层)
gdi_handle my_layer;
void mmi_myapp_exit(void)
{
    ......
    gdi_layer_free(my_layer);
}
void mmi_myapp_entry(void)
{
    ......
    EntryNewScreen(SCR_MYAPP_MAIN,mmi_myapp_exit,NULL,NULL);
}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 锁屏
//假设屏幕中有一个title以及一个menu,那么可能会看到如下绘制流程
draw screen start

    draw title start
    .....
    gdi_layer_blt(.....)
    draw title end

    draw menu_start
    .....
    gdi_layer_blt(.....)
    draw menu end

.....
gdi_layer_blt(.....)
draw screen end
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

从上面可以看出gdi_layer_blt被调用了三次,虽然我们只需要最后一次,但title和menu中的gdi_layer_blt不能少,因为title及menu都有可能独立于screen之外刷新,而我们在系统中频繁调用gdi_layer_blt会极大影响系统性能。为了解决这个我们引进了一套锁屏机制,其原理是在绘画时加入一个计时器,只在当计数器为零时gdi_layer_blt才会真正起作用,流程图如下:

draw screen start-------------------------计数器=0
gdi_layer_lock_frame_buffer();-----------计数器=1
    draw title start
    gdi_layer_lock_frame_buffer();--------计数器=2
    .....
    gdi_layer_unlock_frame_buffer();------计数器=1
    gdi_layer_blt(...)
    draw title end
    draw menu start
    gdi_layer_lock_frame_buffer();--------计数器=2
    .....
    gdi_layer_unlock_frame_buffer();------计数器=1
    gdi_layer_blt(.....)
    draw menu end
.....
gdi_layer_unlock_frame_buffer();----------计数器=0
gdi_layer_blt(.....)
draw screen end
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

gdi_layer_frame_buffer()会将计数器加一,gdi_layer_unlock_frame_buffer()会将计数器减一。显而易见,上面的流程图中只有最后一次gdi_layer_blt才会起实际作用。下面是代码中使用范例

void mmi_myapp_entry(void)
{
    .....
    EntryNewScreen(SCR_MYAPP_MAIN,mmi_myapp_exit,NULL,NULL);
    gdi_layer_lock_frame_buffer();
    .....
    .....
    gdi_layer_unlock_frame_buffer();
    gdi_layer_blt(base_layer,my_layer,NULL,NULL,0,0,UI_device_width,UI_device_height);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值