GuiLite开源GUI学习(一):display与surface类


GuiLite项目地址

移植相关文件

  • UICode.cpp:逻辑代码的编写,向外提供一个接口,如startHello3Ddonut

    static c_surface* s_surface;
    static c_display* s_display;
    // Demo
    void create_ui(void* phy_fb, int screen_width, int screen_height, int color_bytes, struct EXTERNAL_GFX_OP* gfx_op) {
    	if (phy_fb)
    	{
    		static c_surface surface(UI_WIDTH, UI_HEIGHT, color_bytes, Z_ORDER_LEVEL_0);
    		static c_display display(phy_fb, screen_width, screen_height, &surface);
    		s_surface = &surface;
    		s_display = &display;
    	}
    	else
    	{//for MCU without framebuffer
    		static c_surface_no_fb surface_no_fb(UI_WIDTH, UI_HEIGHT, color_bytes, gfx_op, Z_ORDER_LEVEL_0);
    		static c_display display(phy_fb, screen_width, screen_height, &surface_no_fb);
    		s_surface = &surface_no_fb;
    		s_display = &display;
    	}
    	s_surface->fill_rect(0, 0, UI_WIDTH - 1, UI_HEIGHT - 1, 0, Z_ORDER_LEVEL_0);
    	// 向主题添加一个字体,改字体的标识为FONT_DEFAULT,后面可以根据这个标识取出。
        c_theme::add_font(FONT_DEFAULT, &Consolas_13);
    
    	while(true)
    	{
    		build_frame();
    		render_frame();
    	}
    }
    
     interface for all platform 
    // main.c中调用此接口
    extern "C" void startHello3Ddonut(void* phy_fb, int width, int height, int color_bytes, struct EXTERNAL_GFX_OP* gfx_op) {
    	create_ui(phy_fb, width, height, color_bytes, gfx_op);
    }
    
  • main.c:负责画点函数draw_pixel的移植

    //Transfer GuiLite 32 bits color to your LCD color
    #define GL_RGB_32_to_16(rgb) (((((unsigned int)(rgb)) & 0xFF) >> 3) | ((((unsigned int)(rgb)) & 0xFC00) >> 5) | ((((unsigned int)(rgb)) & 0xF80000) >> 8))
    //Encapsulate your LCD driver:
    void gfx_draw_pixel(int x, int y, unsigned int rgb)
    {
        LCD_Draw_ColorPoint(x, y, GL_RGB_32_to_16(rgb));
    }
    //Implement it, if you have more fast solution than drawing pixels one by one.
    void gfx_fill_rect(int x0, int y0, int x1, int y1, unsigned int rgb)
    {
        LCD_Fill(x0, y0, x1, y1, GL_RGB_32_to_16(rgb));
    }
    
    //UI entry
    struct EXTERNAL_GFX_OP
    {
        void (*draw_pixel)(int x, int y, unsigned int rgb);
        void (*fill_rect)(int x0, int y0, int x1, int y1, unsigned int rgb);
    } my_gfx_op;
    extern void startHello3Ddonut(void* phy_fb, int width, int height, int color_bytes, struct EXTERNAL_GFX_OP* gfx_op);
    
    int mian()
    {
        ......
        // 画点函数的移植
        my_gfx_op.draw_pixel = gfx_draw_pixel;
        my_gfx_op.fill_rect = gfx_fill_rect;//gfx_fill_rect;
        startHello3Ddonut(NULL, 240, 240, 2, &my_gfx_op);
    }
    

注意点:

static c_surface_no_fb surface_no_fb(UI_WIDTH, UI_HEIGHT, color_bytes, gfx_op, Z_ORDER_LEVEL_0);
// 虽然画图时都是通过surface,但是下面这句话不能注释掉,因为下面这句话会对surface_no_fb进行赋值,不然的话surface_no_fb是无效值。
static c_display display(phy_fb, screen_width, screen_height, &surface_no_fb);
// 因为:
inline c_display::c_display(..., c_surface* surface)
{
	m_color_bytes = surface->m_color_bytes;
    // 这句话设置surface为有效的
	surface->m_is_active = true;
	(m_surface_group[0] = surface)->attach_display(this);
}

