1.基于S5PV210的图片解码播放器(详解)

有道云笔记详细地址:
文档:图片解码播放器小项目(详解).note
链接:http://note.youdao.com/noteshare?id=9f9a43ac5ec6828cf467940dfa10da51&sub=A8280EB4B5A146C9A1F6612031305071

文章目录

一、开始动手写代码

1、Makefile介绍

(1)这是一个通用的项目管理的Makefile体系,自己写的(有子文件夹组织的)项目可以直接套用这套Makefile体系。

(2)包含三类:顶层Makefile、Makefile.build(完全不需要改动)、子文件夹下面的Makefile。
子文件夹下的Makefile一般是类似下面的内容。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pwsgMELC-1570028293237)(F95308E307894BA1A9ECE5BC07CFA0A1)]

(3)可以参考:http://www.cnblogs.com/lidabo/p/4521123.html。

2、使用SI建立工程

打开SI,在E:\Linux\winshare\x210kernel\tupian_project中新建一个SI_Proj文件夹,用于建立SI工程,然后新建一个输出hello word的main.c文件,并在ubuntu中make整个工程,并make cp到根文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-28cgC87l-1570028293240)(D3212F76475F446D8A238276FDAB198D)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3oy9ZgzM-1570028293241)(61AD59BE2EEA47A89184B37C33D2317A)]

启动开发板,进入driver_test目录下,可以查看到我们的工程目录,进入执行生成的imageplayer文件,可以输出hello word,到此新建工程结束。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tuvzJgKR-1570028293244)(321FA7F935AC44FBAEB009AF46656C7A)]

新建一个脚本文件run.sh,用于管理各种可执行文件,比如上面我们生成的./imageplayer文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bOKzRF4J-1570028293246)(107A0A80C7444F0EACCFD669B6224802)]

二、framebuffer驱动基本操作代码

E:\Linux\winshare\x210kernel\tupian_project

显示图片,需要framebuffer驱动。

①在SI工程中新建fb.c文件(保存在display文件夹下)

并在display文件夹里新建Makefile并添加内容

obj-y += fb.o(这就是子makefile,作用就是让fb.c能被编译进去)

②这里需要用到framebuffer显示图片

故将我们在驱动学习中framebuffer哪一阶段的应用程序拷贝到fb.c中
(即E:\Linux\4.LinuxDriver\7.frambuffer\7.1.fb1\appfb.c),并修改部分代码

(即将之前的所有宏定义和函数声明放在fb.h中,并在主Makefile中添加命令,使其将子目录能编译进去)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eGAxAg1q-1570028293250)(C0D3963AFF084915B8931301C41F7343)]

man.c如下:

#include <stdio.h>
#include <fb.h>
#include <unistd.h>

int main(void)
{
	int ret=-1;
	
	printf("image player demo........star \n");
	
	//第一步,打开设备
	ret = fb_open();
	if(ret < 0)
	    {
			perror("fb_open error.\n");
			return -1;
		}
	fb_draw_badk(WIDTH, HEIGHT, YELLOW);//调用背景填充函数
	
}

从新make编译,并拷贝到根文件系统中,重启开发板,执行脚本,显示预期一致。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-86ARibZo-1570028293255)(9AEAFC67E9E34E4BB28D68B3BA5FD8A5)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RXbxT1fd-1570028293256)(2108333A2096471A97479662733A7263)]

三、图片显示原理和实践

1、图片显示原理

  • (1)概念1:像素
    我们开发板的屏幕像素为1024*600,填充部分颜色,即是想要实现的图案
  • (2)概念2:点阵(像素点构成的阵列)
  • (3)分辨率
    物理分辨率:物理屏幕像素点的真实个数;
    显示分辨率:可以小于等于物理分辨率;(通过抽样)
  • (4)清晰度(与分辨率和像素间距有关,主观概念)
    像素间距相同时(物理尺寸固定了,则像素间距就固定了),分辨率越大越清晰;分辨率相同时,像素间距越小越清晰。
  • (5)bpp(RGB565、RGB888)
    像素深度,每个像素用多少bit数据表示。
    一般每个像素点使用32bit(4个字节),但一般是24位色(高八位一般没用或者用着其他标识,用00或者ff填充以达到内存对齐);
    RGB888表示红用8位,绿8位,蓝8位。
    (6)颜色序(RGB、BGR)

