云计算百科
云计算领域专业知识百科平台

Linux高级编程-framebuffer

1.framebuffer概念

UI技术:User interface  

framebuffer:帧缓冲、帧缓存技术
                        Linux内核专门为图形化显示提供的一套应用程序接口。

2.基本操作

(1)操作步骤

1. 打开显示设备(/dev/fb0)
2. 获取显示设备相关参数(分辨率,像素格式)—》ioctl
3. 建立显存空间和用户空间的内存映射
4. 向映射的用户空间写入RGB颜色值
5. 解除映射关系
6. 关闭显示设备

(2)函数接口

int init_fb(char *devname)
{
//1. 打开显示设备(/dev/fb0)
fb = open(devname, O_RDWR);
if (-1 == fb)
{
perror("open fb error");
return -1;
}

//2. 获取显示设备相关参数(分辨率,像素格式)

int ret = ioctl(fb, FBIOGET_VSCREENINFO, &vinfo);
if (ret < 0)
{
perror("ioctl error");
return -1;
}

printf("xres = %d, yres = %d\\n", vinfo.xres, vinfo.yres);
printf("xres_virtual = %d, yres_virtual = %d\\n", vinfo.xres_virtual, vinfo.yres_virtual);
printf("bits_per_pixel = %d\\n", vinfo.bits_per_pixel);

//3. 建立显存空间和用户空间的内存映射
size_t len = vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel/8;
pmem = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0);
if (pmem == MAP_FAILED)
{
perror("mmap error");
return -1;
}

return 0;
}

void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
功能:建立内存映射
参数:
      addr :映射的用户空间首地址  
                  NULL:让操作系统自己分配用户空间
      length:要映射的空间大小
      prot: 操作权限
                     PROT_READ  Pages may be read.
                     PROT_WRITE Pages may be written
     flag : MAP_SHARED
     fd:显示设备文件描述符
     offset:偏移量
                   0:从显存开头映射
返回值:
      成功:映射的用户空间首地址
      失败:MAP_FAILED((void *)-1)

int uninit_fb()
{
//5. 解除映射关系
//6. 关闭显示设备
size_t len = vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel/8;
munmap(pmem, len);
close(fb);

}

3.基本算法实现

(1)画一个点

void draw_point(int x, int y, unsigned int col)
{
if (x >= vinfo.xres || y >= vinfo.yres)
{
return ;
}
if (vinfo.bits_per_pixel == RGB_FMT_888)
{
unsigned int *p = pmem;
*(p+vinfo.xres_virtual*y+x) = col;
}
else if (vinfo.bits_per_pixel == RGB_FMT_565)
{
unsigned short *p = pmem;
*(p+vinfo.xres_virtual*y+x) = col;
}
}

算法思想:

边界检查:首先检查坐标 (x, y) 是否在屏幕有效范围内(x < vinfo.xres 且 y < vinfo.yres),若越界则直接返回。

像素格式处理:

若为 32位RGB888格式(RGB_FMT_888):

将显存指针 pmem 转换为 unsigned int* 类型。

计算目标像素的线性地址:pmem + vinfo.xres_virtual * y + x。

将颜色值 col 直接写入该地址。

若为 16位RGB565格式(RGB_FMT_565):

将显存指针 pmem 转换为 unsigned short* 类型。

计算目标像素的线性地址:pmem + vinfo.xres_virtual * y + x。

将颜色值 col(需确保是16位)写入该地址。

(2)画一条横线

void draw_x_line(int x, int y, int end, unsigned int col)
{
for(int i = 0; i < end; ++i)
{
draw_point((x + i), y, col);
}
}

算法思想:

水平线绘制:从起点 (x, y) 开始,向右连续绘制 end 个像素。

循环控制:

初始化计数器 i = 0。

循环条件 i < end,每次迭代调用 draw_point(x + i, y, col) 绘制一个像素。

循环结束后,形成一条从 (x, y) 到 (x + end – 1, y) 的水平线段。

(3)画一条竖线

void draw_y_line(int x, int y, int end, unsigned int col)
{
for(int i = 0; i < end; ++i)
{
draw_point(x, (y + i), col);
}
}

算法思想:

垂直线绘制:从起点 (x, y) 开始,向下连续绘制 end 个像素。

循环控制:

初始化计数器 i = 0。

循环条件 i < end,每次迭代调用 draw_point(x, y + i, col) 绘制一个像素。

循环结束后,形成一条从 (x, y) 到 (x, y + end – 1) 的垂直线段。

(4)画一个矩形

void draw_rect(int x, int y, int weight, int high, unsigned int col)
{
draw_x_line(x, y, weight, col);
draw_x_line(x, (y + high), weight, col);
draw_y_line(x, y, high, col);
draw_y_line((x + weight), y, high, col);
}

算法思想:

矩形边框绘制:通过组合四条边完成矩形绘制:

上边:调用 draw_x_line(x, y, weight, col)。

下边:调用 draw_x_line(x, y + high, weight, col)。

左边:调用 draw_y_line(x, y, high, col)。

右边:调用 draw_y_line(x + weight, y, high, col)。

注意:

此函数仅绘制矩形边框,不填充内部。

(5)画一个圆

void draw_Circle(int x, int y, int r, unsigned int col)
{
int x0 = 0;
int y0 = 0;
int si = 0;

for (si = 0; si <= 360; si++)
{
x0 = r * sin(PI * 2 / 360 * si) + x;
y0 = r * cos(PI * 2 / 360 * si) + y;

draw_point(x0, y0, col);
}
}

算法思想:

参数化圆绘制:

遍历角度 si 从 0° 到 360°,步长为 1°。

对每个角度 si:

计算圆上点的坐标:

x0 = r * sin(θ) + x(θ = PI * 2 / 360 * si 弧度)。

y0 = r * cos(θ) + y。

调用 draw_point(x0, y0, col) 绘制单个像素。

效果:

通过离散点近似形成一个圆形,精度取决于角度步长。

(6)画一个图

int get_bmp_head_info(const char *bmpname, Bmp_file_head_t *pheadinfo, Bmp_info_t *pbmpinfo)
{
FILE *fp = fopen(bmpname, "r");
if (NULL == fp)
{
perror("fopen error");
return -1;
}

fread(pheadinfo, sizeof(Bmp_file_head_t), 1, fp);
fread(pbmpinfo, sizeof(Bmp_info_t), 1, fp);

fclose(fp);

return 0;
}

void draw_bmp(int x, int y, char *bmpname)
{
Bmp_file_head_t headinfo;
Bmp_info_t bmpinfo;

get_bmp_head_info(bmpname, &headinfo, &bmpinfo);

int fd = open(bmpname, O_RDONLY);
if (-1 == fd)
{
perror("open bmp error");
return ;
}
lseek(fd, 54, SEEK_SET);
unsigned char *buff = malloc(bmpinfo.biHeight*bmpinfo.biWidth*bmpinfo.biBitCount/8);
read(fd, buff, bmpinfo.biHeight*bmpinfo.biWidth*bmpinfo.biBitCount/8);
close(fd);

unsigned char *p = buff;
unsigned char r, g, b;

for (int j = bmpinfo.biHeight-1; j >= 0; j–)
{
for (int i = 0; i < bmpinfo.biWidth; i++)
{
b = *p;++p;
g = *p;++p;
r = *p;++p;
if (vinfo.bits_per_pixel == RGB_FMT_888)
{
unsigned int col = (r << 16) | (g << 8) | (b << 0);
draw_point(i+x, j+y, col);
}
else if (vinfo.bits_per_pixel == RGB_FMT_565)
{
unsigned short col = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
draw_point(i+x, j+y, col);
}
}

}

free(buff);

}

get_bmp_head_info(const char *bmpname, Bmp_file_head_t *pheadinfo, Bmp_info_t *pbmpinfo)

算法思想:

文件操作:

以只读模式打开BMP文件,失败时返回错误。

头信息读取:

使用 fread 读取BMP文件头(Bmp_file_head_t)到 pheadinfo。

继续读取BMP信息头(Bmp_info_t)到 pbmpinfo。

资源释放:关闭文件句柄,返回成功状态。

draw_bmp(int x, int y, char *bmpname)

算法思想:

头信息解析:调用 get_bmp_head_info 获取BMP的宽、高、位深等信息。

像素数据加载:

打开文件并跳过54字节的文件头和信息头。

根据BMP信息头中的图像尺寸和位深(如 biWidth × biHeight × biBitCount/8),分配缓冲区 buff 并读取像素数据。

像素绘制:

BMP像素顺序:BMP文件存储顺序为从下到上,需反向遍历行(j 从 biHeight-1 到 0)。

颜色转换:

对于 RGB888格式屏幕:将BGR像素数据(BMP格式)转换为RGB888,合并为 unsigned int 后调用 draw_point。

对于 RGB565格式屏幕:将RGB分量压缩为5-6-5位,合并为 unsigned short。

绘制每个像素到屏幕坐标 (x + i, y + j)。

(7)在图中加入字(使用字模)

void draw_word(int x, int y, unsigned char *pword, int w, int h, unsigned int col)
{
for (int j = 0; j < h; j++)
{
for (int i = 0; i < w; i++)
{
unsigned char tmp = pword[i+j*w];
for (int k = 0; k < 8; k++)
{
if (tmp & 0x80)
{
draw_point(x+i*8+k, y+j, col);
}
else
{

}
tmp = tmp << 1;
}
}

}
}

算法思想:

字模数据解析:

字模数据 pword 为 w × h 的字节数组,每个字节表示8个水平像素点(1位1像素)。

逐行逐像素绘制:

外层循环遍历行(j 从 0 到 h-1)。

内层循环遍历列(i 从 0 到 w-1):

获取当前字节 tmp = pword[i + j * w]。

对字节的8个位(从高位到低位):

若某位为 1,调用 draw_point(x + i*8 + k, y + j, col) 绘制像素。

若为 0,跳过(透明或背景)。

效果:

将1位字模数据按指定颜色展开为8倍宽度的字符图形。

赞(0)
未经允许不得转载:网硕互联帮助中心 » Linux高级编程-framebuffer
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!