MTK上层显示机理的学习总结

来源:互联网 发布:centos yum安装ant 编辑:程序博客网 时间:2024/05/17 01:44

MTK上层显示机理的学习总结 收藏
1. 窗体的重画:

通过观察可以发现,每个窗体模板都调用这样一个函数:dm_redraw_ctegory_screen()。这个函数便是显示窗体的函数。它内部的实现是这样的:获得该窗体所包含的组件及它们的属性,再根据组件的类型和属性,调用不同的接口,逐一绘制各组件。

无论是窗体所包含的组件,还是组件的属性,都是根据模板ID获取的。那么现在摆在面前的有两个问题:一、模板ID是如何传递到这个函数中的;二、模板ID和窗体组件、组件的属性,是如何关联到一起的。我们逐一解决这两个问题。

1>   模板ID是如何传递到这个函数中的

模板ID,是ShowCategory..Screen()过程中,所显示的界面的编号,千万不要与EntryNewScreen(scrID,…) 函数中传入的窗口ID相混淆。它们以“MMI_”为前缀,被定义在枚举型结构MMI_CATEGORY_ID_LIST中,又通过结构体dm_data_struct和它的全局结构体变量g_dm_data,在应用程序中被广泛使用。先看看结构体dm_data_struct的定义:

typedef struct

{

     S32 s32ScrId;

     S32 s32CatId;

S32 s32flags;

}

    其中,s32ScrId是当前窗口ID,也就是我们使用EntryNewScreen()时传入的那个参数;而s32CatId才是模板ID;最后的flag,是模板需要显示软键盘、清屏等动作时,所置的标志变量,它在上面提到的那个dm_redraw_category_screen()函数中被判断。还是来重点看一下第2个结构体成员s32CateId的使用。

    以 ShowCategory6Screen() 为例。这个显示模板的函数中,经常可以看到这样的语句:

      dm_data.s32ScrId = (S32)GetActiveScreenId();

      dm_data.s32CatId = MMI_CATEGORY6_ID;

      dm_data.s32flags = 0;

      dm_setup_data(&dm_data);

   再看dm_setup_data()干了什么:

void dm_setup_data(dm_data_struct *dm_data)

{

      g_dm_data.s32CatId = dm_data->s32CatId;

      g_dm_data.s32ScrId = dm_data->s32ScrId;

      g_dm_data.s32flags = dm_data->s32flags;

}

这样,MMI_CATEGORY6_ID就被很自然的赋到g_dm_data.s32ScrId中了,然后随着这个全局变量,顺利的被带到了dm_redraw_category_screen()中。

 

2>   模板ID和窗体组件、组件的属性,是如何关联到一起的

通过分析dm_search_control_set函数,发现窗体模板的组件和属性相关信息都隐藏在一个宏伟的结构体数组中:g_categories_controls_map。

      这个结构体定义dm_category_id_control_set_map_struct定义如下:

typedef struct

{

   U16 category_id;

   U8 *control_set_p;

   S16 *default_coordinate_set_p;

   S16 *rotated_coordinate_set_p;

}dm_category_id_control_set_map_struct;

第一个结构体成员,是窗体模板的ID;

第二个结构体成员,是组件数组的首地址;

第三个结构体成员,是默认的组件属性数组的首地址;

第四个结构体成员,是特殊的组件属性数组的首地址。

dm_search_control_set()函数dm_search_coordinate_set()函数就是通过匹配模板ID在结构体数组中分别获取的组件集合和组件属性集合的。

好,到这里,我们刚才提出的两个问题就明确了。

 

2.构成窗体的组件的定义

  根据上文可以知道,窗体组件的定义与窗体模板ID是通过结构体dm_category_id_control_set_map_struct关联的。现在来看一看组件数组的结构。下面将以5号模板为例。

const U8 category5[] =

{

  5,

  DM_BASE_LAYER_START,

  DM_SCR_BG,

  DM_BASE_CONTROL_SET1,

  DM_MULTILINE_INPUTBOX1,

  DM_CATEGORY_CONTROLLED_AREA

}