2、图片点阵数据获取

(1)Image2LCD软件提取

  • 输出数据类型:C语言数组;
  • 一般不要图像头数据,只需要纯数据;
  • 一般水平扫描;
  • 一般选24位真彩色(即RGB888);
  • 选1024*600后,点右边按钮更新;
  • 输出图像调整中,可以调整RGB。
  • 最后点击保存为xxx.h文件(保存在include目录下)
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YGYaR6s8-1570028293257)(4E611CF9449B4344ADB09C2D7307643A)]

在fb.c中添加如下代码,且在主函数中调动该函数,编译程序,拷贝到跟文件系统
//测试显示1024600分辨率的图片
void fb_draw_picture(void)
{
unsigned char
pdata=gImage_1024;//指针指向图像数组
unsigned int i,j,cnt;
unsigned int*p=pfb;//等于显示缓存区的首地址

for(i=0;i<HEIGHT;i++)
{
for(j=0;j<WIDTH;j++)
{
         cnt=i*WIDTH+j;//取出当前像素点编号
         cnt*=3;//一个像素点是3个字节,当前像素点的数据在数组中的下标
         //当前像素点对应的图像数据的RGB应该分别是pData[cnt+0]、pData[cnt+1]、pData[cnt+2]
         //当前像素点的数据
         *p=((pdata[cnt+0]<<0)|(pdata[cnt+1]<<8)|(pdata[cnt+2]<<16));//可以在这里修改,达到正确的显示(当RB相反时)
     p++;
}
}

}

重启开发板,执行.、run.sh脚本,看到屏幕如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NNbLZtZH-1570028293258)(63BFF505A0AF4F519147EEF410B2D335)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-10zlyp5i-1570028293259)(CD381E03B4114362AE2046736739C10D)]

与我们图片周边是粉色颜色不符合,原因肯定是RGB弄反了,这里,我们可以在上面的fb_draw_picture函数中进行修改,将RB调换,从新编译,执行脚本,显示和我们的原图就一样了。
*p=((pdata[cnt+2]<<0)|(pdata[cnt+1]<<8)|(pdata[cnt+0]<<16));//可以在这里修改,达到正确的显示(当RB相反时)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3l0QCy6i-1570028293260)(05F0F24F487648B7BB91E05F2335095B)]

(2)通过图片/视频文件直接代码方式提取

四、图片显示的高级话题

1、RGB顺序调整

(1)RGB顺序有三个地方都有影响

  • 第一个是fb驱动中的排布;
  • 第二个是应用程序中的排布;
  • 第三个是图像数据本身排布(Image2LCD中调整RGB顺序);

(2)如果三个点中RGB顺序是一致的就会显示正常。如果三个设置不一致就可能会导致显示结果中R和B相反了;

(3)实际写程序时,一般不去分析这东西,而是根据实际显示效果去调。如果反了就去调正应用程序中的RGB顺序就行了,这样最简单。

2、显示函数的其他写法

void fb_draw_picture2(void)
{
unsigned char* pdata=gImage_1024;//指针指向图像数组
unsigned int x,y,cnt;

for(y=0;y<HEIGHT;y++)
{
for(x=0;x<WIDTH;x++)
{
    cnt=y*WIDTH+x;//取出当前像素点编号          
    //当前像素点对应的图像数据的RGB应该分别是pData[cnt+0]、pData[cnt+1]、pData[cnt+2]
    //当前像素点的数据
    *(pfb+cnt)=((pdata[cnt*3+2]<<0)|(pdata[cnt*3+1]<<8)|(pdata[cnt*3+0]<<16));这里的像素矩阵和cnt有线性关系,所以可以这样写
    }
    }

}

五、任意分辨率大小图片显示

(1)图片比屏幕分辨率大

