拓冰建站拓冰建站
首页 / 资讯中心 / 正文

【专题】frmaebuffer打印bmp图片

文章目录framebuffer打印bmp格式图片专题前言一、bmp格式图片1.1 概述1.2 位图文件头1.2 位图信息头1.3 位图数据二、framebuffer2.1 什么是framebuffer2.2 如何操作framebuffer3.2.1 打开显示设备文件3.2.2 获取显示设备的相关参数3.2.3 建立内存映射3.2.4 写入RGB颜色值3.2.5 解除内存映射与关闭显示设备文件三、frmaebuffer打印bmp图片framebuffer打印bmp格式图片专题前言在上篇《文件操作学习3【目录IO、出错处理及framebuffer基础操作】》文章中我们了解到了framebuffer的本质和基本使用方法这篇文章我将分享如何通过framebuffer打印bmp格式的图片。既然需要打印bmp图片我们首先当然得了解bmp图片的存储格式。所以这篇文章我们主要分为bmp图片、framebuffer和通过framebuffer实现打印三个部分组成。一、bmp格式图片1.1 概述bmp是英文Bitmap位图的简写它是Windows操作系统中的标准图像文件格式随着windows的流行bmp位图格式被广泛应用。位图文件由四个部分组成• 位图文件头bitmap-file header• 位图信息头bitmap-information header• 调色板color table需要注意的是16bit、24bit、32bit的位图一般没有调色板因为从16bit开始就直接使用位图数据表示颜色了所以后面的内容我们都以没有调色板的bmp图片为例来讲解。• 位图数据图像数据Data Bits 或Data Body)阵列.1.2 位图文件头BMP位图文件头共14个字节包含了文件类型标志、文件大小等信息定义的结构体也比较详细说明如下#pragmapack(1)//此处一定要强制让结构体按照指定字节数对齐typedefstructinfo_bmp_file{unsignedshortbfType;// 文件的类型该值必需是0x4D42也就是字符BM。unsignedintbfSize;// 位图文件的大小用字节为单位unsignedshortbfReserved1;// 保留必须设置为0unsignedshortbfReserved2;// 保留必须设置为0unsignedintbfOffBits;// 位图数据距离文件开头偏移量用字节为单位}Info_ft;#pragmapack()1.2 位图信息头BMP位图信息头共40个字节包含了文件类型标志、文件大小等信息定义的结构体也比较详细说明如下typedefstructtagBITMAPINFOHEADER{unsignedintbiSize;// BITMAPINFOHEADER结构所需要的字数unsignedintbiWidth;// 图像宽度单位为像素unsignedintbiHeight;// 图像高度单位为像素负数则说明图像是正向的unsignedshortbiPlanes;// 为目标设备说明位面数其值将总是被设为1unsignedshortbiBitCount;// 一个像素占用的bit位值位1、4、8、16、24、32unsignedintbiCompression;// 压缩类型unsignedintbiSizeImage;// 位图数据的大小以字节为单位unsignedintbiXPelsPerMeter;// 水平分辨率单位 像素/米unsignedintbiYPelsPerMeter;// 垂直分辨率单位 像素/米unsignedintbiClrUsed;unsignedintbiClrImportant;}Info_bt;读取bmp图片信息代码示例#includestdio.h#pragmapack(1)typedefstructinfo_bmp_file{unsignedshortbfType;unsignedintbfSize;unsignedshortbfReserved1;unsignedshortbfReserved2;unsignedintbfOffBits;}Info_ft;#pragmapack()typedefstructtagBITMAPINFOHEADER{unsignedintbiSize;unsignedintbiWidth;unsignedintbiHeight;unsignedshortbiPlanes;unsignedshortbiBitCount;unsignedintbiCompression;unsignedintbiSizeImage;unsignedintbiXPelsPerMeter;unsignedintbiYPelsPerMeter;unsignedintbiClrUsed;unsignedintbiClrImportant;}Info_bt;intmain(){FILE*fpfopen(3.bmp,r);if(NULLfp){printf(fopen error!\n);return-1;}Info_ft info_file;Info_bt info_bmp;fread(info_file,14,1,fp);fread(info_bmp,40,1,fp);printf(%d %d %d %d %d\n,info_file.bfType,info_file.bfSize,info_file.bfReserved1,info_file.bfReserved2,info_file.bfOffBits);printf(%d %d %d %d %d %d %d %d %d %d %d\n,info_bmp.biSize,info_bmp.biWidth,info_bmp.biHeight,info_bmp.biPlanes,info_bmp.biBitCount,info_bmp.biCompression,info_bmp.biSizeImage,info_bmp.biXPelsPerMeter,info_bmp.biYPelsPerMeter,info_bmp.biClrUsed,info_bmp.biClrImportant);printf(sizeof(Info_ft) %ld\n,sizeof(Info_ft));printf(sizeof(Info_bt) %ld\n,sizeof(Info_bt));fclose(fp);return0;}1.3 位图数据BMP图像支持多种颜色深度包括1位、4位、8位、16位、24位和32位等。不同颜色深度决定了每个像素所占用的字节数颜色深度位/像素每像素字节数说明10.125单色图像每个字节表示8个像素40.516色图像每个字节表示2个像素81256色图像使用调色板162高彩色RGB555或RGB565格式243真彩色RGB三通道各占1字节324带Alpha通道的真彩色RGBA四通道各占1字节我们后文以24bit为例来进行讲解。位图数据存放实际的argb值可以理解为每个像素点的颜色。由于Windows默认的扫描的最小单位是4字节BMP图像顺应了这个要求要求每行的数据的长度必须是4的倍数。示例24位图像宽度为10像素每像素3字节 → 行字节数为 10 × 3 30字节30字节不是4的整数倍 → 需要填充 2 字节→ 实际行字节数为 32字节在24位BMP图像中每个像素由3个字节组成分别表示红色R、绿色G和蓝色B三个通道的强度值范围为0~255。具体排列顺序通常为BGR而非RGB。BMP图像的像素数据在文件中通常是从图像的底部开始向上存储的即第一行像素数据对应图像的最下方一行。这种设计源于Windows GDI的坐标系其中原点位于图像左上角而扫描线是从下往上进行的。对于bmp图片像素数据这些非常规的存储方式在我们通过framebuffer帧缓存技术去在Linux真终端上打印图片时就需要我们在代码中进行处理了。二、framebuffer2.1 什么是framebuffer帧缓存、帧缓冲技术是Linux为显示设备提供的一个接口把显存抽象后的一种设备文件通过该设备文件可以实现应用层向显存空间的数据写入从而实现像素点的绘制。2.2 如何操作framebuffer通过内存映射技术可以实现显存空间和用户空间的内存映射通过向用户空间写入RGB颜色值从而实现显存数据的写入。像素点位数/位深度的种类RGB888 四个字节RGB565 两个字节framebuffer的操作流程打开显示设备文件/dev/fb0获取显示设备的相关参数分辨率、像素点位数/位深度建立内存映射写入RGB颜色值解除内存映射关闭显示设备文件。3.2.1 打开显示设备文件通过我们的文件IO的函数接口open()打开framebuffer的设备文件它在/dev/fb0这个目录下//1. 打开fbopen(/dev/fb0,O_RDWR);if(-1fb){perror(open /dev/fb0 error);return-1;}3.2.2 获取显示设备的相关参数我们通过ioctl()这个系统调用的函数接口来获取设备的相关参数它的描述是这样的ioctl()系统调用用于操控特殊文件底层对应的设备参数。参数fd必须是一个已经成功打开的文件描述符。第二个参数是依赖具体设备的请求命令码request。不同驱动命令码含义完全不一样没有全局统一标准。第三个参数是一个无类型的内存指针。历史上C 语言还没有void*时这里约定使用char *argp所以手册后续讲解中将它命名为argp。也就是原型里的可变参数你可以传任意类型指针。在linuxubuntu:/usr/include/linux$这个路径下的的fb.h文件是Linux 液晶屏帧缓冲驱动专用头文件里面定义了 Framebuffer 设备 (/dev/fb0) 所有可用的ioctl命令码。例如FBIOGET_VSCREENINFO这个宏就是我们现在需要的获取虚拟屏幕可变信息的命令码分辨率、色深、像素格式等。还有这个struct fb_var_screeninfo 可变屏幕信息结构体structfb_var_screeninfo{__u32 xres;/* visible resolution */__u32 yres;__u32 xres_virtual;/* virtual resolution */__u32 yres_virtual;__u32 xoffset;/* offset from virtual to visible */__u32 yoffset;/* resolution */__u32 bits_per_pixel;/* guess what */__u32 grayscale;/* 0 color, 1 grayscale, *//* 1 FOURCC */structfb_bitfieldred;/* bitfield in fb mem if true color, */structfb_bitfieldgreen;/* else only length is significant */structfb_bitfieldblue;structfb_bitfieldtransp;/* transparency */__u32 nonstd;/* ! 0 Non standard pixel format */__u32 activate;/* see FB_ACTIVATE_* */__u32 height;/* height of picture in mm */__u32 width;/* width of picture in mm */__u32 accel_flags;/* (OBSOLETE) see fb_info.flags */};成员变量含义说明xres/yres可见屏幕分辨率实际 LCD 显示宽高像素例800×480xres800yres480xres_virtual/yres_virtual虚拟显存分辨率显存可以开辟一块比屏幕更大的画布用于硬件滚屏、双缓冲。例如虚拟高度设为yres*2实现页面翻页xoffset/yoffset显示偏移量从虚拟显存的(xoffset,yoffset)位置开始截取画面输出到屏幕修改yoffset配合大的yres_virtual即可实现硬件滚动 (Panning)bits_per_pixel每个像素占用比特数色深常见值16 (RGB565)、24 (RGB888)、32 (RGBA8888)grayscale灰度模式标志0 彩色1 灰度大于 1 代表使用 FOURCC 压缩图像格式red/green/blue/transpRGB 分量位域信息struct fb_bitfield结构体包含每个颜色通道起始偏移位、占用位数。RGB565红占 5bit绿占 6bit蓝占 5bitnonstd非标准像素格式标记非 0 代表不是标准 RGB 格式activate参数生效时机。控制调用FBIOPUT_VSCREENINFO后新分辨率是立刻生效还是等到下一次 vsync 垂直同步height/width屏幕物理尺寸单位毫米 (mm)用于计算 DPI不影响像素绘图accel_flags硬件加速标志已经废弃不再使用代码示例//2. 获取显示设备的相关参数structfb_var_screeninfovinfo;intretioctl(fb,FBIOGET_VSCREENINFO,vinfo);if(-1ret){perror(ioctl error);return-1;}printf(vinfo.xres %d, vinfo.yres %d\n,vinfo.xres,vinfo.yres);printf(vinfo.xres_virtual %d, vinfo.yres_virtual %d\n,vinfo.xres_virtual,vinfo.yres_virtual);printf(vinfo.bits_per_pixel %d\n,vinfo.bits_per_pixel);3.2.3 建立内存映射以这两张图为例Linux帧缓冲通过mmap将位于内核空间的物理显存映射到用户进程虚拟地址空间程序获得映射指针pmem后便可利用公式*(pmem y*w x)直接计算像素偏移通过指针赋值直接修改显存数据无需调用read/write显存更新后硬件自动刷新到800×600的显示屏上实现屏幕绘图。显存可开辟一块大于实际屏幕xres × yres的虚拟画布xres_virtual × yres_virtual屏幕仅截取画布的一部分画面进行显示修改x_offset、y_offset显示偏移量无需移动显存像素数据就能快速切换可视区域从而实现硬件滚屏、双缓冲翻页效果。mmap()函数原型#includesys/mman.hvoid*mmap(void*addr,size_tlength,intprot,intflags,intfd,off_toffset);功能建立文件和用户空间的内存映射。参数addr要映射的用户空间的首地址一般情况下填NULL让内核自己寻找一块连续的内存空间并将空间首地址返回length要映射的用户空间大小prot对映射空间的读写权限1. PROT_READ2. PROT_WRITEflagsMAP_SHARED共享映射区域fd要映射的文件描述符offset映射的偏移量返回值成功返回映射到的用户空间首地址失败返回MAP_FAILED((void *)-1)。内存映射代码示例void*pmemNULL;//3. 建立内存映射lenvinfo.xres_virtual*vinfo.yres_virtual*vinfo.bits_per_pixel/8;pmemmmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fb,0);if(MAP_FAILEDpmem){perror(mmap error);return-1;}3.2.4 写入RGB颜色值首先校验像素坐标是否超出可视屏幕范围随后基于虚拟显存宽度计算目标像素的内存偏移通过显存映射指针pmem直接向帧缓冲写入指定颜色值完成单点绘制。voidshow_point(intx,inty,unsignedintcol){//4. 写入RGB颜色值if(xvinfo.xres||yvinfo.yres){return;}*((unsignedint*)pmemy*vinfo.xres_virtualx)col;}3.2.5 解除内存映射与关闭显示设备文件munmap()函数原型#includesys/mman.hintmunmap(void*addr,size_tlength);功能解除mmap()建立的内存映射释放用户空间与物理显存之间的映射关系取消后不能再通过该指针访问显存关闭帧缓冲文件前必须调用。参数addrmmap返回的映射起始虚拟地址如前面的pmemlength需要解除映射的内存字节长度显存映射大小finfo.smem_len返回值成功返回0失败返回-1并设置errno错误号代码示例voiddeinit_fb(){//5. 解除内存映射munmap(pmem,len);//6. 关闭显示设备文件。close(fb);}通过这些步骤我们就可以绘制一个点了再借助这个点就可以封装其他绘制的函数接口.三、frmaebuffer打印bmp图片接下来就是本片文章的重头戏啦在framebuffer的已有的基础操作之上对bmp格式的图片进行绘制。这是我前面所提到的存储bmp图片的相关信息的结构体在我们通过frmaebuffer打印bmp图片时需要用到里面的信息因此我们先对其进行定义。#pragmapack(1)typedefstructinfo_bmp_file{unsignedshortbfType;unsignedintbfSize;unsignedshortbfReserved1;unsignedshortbfReserved2;unsignedintbfOffBits;}Info_ft;#pragmapack()typedefstructtagBITMAPINFOHEADER{unsignedintbiSize;unsignedintbiWidth;unsignedintbiHeight;unsignedshortbiPlanes;unsignedshortbiBitCount;unsignedintbiCompression;unsignedintbiSizeImage;unsignedintbiXPelsPerMeter;unsignedintbiYPelsPerMeter;unsignedintbiClrUsed;unsignedintbiClrImportant;}Info_bt;首先我们需要打开bmp图片读取文件头与信息头并通过定义的结构体获取bmp图片的参数。intfdopen(bmpfile,O_RDONLY);if(-1fd){perror(open file error);return;}Info_ft fhead;Info_bt ihead;read(fd,fhead,sizeof(fhead));read(fd,ihead,sizeof(ihead));随后按图片宽高和位深计算像素总字节数并申请内存缓冲区读取像素数据存入 buff随后从图片最后一行向前逐行遍历每个像素依次取出蓝、绿、红分量拼接成 24 位色彩值调用 show_point 将像素绘制到 framebuffer 屏幕指定偏移 (x,y) 位置。这里需要特别注意bmp图片像素数据这些非常规的存储方式:每行的数据的长度必须是4的倍数;像素点具体排列顺序通常为BGR而非RGB像素数据在文件中通常是从图像的底部开始向上存储的即第一行像素数据对应图像的最下方一行。在下面的代码中因为对于打印的图片我会对其进行处理使其横向像素满足4的倍数这样就不用对代码进行更多的处理了所以下面的代码仅适合横向像素为4的倍数的bmp图片有兴趣的同学可以想想怎么通过代码的修改实现所有的bmp图片打印。size_tsizeihead.biHeight*ihead.biWidth*ihead.biBitCount/8;unsignedchar*buffmalloc(size);if(NULLbuff){perror(buff malloc error);return;}read(fd,buff,size);close(fd);unsignedchar*pbuff;unsignedcharr,g,b;unsignedintcol;for(intiihead.biHeight-1;i0;--i){for(intj0;jihead.biWidth;j){b*p;p;g*p;p;r*p;p;col(r16)|(g8)|(b0);show_point(jx,iy,col);}}最后绘制完成后释放缓冲区并关闭文件。free(buff);好了以上就是本期博客全部内容啦希望能对屏幕前的你产生帮助我们下期再见
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门