在这个组件数组中,第一个字节“5”代表组件的数量;第二个字节开始就是组件的类别的ID了。比如说,DM_BASE_LAYER_START,代表开始使用Layer;DM_SCR_BG表示背景图;DM_BASE_CONTROL_SET1表示窗体的基本组成——状态栏、标题和软按键;DM_MULTILINE_INPUTBOX1是多行输入框;DM_CATEGORY_CONTROLLED_AREA则是输入法的显示部分。它们被定义在枚举结构mmi_dm_control_ids_enum中。

在MTK环境的原始版本中,一共有99种组件。其后的都是用户自己扩展的。各组件的外观和功用是什么,基本是可以见词知意的,可以在具体使用过程中了解。

3. 各组件的属性定义

    仍以5号模板为例,观察组件属性数组。

cosnt S16 coordinate_set[] =

{

  DM_FULL_SCREEN_COORDINATE_FLAG,

  DM_CONTENT_COORDINATE_FLAG,

  DM_FULL_SCREEN_COORDINATE_FLAG

}

这两个常量都是代表组件属性的标志。定义在Wgui_draw_manager.h中大家可以观察到,它们都是负数:

#define DM_FULL_SCREEN_COORDINATE_FLAG  -10002

#define DM_CONTENT_COORDINATE_FLAG  -10004

现在就让我们一起分析一下它们在控件绘制中起到的作用,以及定义成负数的原因。通过比对我们可以发现,在dm_redraw_category_screen()函数流程的第6步的循环中,就是通过判断组件的ID来实现逐步绘制窗体的,如:

   case DM_MULTILINE_INPUTBOX1:

{

dm_setup_and_draw_multiline_inputbox(&UICtrlAccessPtr_p,&dm_cat_scr_info);

}                                                                     

   先来分析两个参数。联系上面的程序,我们可以知道,UICtrlAccessPtr_p是作为参数传入dm_get_cat_scr_coordinates()函数中的,又作为返回值,完成进入循环前的最后一次更改的,而此前,它指向的是组件属性数组。由于模板需要在其他组件被绘制前绘制窗体本身,因此它使用dm_get_cat_scr_coordinates()提前了解窗体的规格,而组件属性数组的第一个U16,就正是窗体的规格的标志ID:DM_FULL_SCREEN_COORDINATE_FLAG。根据该ID,dm_get_cat_scr_coordinates()做了分类判断,得到了指向结构体dm_cat_scr_info_struct的变量指针,其结构定义如下:

      typedef struct

      {

         S16 x1;

         S16 y1;

         S16 x2;

         S16 y2;

         S16 flags;

}dm_cat_scr_info_struct;

很明显,结构体成员分别是:起始、结束坐标,附加的标志。

在dm_get_cat_scr_coordinates()过后,指针UICtrlAccessPtr_p向后偏移了两个字节,指向了DM_CONTENT_COORDINATE_FLAG。在绘制DM_BASE_LAYER_START、DM_SCR_BG、DM_BASE_CONTROL_SET1时,都不需要类似于起始位置、大小这样的属性来支持,因此它们的绘制函数dm_setup_base_layer()、dm_setup_and_draw_scr_bg()、dm_setup_and_draw_base_control_set()没有访问组件属性数组。而多行输入控件的绘制函数dm_setup_and_draw_multiline_inputbox()、和输入法显示区域的绘制函数dm_setup_and_draw_category_controlled_area() 却不约而同的调用了

*UICtrlAccessPtr_p = dm_get_coordinates(*UICtrlAccessPtr_p, &dm_category_controlled_area_info)这一语句来获取组件属性。因此,DM_CONTENT_COORDINATE_FLAG是多行输入控件的属性标志,DM_FULL_SCREEN_COORDINATE_FLAG 是输入法显示区域的属性标志。

最后观察一下dm_get_coordinates()函数(参见下页的图)。在这个函数里,对属性标志进行了判断,并最终为dm_cat_scr_info_struct结构体变量进行了赋值:

   可以看到,在dm_get_coordinates()函数中,对UICtrlAccessPtr_p指向的内容,也就是某一个组件属性标志常量,进行了判断。而后,根据不同情况对dm_coordinate_info的各成员赋了值。

    因此,可以说,一个组件属性标志,就代表了一组包括组件坐标和标志在内的一组属性值。但是,要引起注意的是,这些值都是固定的。当组件的位置需要调整的时候,又该怎么办。继续观察dm_get_coordinates()的代码段。