这种情况下多出来的部分肯定是没法显示的。处理方法是直接在显示函数中把多余不能被显示的部分给丢掉。

(2)图片大小比屏幕大小要小

这种情况下图片只是填充屏幕中一部分,剩余部分仍然保持原来的底色。
在获取图片数据时,大小和图片实际大小在这里是一致的。假如不一致呢?

  • ①从新使用Image2LCD软件制作分辨率为320*480的图片
  • ②重新编写测试图片显示函数后,编译拷贝…重启开发板,对比两种写法
//测试显示分辨率比屏幕小的图片
//pdata://指针指向图像数组
void fb_draw_picture3(unsigned char* pdata)
{
    //unsigned char* pdata=gImage_320480;
    unsigned int x,y,cnt;
    unsigned int a=0;

//图片的分辨率是320*480
    for(y=0;y<480;y++)
    {
		for(x=0;x<320;x++)
		{
                  cnt=y*WIDTH+x;/*cnt始终都是framebuff像素点的编号*/
      		     *(pfb+cnt)=((pdata[a+0]<<16)|(pdata[a+1]<<8)|(pdata[a+2]<<0));
		     a+=3;/*由于空缺一部分,像素点编号与像素点矩阵不存在倍数关系了,此时应该三个三个地传送进来*/                       
		}
	}
}
//测试显示分辨率比屏幕小的图片 另一种写法,对比看出
//pdata:指针指向图像数组
void fb_draw_picture4(unsigned char* pdata)
{
   // unsigned char* pdata=gImage_320480;//指针指向图像数组
    unsigned int x,y,cnt1;
    unsigned int cnt2=0;

 //图片的分辨率是320*480
    for(y=0;y<480;y++)
    {
	for(x=0;x<320;x++)
	{
             cnt1=y*WIDTH+x;//取出当前屏幕像素点编号   
             cnt2=y*320+x;	//取出当前图片像素点编号  
             //当前像素点对应的图像数据的RGB应该分别是pData[cnt+0]、pData[cnt+1]、pData[cnt+2]
             //当前像素点的数据
   	 *(pfb+cnt1)=((pdata[cnt2*3+2]<<0)|(pdata[cnt2*3+1]<<8)|(pdata[cnt2*3+0]<<16));这里的像素矩阵和cnt有线性关系,所以可以这样写

	}
    }
}

另一种写法,对比两种函数的写法,看出区别,烧录效果都一样,这里,不再赘述
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VBD33q4p-1570028293261)(108ACE9C4BBF4C62A50F3FAFA3E634CB)]

六、任意起点位置图片显示

1、小图片任意起点(但整个图片显示没有超出屏幕范围内)

算法1:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s1btYqX1-1570028293263)(B819CC65CF0C41769D83150895A1202B)]

//测试在屏幕指定坐标显示图片的位置 
//x0:图片想要显示的x坐标  y0:图片想要显示的y坐标
void fb_draw_picture5(unsigned int x0,unsigned int y0,unsigned char* pdata)
{
    //unsigned char* pdata=gImage_320480;//指针指向图像数组
    unsigned int x,y,cnt1;
    unsigned int cnt2=0;

 //图片的分辨率是320*480
    for(y=y0;y<480+y0;y++)
    {
	for(x=x0;x<320+x0;x++)
	{
             cnt1=y*WIDTH+x;//取出当前屏幕像素点编号   
             cnt2=(y-y0)*320+(x-x0);	//取出当前图片像素点编号  

   	 *(pfb+cnt1)=((pdata[cnt2*3+2]<<0)|(pdata[cnt2*3+1]<<8)|(pdata[cnt2*3+0]<<16));这里的像素矩阵和cnt有线性关系,所以可以这样写
	/*左值考虑当前像素点在fb中的偏移量*/
      /*右值考虑当前像素点在图像数据数组的下标(这里是三个为一个元素的数组的下标,因此上面会*3)*/

	}
    }
}

算法2:(因为每循环一次,+3)

