1.用到的结构体
/*确定一个图层的大小,用于对图标显示位置的解释*/
typedef struct Layout {
int iTopLeftX; //左上角xy坐标
int iTopLeftY;
int iBotRightX; //右下角xy坐标
int iBotRightY;
char *strIconName; //图标的名字,即用的哪张图片
}T_Layout, *PT_Layout;
/*用于对事先分配好的几块内存的状态标致*/
typedef enum {
VMS_FREE = 0, //没有用
VMS_USED_FOR_PREPARE, //子线程在用
VMS_USED_FOR_CUR, //当前线程在用
}E_VideoMemState;
/* VideoMem中内存里图片的状态,是整个内存空间的,而不是指这块空间内某一个图层的图片*/
typedef enum {
PS_BLANK = 0, //没有照片
PS_GENERATING, //正在生成
PS_GENERATED, //已经生成好了
}E_PicState;
/*标识事先分配好的内存空间*/
typedef struct VideoMem {
int iID; /* ID值,用于标识不同的页面 */
int bDevFrameBuffer; /* 1: 这个VideoMem是显示设备的显存; 0: 只是一个普通缓存 */
E_VideoMemState eVideoMemState; /* 这个VideoMem的状态,有没有在用,谁在用 */
E_PicState ePicState; /* VideoMem中内存里图片的状态,有没有生成好*/
T_PixelDatas tPixelDatas; /* 内存: 用来存储图像,这里不是指针,当分配一个T_VideoMem结构体时,这一块内存就已经分配了*/
struct VideoMem *ptNext; /* 链表 */
}T_VideoMem, *PT_VideoMem;
/*显示设备结构体,用来注册设备用*/
typedef struct DispOpr {
char *name; /* 显示模块的名字 */
int iXres; /* X分辨率 */
int iYres; /* Y分辨率 */
int iBpp; /* 一个象素用多少位来表示 */
int iLineWidth; /* 一行数据占据多少字节 */
unsigned char *pucDispMem; /* 显存地址 */
int (*DeviceInit)(void); /* 设备初始化函数 */
int (*ShowPixel)(int iPenX, int iPenY, unsigned int dwColor); /* 把指定座标的象素设为某颜色 */
int (*CleanScreen)(unsigned int dwBackColor); /* 清屏为某颜色 */
int (*ShowPage)(PT_VideoMem ptVideoMem); /* 显示一页,数据源自ptVideoMem */
struct DispOpr *ptNext; /* 链表 */
}T_DispOpr, *PT_DispOpr;
2.disp_manager.c
static PT_DispOpr g_ptDispOprHead; //显示设备的头
static PT_DispOpr g_ptDefaultDispOpr; //默认的显示设备
static PT_VideoMem g_ptVideoMemHead; //那几块缓存的头
int RegisterDispOpr(PT_DispOpr ptDispOpr); //注册一个设备
void ShowDispOpr(void); //显示一下都有哪些设备注册了
int DisplayInit(void); //调用需要注册设备的注册函数
void SelectAndInitDefaultDispDev(char *name); //通过名字选择默认设备,并调用其初始化函数和清屏
PT_DispOpr GetDefaultDispDev(void); //获得默认显示设备的结构体
int GetDispResolution(int *piXres, int *piYres, int *piBpp); //获得显示设备的分辨率信息
int AllocVideoMem(int iNum); //分配iNum块buffer,用来提前填满显示数据
PT_VideoMem GetDevVideoMem(void); //相当于获得设备的framebuffer
PT_VideoMem GetVideoMem(int iID, int bCur); //根据ID号获得一块内存,并把状态设置为bCur
void PutVideoMem(PT_VideoMem ptVideoMem); //使用GetVideoMem获得的VideoMem, 用完时用PutVideoMem释放掉
void ClearVideoMem(PT_VideoMem ptVideoMem, unsigned int dwColor);//把某块分配的内存清为某种颜色,与设备的CleanScreen函数不同,设备的CleanScreen只是清framebuffer
void ClearVideoMemRegion(PT_VideoMem ptVideoMem, PT_Layout ptLayout, unsigned int dwColor);
int FBInit(void);
重要函数具体解析
分配几块内存供 提前准备显示数据 使用
int AllocVideoMem(int iNum)
{
int i;
int iXres = 0;
int iYres = 0;
int iBpp = 0;
int iVMSize; //整块buffer的大小
int iLineBytes; //一行的大小
PT_VideoMem ptNew;
/* 获得显示设备的分辨率及bpp,求出一块完整的显示需要多少buffer */
GetDispResolution(&iXres, &iYres, &iBpp);
iVMSize = iXres * iYres * iBpp / 8;
iLineBytes = iXres * iBpp / 8;
/* 先把设备本身的framebuffer放入链表
* 分配一个T_VideoMem结构体, 注意我们没有分配里面的tPixelDatas.aucPixelDatas
* 而是让tPixelDatas.aucPixelDatas指向显示设备的framebuffer
*/
ptNew = malloc(sizeof(T_VideoMem)); /* 用一个 T_VideoMem 结构体来标识framebuffer */
if (ptNew == NULL)
{
return -1;
}
/* 指向framebuffer */
ptNew->tPixelDatas.aucPixelDatas = g_ptDefaultDispOpr->pucDispMem; //指针直接指向framebuffer
ptNew->iID = 0;
ptNew->bDevFrameBuffer = 1; /* 表示这个VideoMem是设备本身的framebuffer, 而不是用作缓存作用的VideoMem */
ptNew->eVideoMemState = VMS_FREE; // 标识目前这块内存是未使用的
ptNew->ePicState = PS_BLANK; // 这块内存是空的
ptNew->tPixelDatas.iWidth = iXres;
ptNew->tPixelDatas.iHeight = iYres;
ptNew->tPixelDatas.iBpp = iBpp;
ptNew->tPixelDatas.iLineBytes = iLineBytes;
ptNew->tPixelDatas.iTotalBytes = iVMSize;
if (iNum != 0) //就是说要分配其他的内存,不仅仅使用framebuffer
{
/* 如果下面要分配用于缓存的VideoMem,
* 把设备本身framebuffer对应的VideoMem状态设置为VMS_USED_FOR_CUR,
* 表示这个VideoMem不会被作为缓存分配出去
*/
ptNew->eVideoMemState = VMS_USED_FOR_CUR;
}
/* 放入链表 */
ptNew->ptNext = g_ptVideoMemHead; //这种方法是单向链表的头插法
g_ptVideoMemHead = ptNew;
/*
* 分配用于缓存的VideoMem
*/
for (i = 0; i < iNum; i++)
{
/* 分配T_VideoMem结构体本身和"跟framebuffer同样大小的缓存" */
ptNew = malloc(sizeof(T_VideoMem) + iVMSize); //除了要分配一个结构体的空间,还需要分配一个buffer的空间,这样分配使这两块内存连在一起
if (ptNew == NULL)
{
return -1;
}
/* 在T_VideoMem结构体里记录上面分配的"跟framebuffer同样大小的缓存" */
ptNew->tPixelDatas.aucPixelDatas = (unsigned char *)(ptNew + 1); //指向空着的这块内存
ptNew->iID = 0;
ptNew->bDevFrameBuffer = 0; //不是frambuffer
ptNew->eVideoMemState = VMS_FREE;
ptNew->ePicState = PS_BLANK;
ptNew->tPixelDatas.iWidth = iXres;
ptNew->tPixelDatas.iHeight = iYres;
ptNew->tPixelDatas.iBpp = iBpp;
ptNew->tPixelDatas.iLineBytes = iLineBytes;
ptNew->tPixelDatas.iTotalBytes = iVMSize;
/* 放入链表 */
ptNew->ptNext = g_ptVideoMemHead;
g_ptVideoMemHead = ptNew;
}
return 0;
}
根据ID号获取一块内存,并更改状态bCur
PT_VideoMem GetVideoMem(int iID, int bCur)
{
PT_VideoMem ptTmp = g_ptVideoMemHead;
/* 1. 优先: 取出空闲的、ID相同的videomem */
while (ptTmp)
{
if ((ptTmp->eVideoMemState == VMS_FREE) && (ptTmp->iID == iID)) //这块内存之前用过,有对应的ID号,并且现在未被使用
{
ptTmp->eVideoMemState = bCur ? VMS_USED_FOR_CUR : VMS_USED_FOR_PREPARE; //状态改为主线程或者子线程
return ptTmp;
}
ptTmp = ptTmp->ptNext;
}
/* 2. 如果前面不成功, 取出一个空闲的并且里面没有数据(ptVideoMem->ePicState = PS_BLANK)的VideoMem */
ptTmp = g_ptVideoMemHead; //执行到这里说明目前这个ID号的内存,之前一次也没有用过,这里就找一块空着的给他用
while (ptTmp)
{
if ((ptTmp->eVideoMemState == VMS_FREE) && (ptTmp->ePicState == PS_BLANK)) //找一个未被使用并且空着的,注意:有ID号的内存,但是里面没东西,也是会被分给别人用的
{
ptTmp->iID = iID;
ptTmp->eVideoMemState = bCur ? VMS_USED_FOR_CUR : VMS_USED_FOR_PREPARE;
return ptTmp;
}
ptTmp = ptTmp->ptNext;
}
/* 3. 如果前面不成功: 取出任意一个空闲的VideoMem */
ptTmp = g_ptVideoMemHead;
while (ptTmp)
{
if (ptTmp->eVideoMemState == VMS_FREE) //如果没有空的,但是又需要,那么就返回一个目前没有在使用的内存返回去
{
ptTmp->iID = iID;
ptTmp->ePicState = PS_BLANK; //强制设置成空
ptTmp->eVideoMemState = bCur ? VMS_USED_FOR_CUR : VMS_USED_FOR_PREPARE;
return ptTmp;
}
ptTmp = ptTmp->ptNext;
}
/* 4. 如果没有空闲的VideoMem并且bCur为1, 则取出任意一个VideoMem(不管它是否空闲) */
if (bCur) //主线程要用,就拿一块出去给它用
{
ptTmp = g_ptVideoMemHead;
ptTmp->iID = iID;
ptTmp->ePicState = PS_BLANK;
ptTmp->eVideoMemState = bCur ? VMS_USED_FOR_CUR : VMS_USED_FOR_PREPARE;
return ptTmp;
}
return NULL;
}
把某一块内存刷成某一个颜色
void ClearVideoMem(PT_VideoMem ptVideoMem, unsigned int dwColor)
{
unsigned char *pucVM;
unsigned short *pwVM16bpp;
unsigned int *pdwVM32bpp;
unsigned short wColor16bpp; /* 565 */
int iRed;
int iGreen;
int iBlue;
int i = 0;
pucVM = ptVideoMem->tPixelDatas.aucPixelDatas; //获得这一块内存的地址
pwVM16bpp = (unsigned short *)pucVM; //强制转换成对应bpp,这样地址自加就可以了
pdwVM32bpp = (unsigned int *)pucVM;
switch (ptVideoMem->tPixelDatas.iBpp) //根据bpp选择不同的
{
case 8:
{
memset(pucVM, dwColor, ptVideoMem->tPixelDatas.iTotalBytes); //memset只会取dwColor的后8位,把整个内存填满
break;
}
case 16:
{
/* 先根据32位的dwColor构造出16位的wColor16bpp */
//32位的dwColor应该是RGB888的,只使用低24位,如下
iRed = (dwColor >> (16+3)) & 0x1f;
iGreen = (dwColor >> (8+2)) & 0x3f;
iBlue = (dwColor >> 3) & 0x1f;
wColor16bpp = (iRed << 11) | (iGreen << 5) | iBlue; //改成565的
while (i < ptVideoMem->tPixelDatas.iTotalBytes)
{
*pwVM16bpp = wColor16bpp;
pwVM16bpp++;
i += 2;
}
break;
}
case 32:
{
while (i < ptVideoMem->tPixelDatas.iTotalBytes) //32字节的就直接4个字节4个字节地写进去
{
*pdwVM32bpp = dwColor;
pdwVM32bpp++;
i += 4;
}
break;
}
default :
{
DBG_PRINTF("can't support %d bpp\n", ptVideoMem->tPixelDatas.iBpp);
return;
}
}
}
把某个内存中的某一块地方刷成某个颜色
void ClearVideoMemRegion(PT_VideoMem ptVideoMem, PT_Layout ptLayout, unsigned int dwColor)
{
unsigned char *pucVM;
unsigned short *pwVM16bpp;
unsigned int *pdwVM32bpp;
unsigned short wColor16bpp; /* 565 */
int iRed;
int iGreen;
int iBlue;
int iX;
int iY;
int iLineBytesClear;
int i;
//算出初始的地址
pucVM = ptVideoMem->tPixelDatas.aucPixelDatas + ptLayout->iTopLeftY * ptVideoMem->tPixelDatas.iLineBytes + ptLayout->iTopLeftX * ptVideoMem->tPixelDatas.iBpp / 8;
pwVM16bpp = (unsigned short *)pucVM;
pdwVM32bpp = (unsigned int *)pucVM;
iLineBytesClear = (ptLayout->iBotRightX - ptLayout->iTopLeftX + 1) * ptVideoMem->tPixelDatas.iBpp / 8; //要清的这块内存,一行有多少Bytes
switch (ptVideoMem->tPixelDatas.iBpp)
{
case 8:
{
for (iY = ptLayout->iTopLeftY; iY <= ptLayout->iBotRightY; iY++) //一行一行的memset
{
memset(pucVM, dwColor, iLineBytesClear);
pucVM += ptVideoMem->tPixelDatas.iLineBytes;
}
break;
}
case 16:
{
/* 先根据32位的dwColor构造出16位的wColor16bpp */
iRed = (dwColor >> (16+3)) & 0x1f;
iGreen = (dwColor >> (8+2)) & 0x3f;
iBlue = (dwColor >> 3) & 0x1f;
wColor16bpp = (iRed << 11) | (iGreen << 5) | iBlue;
for (iY = ptLayout->iTopLeftY; iY <= ptLayout->iBotRightY; iY++) //一行一行地来
{
i = 0;
for (iX = ptLayout->iTopLeftX; iX <= ptLayout->iBotRightX; iX++) //一个像素一个像素的cp
{
pwVM16bpp[i++] = wColor16bpp; //这里用i主要是让pwVM16bpp始终指向本行的首地址,在下面换行的时候就简单了
}
pwVM16bpp = (unsigned short *)((unsigned int)pwVM16bpp + ptVideoMem->tPixelDatas.iLineBytes); //指向下一行
}
break;
}
case 32:
{
for (iY = ptLayout->iTopLeftY; iY <= ptLayout->iBotRightY; iY++) //一行一行
{
i = 0;
for (iX = ptLayout->iTopLeftX; iX <= ptLayout->iBotRightX; iX++) //一个像素一个像素
{
pdwVM32bpp[i++] = dwColor;
}
pdwVM32bpp = (unsigned int *)((unsigned int)pdwVM32bpp + ptVideoMem->tPixelDatas.iLineBytes); //下一行,与16bpp大同小异
}
break;
}
default :
{
DBG_PRINTF("can't support %d bpp\n", ptVideoMem->tPixelDatas.iBpp);
return;
}
}
}
3.fb.c
/* 屏的初始化,包括了
* 打开屏、获得屏的可变与不可变信息、屏的framebuffer映射到内存
* 计算整个framebuffer的大小、计算一行的大小、填充T_DispOpr结构体
* */
static int FBDeviceInit(void)
/* 屏上某一个点的显示 */
static int FBShowPixel(int iX, int iY, unsigned int dwColor)
/* T_DispOpr结构体中的一项
* 在该显示设备(这里是屏)上显示一页
* 本质就是 PT_VideoMem->tPixelDatas.aucPixelDatas 地址memcpy到 framebuffer中
* */
static int FBShowPage(PT_VideoMem ptVideoMem)
/* 把屏清成某一种颜色 */
static int FBCleanScreen(unsigned int dwBackColor)
/* 注册,在disp_manager.c中调用,disp_manager.h中声明 */
int FBInit(void)