在判断了所有组件属性标志后,出现了一个else判断:

else

{

  dm_coordinate_info->s16X = *UICtrlAccessPtr_p;

  UICtrlAccessPtr_p ++;

  dm_coordinate_info->s16Y = * UICtrlAccessPtr_p;

  UICtrlAccessPtr_p ++;

dm_coordinate_info->s16Width = *UICtrlAccessPtr_p;

  UICtrlAccessPtr_p ++;

dm_coordinate_info->s16Height = *UICtrlAccessPtr_p;

  UICtrlAccessPtr_p ++;

dm_coordinate_info->Flags = *UICtrlAccessPtr_p;

  UICtrlAccessPtr_p ++;

}

   也就是说,UICtrlAccessPtr_p指向的值,如果不是任何一个属性标志常量的话,它就被认为是起始X的值,然后指针将被后移2字节,下一个值被认为是起始Y的值,以此类推,直到这个dm_coordinate_info赋值完毕。这就是自定义控件位置的方法。

如果,要定义一组默认值,就可以先在Wgui_draw_manager.h中定义常量,然后在dm_get_coordinates()加入分支,当组件属性中包含这个常量时,就将dm_coordinate_info的各结构体成员赋值。

    这就是构造一个窗体模板的原理和方法。利用这些,我们就可以利用GDI接口和GUI组件,随意的画我们自己的界面了,而不用仅仅去调用单调的ShowCategoryXXScreen()了。

 一、什么是History管理

    对于我们上层用户而言,经常接触到的History管理是这样的:

    void EntryFunc()

{

U8 *guiBuffer;

            EntryNewScreen( Screen_ID , Exit_Func , Entry_Func , NULL );

    guiBuffer = GetCurrGuiBuffer( SCR_ID_WORDMAIN_LIST );

              ShowCategroyXXScreen( Title_ID , … , guiBuffer);

    }

    但是,无论是EntryNewScreen的调用,还是guiBuffer的传入,我们都很少考虑过对这些指针和函数在GUI的管理起到了什么样的作用。下面我们就要了解,以上的代码与History管理之间存在的关系。

    在MTK环境中,每当我们进入一个窗口,系统将先提取前一个窗口需保留的数据。这些数据包括:

1.    窗口ID ;

2.    进入窗口时调用的函数和退出调用的函数 -- Exit_Func 和 Entry_Func ;

3.    组成窗体的控件的属性(如,列表控件当前高亮显示的条目、当前屏的首末条目等)。

举例说明这些数据在实际中是如何被使用的。

假设存在AB两个窗口,A窗口需要保留的数据为data_A。我们先从A窗口进入到B窗口。data_A将在B窗口调用EntryNewScreen()的时候,被压入一个结构类似于栈的数据存储区域;当从B调用GoBackHistory()返回A时,data_A从栈顶被弹出,然后A利用data_A将自身还原到其进入B之前的状态。

这就是History管理的作用。简言之,就是要保持窗口的外观状态。

 

 

二、History管理的机制

    现在,我们来了解一下前面所说的data_A的数据结构是什么样的。

typedef struct _history

{

    U16 scrnID;                    //(1)Screen ID (窗口号)

FuncPtr entryFuncPtr;         //(2)EntryNewScreen时要进入的 Entry_Func

    U8 inputBuffer[MAX_INPUT_BUFFER];

                                   //(3)没遇到过其使用,都是NULL。

    U8 guiBuffer[MAX_GUI_BUFFER];

                               //(4)窗体中控件的一些需保存的信息的Buffer,通常在使用时被转化成各控件自定义的结构体如: list_menu_category_history。

} history;            

而存放data_A的类似于堆栈的数据区则以全局变量的形式定义在系统中:

historyNode  historyData[MAX_HISTORY]; (MAX_HISTORY = 50):

设当前窗口A所对应的数据是historyData[ EntryScreenNum – 1 ] ,那么它是何时、是如何被赋值的?又是何时、如何被使用的?