//测试在屏幕指定坐标显示图片的位置,和fb_draw_picture5函数一样的功能,只是实现算法不一样,更加简单
//x0:图片想要显示的x坐标  y0:图片想要显示的y坐标
void fb_draw_picture6(unsigned int x0,unsigned int y0,unsigned char* pdata)
{
    //unsigned char* pdata=gImage_320480;
    unsigned int x,y,cnt;
    unsigned int a=0;

//图片的分辨率是320*480
    for(y=y0;y<480+y0;y++)
    {
	for(x=x0;x<320+x0;x++)
		{
                  cnt=y*WIDTH+x;/*cnt始终都是framebuff像素点的编号*/
      		     *(pfb+cnt)=((pdata[a+0]<<16)|(pdata[a+1]<<8)|(pdata[a+2]<<0));
		     a+=3;/*由于空缺一部分,像素点编号与像素点矩阵不存在倍数关系了,此时应该三个三个地传送进来*/
                         
		}
	}
}

在主函数中,调用上面两种不同算法实现的任意起点位置图片显示函数

int main()
{
int ret=-1;
	printf("image player demo........star \n");
	//第一步,打开设备
	ret = fb_open();
	if(ret < 0)
	    {
			perror("fb_open error.\n");
			return -1;
		}
  
fb_draw_badk(WIDTH, HEIGHT, RED);//调用背景填充函数
fb_draw_picture6((1024-320)/2,(600-480)/2,gImage_320480);//居中显示该图片
fb_close();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jmpwWQkS-1570028293264)(E33B3E0C2EBC41DDAAFAB3FBCB3BF3D2)]

2、起点导致图片超出屏幕外

(1)现象
在主函数中调用任意起点位置图片显示函数:
fb_draw_picture5(900,100);//图片大小超出屏幕范围测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xSoDa7N4-1570028293265)(979CE78FD41744F2B7D0CE7C59E6EB29)]

  • 左右超出去,会在相反方向补全:这是内部for循环可能超过1024的界定(但没有超出fb的大小)造成的。
  • 上下超出去,则会消失:因为双缓冲进到了另一帧。如果没有双缓冲,则会内存溢出。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mX0sz4KF-1570028293265)(042BB86735124F09B09E687683944174)]

(2)修改代码,使得超出部分不再显示。
算法1:

//x和y两个方向超出屏幕外的部分都不显示
void fb_draw_picture7(unsigned int x0,unsigned int y0, unsigned char* pdata)
{
    //unsigned char* pdata=gImage_320480;
    unsigned int x,y,cnt;
    unsigned int a=0;

//图片的分辨率是320*480
    for(y=y0;y<480+y0;y++)
    {
    		if(y>=HEIGHT)//y方向超出
	    {
		 a+=3;
		 break;//最后一行已经显示了,剩下的不用考虑了
	     }

	for(x=x0;x<320+x0;x++)
		{

			if(x>=WIDTH)//x方向超出了
			 {
			      a+=3;
			    continue;//仅结束本次循环
			  }		  
			
                  cnt=y*WIDTH+x;/*cnt始终都是framebuff像素点的编号*/
      		     *(pfb+cnt)=((pdata[a+0]<<16)|(pdata[a+1]<<8)|(pdata[a+2]<<0));
		     a+=3;/*由于空缺一部分,像素点编号与像素点矩阵不存在倍数关系了,此时应该三个三个地传送进来*/
                         
		}
	}
}

算法2:

//x和y两个方向超出屏幕外的部分都不显示 和fb_draw_picture7一样的功能 实现算法不一样
void fb_draw_picture8(unsigned int x0,unsigned int y0, unsigned char* pdata)
{
   // unsigned char* pdata=gImage_320480;//指针指向图像数组
    unsigned int x,y,cnt1;
    unsigned int cnt2=0;

 //图片的分辨率是320*480
    for(y=y0;y<480+y0;y++)
    {
	    		if(y>=HEIGHT)//y方向超出
		    {
			 break;//最后一行已经显示了,剩下的不用考虑了
		     }
	for(x=x0;x<320+x0;x++)
	{
			if(x>=WIDTH)//x方向超出了
			 {
			    continue;//仅结束本次循环
			  }	
             cnt1=y*WIDTH+x;//取出当前屏幕像素点编号   
             cnt2=(y-y0)*320+(x-x0);	//取出当前图片像素点编号  

   	 *(pfb+cnt1)=((pdata[cnt2*3+2]<<0)|(pdata[cnt2*3+1]<<8)|(pdata[cnt2*3+0]<<16));这里的像素矩阵和cnt有线性关系,所以可以这样写
	/*左值考虑当前像素点在fb中的偏移量*/
      /*右值考虑当前像素点在图像数据数组的下标(这里是三个为一个元素的数组的下标,因此上面会*3)*/

	}
    }
}