s_surface = &surface_no_fb;
// 这句话可以注释掉
s_display = &display;

注意
使用时只需包含头文件GuiLite.h,此头文件中包含一些已实现的控件。在GuiLite项目中,作者也将各个模块抽离出来,方便读者研究,即GuiLite的src目录下。

display类

surface是它的友元类,即surface可以访问display的属性和方法。

属性:

void*			m_phy_fb;		//physical framebuffer
int				m_phy_read_index;
int				m_phy_write_index;
c_surface*		m_surface_group[SURFACE_CNT_MAX];	///对应多个surface
int				m_surface_cnt;	//surface count
int				m_surface_index;

注意点:

  1. 一个display只有一个m_phy_fb
  2. 但是有SURFACE_CNT_MAX个surface;

方法:

inline c_display(void* phy_fb, int display_width, int display_height, int surface_width, int surface_height, unsigned int color_bytes, int surface_cnt, EXTERNAL_GFX_OP* gfx_op = 0);// 创建拥有surface_cnt个surface的display
inline c_display(void* phy_fb, int display_width, int display_height, c_surface* surface);// 创建单层surface的display,且为fb模式
inline c_surface* alloc_surface(Z_ORDER_LEVEL max_zorder, c_rect layer_rect = c_rect());//为display申请多个surface

inline int swipe_surface(c_surface* s0, c_surface* s1, int x0, int x1, int y0, int y1, int offset);
// 得到最新的fb
void* get_updated_fb(int* width, int* height, bool force_update = false);
// 将当前的fb保存到文件(bmp形式)
int snap_shot(const char* file_name)

surface类

display和bitmap都是surface的友元类。

方法:

// 构造函数只有一个
void c_surface(, Z_ORDER_LEVEL max_zorder = Z_ORDER_LEVEL_0, c_rect overlpa_rect = c_rect()) : m_width(width), m_height(height), m_color_bytes(color_bytes), m_fb(0), m_is_active(false), m_top_zorder(Z_ORDER_LEVEL_0), m_phy_fb(0), m_phy_write_index(0), m_display(0);

// 此函数是将m_fb的值刷新到m_phy_fb。
void flush_screen(x1, y1, x2, y2);

surface分为带有framebuffer和不带framebuffersurface,两者的区别不大,只是不带fb的多了一个struct EXTERNAL_GFX_OP* m_gfx_op属性,主要原因是:

  1. 带有framebuffer的可以直接对某块内存进行对写;
  2. 不带有framebuffer的surface通过m_gfx_op中的draw_pixel来重写virtual void draw_pixel_on_fb(int x, int y, unsigned int rgb)
  3. 通过上述可知,最终两种surface在使用上没有区别

属性:

void*			m_fb;			//frame buffer you could see
c_layer 		m_layers[Z_ORDER_LEVEL_MAX];//all graphic layers
bool			m_is_active;	//active flag
Z_ORDER_LEVEL	m_max_zorder;	//surface拥有的最多layer个数
Z_ORDER_LEVEL	m_top_zorder;	//指向你当前想显示的layer
void*			m_phy_fb;		//physical framebufer
int*			m_phy_write_index;
c_display*		m_display;
// 没有fb的还要多个struct EXTERNAL_GFX_OP* m_gfx_op成员

注意点:

  1. 一个surface指向一个display,也指向一个m_fbm_phy_fb
  2. 有多个layer;
  3. 即一个display指向多个surface、一个surface有多个layer;

方法:

// 有很多不同的画图方法,调用的都是
virtual void draw_pixel(int x, int y, unsigned int rgb, unsigned int z_order);
// 但最终是调用的是(没有fb的会被重写,通过m_gfx_op属性):
draw_pixel_on_fb(x, y, rgb);