经过跟踪调试,我们已经知道,在由窗口A进入到窗口B(调用EntryNewScreen)的时候,我们将data_A记录到了historyNode 的结构体变量中。但是,在EntryNewScreen的时候传入的,却是data_B,data_A是如何被记录和使用的呢?

我们摘选EntryNewScreen的子函数中所包含的较核心的代码来说明这个问题。这三段代码是按照现在的排放顺序来执行的。

第一段(history h 可理解为data_A):

    h.scrnID = scrnID;                      // scrnID  =  currExitScrnID

    h.entryFuncPtr = entryFuncPtr;         // entryFuncPtr =  currEntryFuncPtr

    pfnUnicodeStrcpy((S8*) h.inputBuffer, (S8*) & nHistory);

                                          // nHistory = NULL ;

    GetCategoryHistory(h.guiBuffer);

//GetCategoryHistory是指向获取guiBuffer的函数的指针

    AddHistory(h);                        //数据入栈

第二段:

if(currExitFuncPtr)

    {

        //…

        (*currExitFuncPtr) ();             //执行Exit_Func

    }

第三段(记录Screen_ID,Exit_Func和EntryFunc):

currExitScrnID = scrnID;

    currExitFuncPtr = exitFuncPtr;

    currEntryFuncPtr = entryFuncPtr;

这样,我们就可以看出,EntryNewScreen函数先将上次执行EntryNewScreen时所记录的currExitScrnID, currEntryFuncPtr以history结构为载体记录入栈;然后执行了记录中的currExitFuncPtr;最后将本窗口的scrnID、exitFuncPtr、entryFuncPtr分别记录入全局变量currExitScrnID、currExitFuncPtr和currEntryFuncPtr,留待下次调用EntryNewScreen时使用。

下面有数据出入栈流程,有兴趣的话可以跟踪一下。以先后顺序代表包含关系,如下:

1.入栈(EntryNewScreen):

(1)U8 EntryNewScreen(U16 newscrnID, FuncPtr newExitHandler, FuncPtr newEntryHandler, void *peerBuf)

(2)static void ExecuteCurrExitHandler(void);

(3)void ExecuteCurrExitHandler_Ext(void);

(4)void GenericExitScreen( U16 scrnID , FuncPtr entryFuncPtr );

(5)void AddHistoryReference(history *addHistory);   //处理historyData

(6)S16 increment();                                 //更改栈指针

2.出栈(GoBackHistory):

(1)void GoBackHistory(void);

(2)static void ExecutePopHistory(void);             //处理historyData

(3)static U8 decrement(void);                       //更改栈指针

现在我们已经知道了history 的三个结构体成员是如何记录的了,最后来重点看一下history.guiBuffer是如何被记录和使用的。

 

 

三、GUI Buffer对控件属性的记录

    由上2节我们知道,guiBuffer是窗体中某些控件的需保存的属性的Buffer,通常在使用时被转化成各控件自定义的结构体。如: list_menu_category_history。

现在有几个问题需要我们解答:

1.guiBuffer 指向的Buffer是如何被分配的?该块数据是动态的还是静态的?

2.这块 Buffer 是何时被写入数据的?

3.如何释放(动态分配时)或清空(固定地址时)该块 Buffer ?

让我们逐一解答上面的三个问题,以清晰我们对guiBuffer的认识。

1.答:在void AddHistoryReference(history *addHistory)中,调用OslMalloc(MAX_GUI_BUFFER)动态申请了一块内存,用来保存在 GenericExitScreen 中获取的history.guiBuffer。[参见出入栈流程]

2.如何释放(动态分配时)或清空(固定地址时)该块 Buffer ?

答 :在static void decrement (void)函数中,该buffer被释放: OslMfree(historyData[currHistoryIndex].guiBuffer);。[参见出入栈流程]

3.答 : 只要一个窗体模板有需要保存状态的控件,它们都调用了这个函数——dm_setup_category_functions()。函数定义如下:

void dm_setup_category_functions(

        FuncPtr redraw_function,

        U8 *(*get_history_function) (U8 *buffer),

        S32(*get_history_size_function) (void)

)