在主函数中调用该函数:

//fb_draw_picture7(900,100);
	fb_draw_picture8(900,100,gImage_320480);//图片大小超出屏幕范围测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tn3LV1BO-1570028293266)(682FEBF34B51492B922EC770ED1D7FC1)]

七、BMP图片的显示

1、图片文件的本质

(1)图片文件是二进制文件。
文件分两种,即二进制文件、文本文件;

(2)不同格式的图片文件的差别。
图片被压缩与否的区别。

(3)BMP图片的基本特征
未被压缩的元素位图格式(bit map)。

2、BMP图片详解

(1)如何识别BMP文件?
每种图片格式都有定义好的一种识别方法,BMP图片特征是以0x424D开头;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0cOqRkx3-1570028293267)(0078B254880F41FA8EA756D6AEF94BA2)]

(2)BMP文件组成
头信息+有效信息

3、BMP文件头信息、图片有效数据区

以下对BMP文件的测试都是参考这里:https://blog.csdn.net/oqqHuTu12345678/article/details/78862613

(1)文件大小

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RYjtiQur-1570028293268)(F124EA63B0BC497AB887E31665689473)]

(2)有效数据开始的位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kojTUOTh-1570028293292)(4030384155844CF2AB82D92A32C1EC27)]

(3)信息头的大小:40个字节

(4)分辨率

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QIiMZfWn-1570028293293)(88E96E617AF2439C82B9D8C3B68FCE86)]

(5)24位真彩色

4、写代码解析BMP图片

  • 第一步:打开BMP图片(并将BMP文件放入工程中)
  • 第二步:判断图片格式是否真是BMP
  • 第三步:解析头信息,得到该BMP图片的详细信息
  • 第四步:根据第三步得到的信息,去合适位置提取真正的有效图片信息
  • 第五步:将得到的有效数据丢到fb中去显示
  • 这样实际比较繁琐!使用结构体比较好!

①.新建Fb_bmp.c文件,写入程序如下:

//path是bmp图片的pathname
//该函数解析path图片,并将图片数据丢到bmp_buf中
//返回值错误时返回-1,正确返回0
int bmp_analyze(unsigned char *path)
{

	int fd=-1;
	unsigned char buf[54]={0};
	ssize_t ret=-1;

	
	//第一步:打开BMP图片
	fd=open(path,O_RDONLY);
	if(fd<0)
		{
			fprintf(stderr,"open %s error.\n",path);
			return -1;	 	
		}

	// 第二步: 读取文件头信息
	ret=read(fd,buf,54);//这里为什么是54,因为有效数据开始的位置地址为0x36=54
	if(ret!=54)
		{
			fprintf(stderr,"read file header error.\n");
			return -1;	 	
		}
	
	// 解析头
	// 第三步: 判断是不是BMP图片
	if(buf[0]!='B'||buf[1]!='M')//BMP头肯定为BM
		{
			fprintf(stderr,"file %s is not a bmp picture.\n",path);
			return -1;
		}
 
	printf("file %s is a bmp picture....ok\n",path);
    
    // 第四步:
	printf("width is %d\n",*((unsigned int*)(buf+0x12)));//这里为什么是buf+0x12,因为0x12是位图的宽度
	printf("hith   is %d\n",*((unsigned int*)(buf+0x16)));//0x16是位图的高度
	close(fd);
	return 0;
}

②. 函数分析:这里为什么是54??&

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值