标签:
[2014年写一个UI库时写的几个文章,发布出来]
几年前的一个嵌入式的UI开发,使自己有机会接触到了UI的一些底层知识,虽然之前也开发过很多Windows下的信息应用系统,也做很多的界面开发,但一直却对UI的一些运作却不了解。
BOSS决定使用UCGUI做为UI的基本库来开发UI界面的一些应用。用UCGUI的库来做开发,它已经有很完善的基本构件,像窗体的管理,基本的控件,图片,文字处理等,不过听说UCGUI的授权费用也是不菲的,但我认为它确实是一个物有所值的东西。
在使用中也还是遇到过一些问题,但这些基本都不会有什么大的影响,主要的是有源代码,有一些小的BUG,也可以自己处理,满足不了的控件可以自己开发,也可以很方便的在他的控件基础上做一些扩展。
使用它避免不了要去读了解它的一些底层代码,这样自己有机会对UI的动作有了一些基本的了解。最近也做些UI的事,想起UCGUI的代码架构还是值得学习的,所以自己从UI的基本原理上再次学习了一次,自己也花时间动手做了一些基本的代码实现,很多基本原理都是从UCGUI上学习来的,再把这些基本的东西记下来,方便后来的学习者。
如何自己动手写一个UI?在我自己没接触底层UI前,一直没想过,也觉得它是一件比较复杂和难的事情。它有多难?现在我觉得如果会一门编程语言就可以写一个UI出来,这样的基础就够了,我想可以试一试!
从画一个点开始
“像素”这个词我们一定已经很熟悉了,现在买手机时大家很观注的一个参数就是摄像头的成像像素是多少,因为图像的质量很大成度就是它决定的,一个像素代表一个点,同样在显示器上也是一样,我们说的分辨率就是长宽多少个像素,在一个屏幕上显示有文字,有窗体,有图片及各种形状等等!看起来这是一个挺复杂的东西,但它却是由一个一个的点构成的,一个点代表一个像素,而这个点不同的像素值就代表不同的颜色,一个像素可以用8位,16位,32位来表示,位数越多,表示它能表示的颜色就越多显示的色彩也就越丰富了。
一个屏幕像是这样的:
我们以左上角为原点,做一个坐标系,分别有X,Y方向。像我现在的电脑的分辨率是1440x900,这样 X 最大取值是1440,Y最大取值是900。
如果我们要在一个屏幕上画一个点,给定一个点如:(500,500),只要在坐标系里找到相应的位置就可以写一个像素值进去,屏幕就会显示出一个颜色点。
显示器有一个显存,要在屏幕上画出点,也就需要在相应的显存位置写入颜色值,显存又会映射到内存的一块连续的区域,这样我们只要把值写入内存的区域,系统又会作I/O读写把更新显存的值,所心我们只要关心写的内存的区域就好了。
在Linux里有一个叫framebuffer的概念,叫“帧缓存”,其实就是对显存当前值的缓存,Linux系统的显卡驱动都有实现,我们对framebuffer的读写就是对显存的读写。
在Linux里/dev目录里,应都有一个类似fbx(x表示一个数字)设备文件,打开它,再用mmap函数把framebuffer内存映射进我们的进程里就可认方便的对显示器操作了,这样我们可以画点,画线显示在显示器上面。
如下面的代码操作,打开fb0,并读取设置相应的参数:
static struct fb_var_screeninfo stVarInfo;
static struct fb_fix_screeninfo stfixInfo;
static unsigned char *pFrameBuffer = NULL;
char *file_name = "/dev/fb0";
int fbDev, s32Ret;
fbDev = open(file_name,O_RDWR,0);
if(fbDev < 0)
{
printf("open framebuff failed!\n");
return;
}
stVarInfo.bits_per_pixel = 16;
stVarInfo.activate = FB_ACTIVATE_NOW;
stVarInfo.xres = stVarInfo.xres_virtual = 1440;
stVarInfo.yres = stVarInfo.yres_virtual = 900;
s32Ret = ioctl(fbDev, FBIOPUT_VSCREENINFO, &stVarInfo);
if(s32Ret < 0)
{
printf("PUT_VSCREENINFO failed!%x\n",s32Ret);
return ;
}
if (ioctl(fbDev, FBIOGET_FSCREENINFO, &stfixInfo) < 0)
{
printf("Get fix screen info failed!\n");
return ;
}
pFrameBuffer = mmap(HI_NULL, stfixInfo.smem_len, PROT_READ|PROT_WRITE, MAP_SHARED, fbDev, 0);
memset(pFrameBuffer,0x00,stfixInfo.smem_len);
在上面的代码中打开一个framebuffer,设置读取显示器的信息其中两个比较重要的结构:fb_var_screeninfo和fb_fix_screeninfo ,具体的可以搜索了解一下。
在mmap时,stfixInfo.smem_len就是显存在内存区域的大小, memset的操作效果就是我们把显示器设成全黑色了,因为我们写入的每一个点的像素值都是0x0。
显存的地址空间是一个线性的一维地址空间,我们的屏幕像上面的坐标系,是一个二维的了,给定一个点(x,y),这样我们就需要把它转换成相应的内存所在的地址。当知道一个屏幕的分辨率了,如1440 * 900,现在通过FBIOGET_FSCREENINFO知道了一些基本的信息,通过mmap知道了首地址。一个像素可以用8位,或16位,或32位,或更多的位表示,如我的板上的系统是16位,我用的LINUX系统是用32位的。以我板上的为例,用16位来表示一个像素点,也就是2个字节表示一个像素点。
那么屏幕上的点是这样确定的,想像你拿支笔从第一行画点,但把第一行画满后,再从第二行开始,持续的把整个屏画满,在屏上画点也是这样,假如是16位2个字节表示一个像素,分辨率是1400*900,所以给定的点(x,y)的地址为:
x * 2 + y * (1440 * 2),
如果用上一步代码里得到的信息来表示就是:
(x + stVarInfo.xoffset) * (stVarInfo.bits_per_pixel >> 3) + (y + stVarInfo.yoffset) * stfixInfo.line_length;
Xoffset,yoffset表示是否相对原点的偏移,bit_per_pixel表示一个像素用多少位表示,line_length表示一行占用多少字节。
画一个点(x,y)的地址确定了,这样写入一个值,相应的位置显示相应的颜色。那么对应的画点函数如下:
void UI_SetPointPixel(int x, int y, int pixelValue)
{
int location = 0;
location = (x + stVarInfo.xoffset) * (stVarInfo.bits_per_pixel >> 3) + (y + stVarInfo.yoffset) * stfixInfo.line_length;
*(short *)(pFrameBuffer + location) = (short)pixelValue;
}
应为是16位的,所以注意上面的转换 (short *).
同样有时操作需要得到一个点的颜色,根据上面画点的函数,可以如下写出获取点的函数:
unsigned int fb_GetPointPixel(int x, int y)
{
int location = 0;
location = (x + stVarInfo.xoffset) * (stVarInfo.bits_per_pixel >> 3) + (y + stVarInfo.yoffset) * stfixInfo.line_length;
int PixelIndex = *(short*)(pShowScreen + location);
return PixelIndex;
}
我们工作用的操作UI上,有窗体,图片,视频等等,很多东西,看起来很复杂,但这样的复杂北后,却是基本的像素点组成的,但再复杂的实现,都是在上面的画点函数,为基础的,为了方便我们开法,于是扩展了上面的画点函数,有画线段,矩形,圆等等,为了方便后面画更复杂的,我们实现画线和填充矩形函数:
void fb_DrawHLine(int x0, int y0, int x1)
{
U16 PixelColor = UI_GetDrawPixeColor();
for(; x0 <= x1; x0++)
{
UI_SetPointPixel(x0, y0, PixelColor);
}
}
void fb_DrawVLine(int x0, int y0, int y1)
{
U16 PixelColor = UI_GetDrawPixeColor();
for(; y0 <= y1; y0++)
{
UI_SetPointPixel(x0, y0, PixelColor);
}
}
void fb_DrawFillRect(int x0, int y0, int x1, int y1)
{
for(; y0<=y1; y0++)
{
UI_DrawHLine(x0, y0, x1);
}
}
分别实现画水平,垂直线,画填充的矩形,上面的 UI_GetDrawPixeColor 函数得到当前画点线的填充颜色值。
这样我们就有了基本的操作UI的工具了。我们现在用上面的函数就可以画点,线,矩形,还是很简单的。
2014-11-15
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/jhting/article/details/46738585