{

       //指向窗体重画函数的函数指针

    RedrawCategoryFunction = redraw_function;

       //指向获取窗体guiBuffer的函数指针

    GetCategoryHistory = get_history_function;

       //指向获取窗体guiBuffer大小的函数指针

    GetCategoryHistorySize = get_history_size_function;

}

在只有一个控件的状态需要保存的窗体中,会这样传参给这个函数:

dm_setup_category_functions(dm_redraw_category_screen, dm_get_category_history, dm_get_category_history_size);

GenericExitScreen()函数中,将使用 GetCategoryHistory()获取某个控件的GuiBuffer[参见出入栈流程]。如果按照上面的设置,GetCategoryHistory指向了

dm_get_category_history这个函数。看看这个函数做了什么:

control_set_ptr = dm_search_control_set((U16) p_dm_data->s32CatId, &coordinate_set_p);              //获取窗体模板内的控件类型数组control_set_ptr

u8NoOfUICtrls = control_set_ptr[0];      //获取数组内变量个数,即控件的个数

/*根据控件类型,获取控件的guiBuffer.值得注意的是,1.这里的histroy_Buffer的名称起的不好,应该起名为guiBuffer,不应混淆视听;2.最终history_buffer 将指向模板中定义的最后一个控件的guiBuffer*/                   

for (u8CtrlCt = 1; u8CtrlCt <= u8NoOfUICtrls; u8CtrlCt++)

{

 switch (control_set_ptr[u8CtrlCt])

{

    case DM_CIRCULAR_MENU1:

   {

 get_circular_menu_category_history((U16) p_dm_data->s32CatId, history_buffer);

       break;

    }

    case DM_LIST1:

    {

  get_list_menu_category_history((U16) p_dm_data->s32CatId, history_buffer)

        break;

    }

    case DM_DYNAMIC_LIST1:

    {

   get_list_menu_category_history((U16) p_dm_data->s32CatId, history_buffer);

         break;

     }

     case DM_ASYNCDYNAMIC_LIST1:

     {

   get_list_menu_category_history((U16) p_dm_data->s32CatId, history_buffer);

         break;

      }

      //...

 }

 //...

}

而在模版显示函数(ShowCategroyXXScreen)中,则根据 guibuffer 的情况设置控件的属性。如果 guibuffer 不为空,则说明该模板的显示函数是在GoBackHistory()的时候被调用的,而不是进入新窗口时被调用的。那么控件必然有一些保留的属性需要被还原。以6号窗口的List为例。在ShowCategory6Screen()中,调用下面的函数来恢复List设置:

h_flag = set_list_menu_category_history(MMI_CATEGORY6_ID, history_buffer);

这样guiBuffer的Get和Set就统一起来了。

现在,我们已经知道了guiBuffer 所起到的作用。但是,如果一个窗体模板内有两个或两个以上需要记录状态的控件,又该怎么办呢?

 在某些频繁更新的界面中,如果某些显示元素一直没有变化,我们就可以将这些元素提取出来画到一个模拟的屏幕中,而将一些需要更新的元素画到另外的模拟屏幕,而后将两个模拟屏幕合并到真正的屏幕上,这样我们就节省了不变元素的重画时间,从而减轻了系统负担及加速画面更新。我们把这样的模拟屏幕就叫层,也可以说层就是屏幕的缓冲空间。

    例如,如果我们用动画做为背景,将其他的一些元素也画到这一层中,就会出现当动画跳到第二帧后,动画上面的文本及图象都会被盖住。而有了层以后,我们就可以将不变的文本及图象放到新建的一个层中,将动画放到背景层中,每当有动画换帧时,只需将新帧画到背景层中,然后合并两个屏幕(动画刷新时会自动合并),这样上层的文本等就不会被盖住了。
    另外,因为层的格式简单且统一,并且一般的图形系统都会用硬件加速合并,所以在层合并时加上些特效会很方便,如通透、半透明、剪切等。
    下面将以mmi_phoart_entry_main_screen()为例说明关于层的一些内容。
