学习笔记: 系统IO实际应用(显示bmp图片)

目录

一、24位bmp图片格式特点

二、具体实现思路

问题1:

问题2:


一、24位bmp图片格式特点

        54头字节,每个像素占3字节BGR顺序,上下颠倒存储,bmp图片的宽度占用的字节数如果不能被4整除,window系统会给每一行填充垃圾数凑够4字节整除。

学习笔记: 系统IO实际应用(显示bmp图片)

学习笔记: 系统IO实际应用(显示bmp图片)

        液晶屏:每个像素点占4个字节,分别表ARGB的值, A表示透明度

        54字节的头信息在另外一位大佬的博客中有详细的介绍,以下为链接:24位图片特点

二、具体实现思路

        第一步:打开你要显示的图片,必须为bmp格式。  open()

        第二步:打开开发板液晶的驱动。  open("/dev/fb0")

        第三步:读取图片的像素,保存到buf中。(有效像素点的值在54字节后,使用lseek()移动光标) read()。

        第四部:往液晶写像素,必须对应,否则图片失真。

        最后关闭图像和液晶。

#include "myhead.h"

int main(int argc,char **argv)
{
	int i;
	int x,y;
	int bmpfd;
	int lcdfd;
	//定义数组存放读取到的颜色值
	char bmpbuf[800*480*3];  //BGR数据
	//定义数组存放转换得到的ARGB数据
	int lcdbuf[800*480];
	//定义一个临时数组
	int tempbuf[800*480];
	//打开要显示的bmp图片
	bmpfd=open(argv[1],O_RDWR);
	if(bmpfd==-1)
	{
		perror("打开bmp失败!\n");
		return -1;
	}
	
	//打开液晶屏的驱动
	lcdfd=open("/dev/fb0",O_RDWR);
	if(lcdfd==-1)
	{
		perror("打开lcd失败!\n");
		return -1;
	}
	
	//跳过54字节的头信息,从55开始读取
	lseek(bmpfd,54,SEEK_SET);
	
	//从55字节开始读取
	read(bmpfd,bmpbuf,800*480*3);
	
	//把3个字节bmp数据--》转换成4个字节ARGB数据
	/*
	bmpbuf[0]-->R
	bmpbuf[1]-->G
	bmpbuf[2]-->B
	*/
	for(i=0; i<800*480; i++) //保证每个像素点都能转换
		lcdbuf[i]=0x00<<24|bmpbuf[3*i+2]<<16|bmpbuf[3*i+1]<<8|bmpbuf[3*i];
		
	//(x,y)这个点跟(x,479-y)调换  lcdbuf[y*800+x]
	//把颠倒的图片翻转过来
	for(x=0; x<800; x++)
		for(y=0; y<480; y++)
			//lcdbuf[(479-y)*800+x]=lcdbuf[y*800+x]; //错误,不应该自己赋值给自己
            tempbuf[(479-y)*800+x]=lcdbuf[y*800+x];
	//把转换得到的ARGB数据填充到液晶屏
	write(lcdfd,tempbuf,800*480*4);
	
	//关闭
	close(bmpfd);
	close(lcdfd);
	return 0;
}

问题1:

        write()显示图像效率太低,速度慢。

        原因:

学习笔记: 系统IO实际应用(显示bmp图片)

         解决:#include <sys/mman.h>
                   void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
                          返回值:成功 返回液晶屏的首地址
                                  失败 NULL
                            参数: addr --》一般设置为NULL 表示由操作系统自动分配硬件的首地址
                                  length --》你想映射的液晶屏地址大小  800*480*4
                                  prot --》内存区域的访问权限
                                             PROT_READ    //可读
                                             PROT_WRITE   //可写
                                  flags --》你映射的地址空间是否可以被共享
                                             MAP_SHARED   可以共享
                                             MAP_PRIVATE  不可以共享
                                  fd --》你要映射的硬件设备的文件描述符
                                  offset --》地址偏移,一般设置为0
                       解除映射
                            int munmap(void *addr, size_t length);
                            参数:addr --》你之前映射得到的首地址
                                  length --》映射的内存大小  

        问题2:

                如何实现任意位置显示任意大小图像

                

学习笔记: 系统IO实际应用(显示bmp图片)

#include "myhead.h"

int showbmp(int x,int y,char *bmpname);

int main(int argc,char **argv)
{
	int x = atoi(argv[2]);//atoi()将字符型转换成整形
	int y = atoi(argv[3]);
	
	showbmp(x,y,argv[1]);
	return 0;
}

int showbmp(int x,int y,char *bmpname)
{
	int i,j,k=0;
	int w,h;//图片的宽和高
	int bmpfd;
	int lcdfd;
	int headbuf[2]={0};//headbuf[0]  宽   headbuf[1]   高
	
	//定义数组存放转换得到的ARGB数据
	int lcdbuf[800*480];
	//打开要显示的bmp图片
	bmpfd=open(bmpname,O_RDWR);
	if(bmpfd==-1)
	{
		perror("打开bmp失败!\n");
		return -1;
	}
	//打开液晶屏的驱动
	lcdfd=open("/dev/fb0",O_RDWR);
	if(lcdfd==-1)
	{
		perror("打开lcd失败!\n");
		return -1;
	}
	
	//映射得到lcd的首地址
	int *lcdmem=mmap(NULL,800*480*4, PROT_READ |PROT_WRITE,MAP_SHARED ,lcdfd,0 );
	
	lseek(bmpfd,18,SEEK_SET);
	//读头信息
	read(bmpfd,headbuf,8);
	w=headbuf[0];
	h=headbuf[1];
	
	if(x+w>800||y+h>480 ) //判断图像是否在合理范围内
	{
		perror("图片超出范围!\n");
		return -1;
	}
	//定义数组存放读取到的颜色值
	char bmpbuf[w*h*3];  //BGR数据
	//定义一个临时数组
	int tempbuf[w*h];

	//跳过54字节的头信息,从55开始读取
	lseek(bmpfd,28,SEEK_CUR);
	if((3*w)%4==0)
	{
		read(bmpfd,bmpbuf,w*h*3);	//从55字节开始读取
	}else 
	{
		for(int k=0;k<h;k++)
		{
			read(bmpfd,&bmpbuf[k*w*3],w*3);
			lseek(bmpfd,4-(3*w)%4,SEEK_CUR );
		}
	}
	for(i=0; i<w*h; i++) //保证每个像素点都能转换
		lcdbuf[i]=0x00<<24|bmpbuf[3*i+2]<<16|bmpbuf[3*i+1]<<8|bmpbuf[3*i];
	
	//把颠倒的图片翻转过来
	for(i=0; i<w; i++)
		for(int j=0; j<h; j++)
            tempbuf[(h-j)*w+i]=lcdbuf[j*w+i];
	//确定坐标
	for(i=0;i<h;i++)
		for(int j=0;j<w;j++)
		{
			*(lcdmem+800*(i+y)+j+x)=tempbuf[k];
			k++;
		}
		
	//关闭
	close(bmpfd);
	close(lcdfd);
	//解除映射
	munmap(lcdmem,800*480*3);
	return 0;

}

上一篇:【C#】图片处理(底片,黑白,锐化,柔化,浮雕,雾化)


下一篇:解密RimWorld环世界地形数据