画图时z_order的意义

draw_pixel(x, y, rgb, z_order)的意义(非常重要!!!):

一个surface有多层layer,有两个属性z_max_order(surface最多拥有的layer个数)和z_top_order(当前surface的最顶层)。而其作用就是:

  1. 判断点(x,y)是否在m_layers[z_order].rect中,如果在,那么就将m_layers[z_order].fb更新
  2. 然后判断z_order是否在最大层z_max_order,如果在,则将其立刻显示到屏幕上去;
  3. 如果z_order>m_top_order,则m_top_order=z_order,即更新最顶层
  4. 如果z_order=m_top_order,也立即将其显示到屏幕上去;
  5. 走到这里,说明z_order<m_top_order,那么从最大层tmp=z_max_order-1开始,从上到下遍历每一层,如果遍历到z_order,点(x,y)都没在m_layers[tmp].rect中,那么说明z_order的上层都没有把点(x,y)遮挡住,所以应该把点(x,y)立即显示到屏幕上去;否则,不立刻显示到屏幕上去,即只做了第一步;
  6. 如果非要显示,那么需要调用show_layer(c_rect, z_order)但是注意,c_rect必须在m_layers[z_order].rect之中。

大概如下,但如下没有考虑点(x,y)layer.rect的关系,只是考虑了图层的关系。

显示情况
方法(c_surface)
属性
指向
参数
没有fb
要显示
==
==
z_order>top
else
立即显示
</br>需调用show_layer(,zorder)
只是对m_layer</br>[z_order]赋值
draw_pixel(,z_order)
draw_line
draw_rect
fill_rect
draw_pixel_on_fb(,z_order)
z_order
gfx.draw_pixe(x,y,rgb)
m_layers[m_top_zorder]
m_max_zorder
m_top_zorder</br>(value=0:max-1)
top=z_order

surface和display及c_layer的关系

  1. 一个display指向多个surface,指向一个fb;
  2. 每个surface都指向一个fb,也指向一个display;
  3. 每个surface有多个layer,用z_order标识;
  4. 一个layer其实就是一个c_rect,然后还指向了一个fb,注意这个fb不是物理的,而是一个内存,即每个layer的fb都不同;
  5. surface的show_layer(c_rect& rect, z_order)就是展示第z_order个layer,但是注意,c_rect必须在m_layers[z_order].rect之中;&C++中的引用,用作函数参数时,效果同指针。它会从fb中读取rgb值,然后再调用draw_pixel_on_fb(x,y,rgb)

疑问

  1. 在创建display时创建了多个surface,怎么获取想要的surface?此问终于解了

    在阅读slide_group.hHelloSlide.h源码时,终于发现怎么获取想要的surfacedisplay的部分属性如下:

    c_surface*		m_surface_group[SURFACE_CNT_MAX];
    int				m_surface_cnt;	//在创建时传入
    int				m_surface_index;
    c_surface* alloc_surface(Z_ORDER_LEVEL max_zorder, c_rect layer_rect);
    

    m_surface_index用于索引对应的surface,我发现其只在alloc_surface中会被改变,然后又看了slide_group.h源码:

    int add_slide(c_wnd* slide, unsigned short resource_id, short x, short y, short width, short height, WND_TREE* p_child_tree = 0, Z_ORDER_LEVEL max_zorder =  Z_ORDER_LEVEL_0)
    {
        c_surface* old_surface = get_surface();
        c_surface* new_surface = old_surface->get_display()->alloc_surface(max_zorder);
        new_surface->set_active(false);
        set_surface(new_surface);
        slide->connect(this, resource_id, 0, x, y, width, height, p_child_tree);
        set_surface(old_surface);
    }
    

    先通过第一个surface获取display,然后再调用alloc_surface(),这个会返回新的surface

  2. 画图时,都是通过surface的,那么display有什么用?

未完待续…

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值