1.首先,因为用到两个层,所以我们要开启多层
gdi_layer_multi_layer_enable();
等到退出该屏幕的时候需要gdi_layer_multi_layer_disable();
2.创建层:创建层需要先建一个层句柄,我们可以通过该句炳来控制层,函数gdi_layer_create用来创建层
gdi_layer_create(OFFSET_X,OFFSET_Y,WIDTH,HEIGHT,HANDLE_PTR);
OFFSET_X,OFFSET_Y是层的位置坐标,WIDTH,HEIGHT是层的大小;HANDLE_PTR是层句炳,用于返回所创建的层。不过,因为创建层时系统要为其分配动态内存空间,而系统保留的内存一般只够创建一个UI_device_width*UI_device_height大小的层,所以如果已有两个层,而需要再创建新层时,就需要用到函数gdi_layer_create_using_outside_memory(X,Y,WIDTH,HEIGHT,HANDLE_PTR,OUTMEM_PTR,OUTMEM_SIZE)
前5个参数和gdi_layer_create的参数相同,OUTMEM_PTR是存放层的BUFFER,OUTMEM_SIZE是层的大小。
3.激活层:在我们的图形系统中,任何时刻有且只能有一个层处于激活状态,所有的绘画都会默认画到这个激活层中,所以想要在层上绘画必须先将其激活。激活函数是gdi_layer_set_active(gdi_handle handle);不过,由于在多层的处理中需要在各个层之间切换激活,所以我们经常用到的是gdi_layer_push_and_set_active(gdi_handle handle),此函数会把当前的激活层入栈而激活参数层,等到下次需要激活栈中的层时,只需要用函数gdi_layer_pop_and_restore_active()激活就可以了。
4.基础层:系统开机的时候会为每个硬件屏幕创建一个基础层,基础层有以下几个特点:
(1)基础层由系统创建,无法删除。
(2)与硬件屏幕完全重合。
(3)系统默认的激活层,EntryNewScreen时系统会紫铜将基础层激活。
(4)显示更加快速,基础层存储于芯片内的flash中,所以在其上面绘画极快,一般我们会将刷新频繁的内容放在基础层上。
     基于以上几点,通常在不使用多层的情况下,我们完全可以将基础层当成硬件屏幕来看待,也就是说普通程序完全可以忽略层的概念。另外,因为系统一般只在EntryNewScreen时才会自动将基础层激活,为避免特殊情况下使用层混乱,通常在新层上绘画完毕后我们会主动将基础层还原为激活状态。
    其实,对于我们上面说到的层之间的切换激活而言,大多说情况下是基础层和新层之间的切换。为此需要用到gdi_layer_get_active(gdi_handle *handle_ptr)得到当前激活层句柄(多数情况下是基础层句柄),这样我们就可以切换激活层了。
5.合并:有了多个层,当然要合并到一起了。合并层的函数是gdi_layer_blt_previous(S32 x1, S32 y1, S32 x2, S32 y2),gui_BLT_double_buffer也具有同样的效果。另外,在合并前,我们还需要用函数gdi_layer_set_blt_layer(H1,H2,H3,H4) 来指明需要合并的层。
6。释放:由于空间的问题,我们创建的新层在用完后一定要释放,释放函数是:
gdi_layer_free(gdi_handle handle)。注意:层一般是在退出函数中释放。

    说到这儿,我们就可以建立一个多层的屏幕了。下面我们在看看层的一些特效:
1.剪切:所谓剪切,就是在层中设一个限制区域,只有在这个区域中的绘画才是有效的,否则就会被自动忽略。剪切有两个显著的特点,一是每个层一定而且只能有一个剪切区域。二是剪切区域一经设置将永远生效。所以剪切区域用完后最好用gdi_layer_reset_clip()还原。剪切用函数gdi_layer_set_clip()来实现。
2.通透:所谓通透,就是如果我们将某种颜色设为通透色,在层合并的时候,系统会自动将层中与通透色相同的颜色忽略掉,这样我们在这一点上看到的是其下层的颜色。设置通透的函数是gdi_layer_set_source_key。另外在设置通透前我们通常用gdi_layer_clear_background将该层刷上某种颜色。
3.透明:gdi_layer_set_opacity(BOOL opacity_enable, U8 opacity_value)
opacity_enable指通明是否开启,opacity_value指透明度,范围是0至255,值越小越透明。
4.锁屏:由于某些元素要频繁的刷新,而如果我们每次都合并就会很浪费时间,因此我们可以设置两个计数器,一个用来加,一个用来减,当计数器为0时,我们再合并。这两个计数器就是 gdi_layer_lock_frame_buffer()和 gdi_layer_unlock_frame_buffer();

请参考以下进入一个新屏的函数:

static void mmi_phoart_entry_main_screen (void)
{
S8 buf_filename[FMGR_PATH_BUFFER_SIZE];
S32 image_width;
S32 image_height;
PU8 buf_ptr;
U8 *gui_buffer;
GDI_RESULT result;
S32 offset_x;
S32 offset_y;
U16 img_type;
UI_string_type title_string;
S32 str_width;
S32 str_height;
EntryNewScreen (SCR_ID_PHOART_MAIN,mmi_phoart_exit_main_screen,mmi_phoart_entry_main_screen, NULL);
gdi_layer_reset_clip();//设置剪切区域
gdi_layer_reset_text_clip();//设置剪切区域
gui_buffer = GetCurrGuiBuffer(SCR_ID_PHOART_MAIN);
entry_full_screen();
gdi_layer_multi_layer_enable();//开启多层
gdi_layer_get_active(&g_phoart_cntx.base_layer_handle);//得到当前活动层
gdi_layer_get_source_key(&g_phoart_cntx.was_source_key_enable, &g_phoart_cntx.old_source_key);//得到当前活动层的通透属性
gdi_layer_set_source_key(FALSE, g_phoart_cntx.old_source_key);//设置通透
gdi_layer_create(0, 0, UI_device_width, UI_device_height, &g_phoart_cntx.osd_layer_handle);//创建新层
gdi_layer_push_and_set_active(g_phoart_cntx.osd_layer_handle);//激活新层
gdi_layer_clear_background(GDI_COLOR_TRANSPARENT);//刷色
gdi_layer_set_source_key(TRUE, GDI_COLOR_TRANSPARENT);//设置通透
//base layer
gdi_layer_pop_and_restore_active();//激活栈中的层
//gdi_layer_reset_clip();
//gdi_layer_reset_text_clip();
gdi_layer_clear_background(GDI_COLOR_RED);
gdi_layer_set_source_key(TRUE, GDI_COLOR_RED);
gdi_image_stop_animation_all();
gdi_image_draw_animation_id(50, 100, IMG_ID_PHOART_ICON_6, NULL);
//new layer
gdi_layer_push_and_set_active(g_phoart_cntx.osd_layer_handle);
// gdi_layer_reset_clip();
//gdi_layer_reset_text_clip();
gdi_layer_set_clip(0,0,50,50);
gdi_layer_clear_background(GDI_COLOR_TRANSPARENT);
gdi_layer_set_source_key(TRUE, GDI_COLOR_TRANSPARENT);
gui_set_font(&MMI_medium_font);
gui_set_text_color(g_phoart_cntx.text_color);
gui_set_text_border_color(g_phoart_cntx.text_border_color);
title_string = (UI_string_type) get_string(STR_ID_PHOART_APP);
gui_measure_string(title_string, &str_width, &str_height);
gdi_layer_set_clip((UI_device_width - str_width) >> 1,(MMI_title_height - str_height) >> 1,200,100);//设置剪切区域
gui_move_text_cursor((UI_device_width - str_width) >> 1, (MMI_title_height - str_height) >> 1);
gui_print_bordered_text(title_string);
mmi_phoart_draw_softkey((PS8) get_string(STR_GLOBAL_OPTIONS), (PS8) get_string(STR_GLOBAL_BACK), FALSE);
gdi_layer_pop_and_restore_active();
gdi_layer_set_blt_layer(g_phoart_cntx.base_layer_handle, g_phoart_cntx.osd_layer_handle, 0, 0);//指明需要合并的层
gdi_layer_blt_previous(0, 0, UI_device_width - 1, UI_device_height - 1);//合并层


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zlfsjx/archive/2010/01/07/5145511.aspx

原创粉丝点击