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

mac2022-06-30  23

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

文章目录

一、开始动手写代码1、Makefile介绍2、使用SI建立工程 二、framebuffer驱动基本操作代码①在SI工程中新建fb.c文件(保存在display文件夹下)②这里需要用到framebuffer显示图片 三、图片显示原理和实践1、图片显示原理2、图片点阵数据获取(1)Image2LCD软件提取 四、图片显示的高级话题1、RGB顺序调整2、显示函数的其他写法 五、任意分辨率大小图片显示(1)图片比屏幕分辨率大(2)图片大小比屏幕大小要小 六、任意起点位置图片显示1、小图片任意起点(但整个图片显示没有超出屏幕范围内)2、起点导致图片超出屏幕外 七、BMP图片的显示1、图片文件的本质2、BMP图片详解3、BMP文件头信息、图片有效数据区4、写代码解析BMP图片①.新建Fb_bmp.c文件,写入程序如下:②. 函数分析:这里为什么是54???③.主函数中调用该函数④.编译拷贝到跟文件系统,然后运行run.shu脚本,打印BMP信息正确。 5、用结构体方式解析BMP信息头①.在fb_bmp.h中添加一下结构体,此结构体是百度BMP信息头结构体得来②.将判断一个图片文件是不是一个合法的bmp文件从新封装为一个函数,然后在主函数中先调用该函数,判断是否为一个合法的bmp文件③.调用BMP头信息结构体,实现解析BMP图片数据,并将数据丢到FB中显示④.重新修改图片显示函数主函数调用: 八、及时规整1、再次强调规范问题2、为什么要规整项目?3、对本项目进行规整4、一些重构代码的技巧5、添加DEBUG宏以控制调试信息输出6、图片信息用结构体来封装传递①.主要是Fb_bmp.c中的bmp_analyze函数②.fb.c中的fb_draw函数主函数调用:开发板能正常显示图片,且终端打印信息正确。 九、jpg图片的显示原理分析1、认识jpg图片2、jpg图片如何显示3、如何解码jpg图片 十、libjpeg介绍及开源库的使用方法1、libjpeg介绍2、libjpeg版本及下载资源3、开源库的使用方法 十一、libjpeg的移植实战1、移植2、部署 十二、使用libjpeg解码显示jpg图片1、如何使用一个新的库2、libjpeg说明文档和示例代码3、结合说明文档来实践4、解读example.c和移植主函数调用该函数:5、代码问题6、部署动态库以使程序运行起来7、测试读取头信息主函数调用如下: 十三、解决解码显示中的问题1、问题分析及解决记录2、结束jpg图片部分主函数调用:测试: 十四、解码显示png图片1、思路分析2、libpng移植(1)下载源码包:(2)解压、配置、修改Makefile、编译、部署。注意实际路径。①解压:②配置:进入libpng-1.6.6目录下,运行一下命令 (3)配置出错,报错信息:configure: error: zlib not installed 3、zlib移植①下载zlib库,并拷贝到上面的/root/decodeporting/目录下,解压...这里不再赘述②导出CC以确定配置时为arm-linux-:③配置zlib库,得到makefile: 4、再次回到==》(2、libpng移植)配置环节①配置:进入libpng-1.6.6目录下,配置libpng,还是报错,②解决方案就是使用epport临时性的导出,在终端中依次输入以下命令:③导出后再次配置就过了,然后编译和安装④ make && make install 5、参考源码包自带的资料开始编程6、开始编程①先is_png函数使用判断一个图片文件是不是png图片 主函数调用:② int png_analyze解码图片函数的编写(上)③ int png_analyze解码图片函数的编写(下) 十五、目录扫描,图片文件的管理模块1、图片文件的管理2、图片信息的自动检索①使用readdir函数来读取文件夹下内容②使用lstat函数来实现读取文件夹下内容 十六、添加触摸翻页功能(前提是已经学习过触摸屏驱动相关)1、读取触摸坐标数据主函数调用:2、使用触摸坐标判断并执行翻页操作主函数调用该函数 十七、总结与回顾1、项目总结(1)项目描述:软硬件平台等(2)重点和难点(3)主要技术:1.1.驱动模块(驱动模块主要的任务就是进行Framebuffer地址映射)1.2.目录扫描,图片管理模块1.3.图片切换模块1.4.图片显示模块

一、开始动手写代码

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???

ret=read(fd,buf,54);//这里为什么是54,因为有效数据开始的位置地址为0x36=54

因为有效数据开始的位置地址为0x36=54 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4gWJOTD0-1570028293294)(41C67C8DFD754CA89F9D1BEC321CF639)]

为什么这里使用了buf+0x12??

printf("width is %d\n", *((unsigned int*)(buf+0x12)));//这里为什么是buf+0x12, 因为0x12是位图的宽度printf("hith is %d\n",*((unsigned int*)(buf+0x16)));//0x16是位图的高度

原因如下,这是在上面的博客地址中BMP头信息的地址 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cdIb2y69-1570028293295)(EB2DCC1F79B4439C9E2EDD771061DC7B)]

③.主函数中调用该函数

bmp_analyze("cute_pic.bmp");//必须保证在工程中已经存在该BMP文件

且修改dispaly子文件夹下的Makefile,包含Fb_bmp.c,不然编译肯定报错

obj-y += Fb_bmp.o

④.编译拷贝到跟文件系统,然后运行run.shu脚本,打印BMP信息正确。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pWkAAKht-1570028293296)(BC2C0AC96CA94264A9E629DA27D9D3C0)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SK6ebTeN-1570028293297)(ABC4C30F2F03460BBE73F7C8FC8D346E)]

5、用结构体方式解析BMP信息头

①.在fb_bmp.h中添加一下结构体,此结构体是百度BMP信息头结构体得来

typedef struct { unsigned char bfType[2];//文件类? unsigned long bfSize; //位图大小 unsigned short bfReserved1; //位0 unsigned short bfReserved2; //位0 unsigned long bfOffBits;//到数据偏移量 }__attribute__((packed))BitMapFileHeader;//使编译器不优化,其大小为14字节 //信息头结构体 typedef struct { unsigned long biSize;// BitMapFileHeader 字节数 long biWidth;//位图宽度 long biHeight;//位图高度,正位正向,反之为倒图 unsigned short biPlanes;//为目标设备说明位面数,其值将总是被设为1 unsigned short biBitCount;//说明比特数/象素,为1、4、8、16、24、或32。 unsigned long biCompression;//图象数据压缩的类型没有压缩的类型:BI_RGB unsigned long biSizeImage;//说明图象的大小,以字节为单位 long biXPelsPerMeter;//说明水平分辨率 long biYPelsPerMeter;//说明垂直分辨率 unsigned long biClrUsed;//说明位图实际使用的彩色表中的颜色索引数 unsigned long biClrImportant;//对图象显示有重要影响的索引数,0都重要。 } __attribute__((packed)) BitMapInfoHeader;

②.将判断一个图片文件是不是一个合法的bmp文件从新封装为一个函数,然后在主函数中先调用该函数,判断是否为一个合法的bmp文件

// 判断一个图片文件是不是一个合法的bmp文件 // 返回值: 如果是则返回0,不是则返回-1 int is_bmp(const char *path) {

int fd=-1; unsigned char buf[2]={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,2);//只读取文件头2个字节 if(ret!=2) { fprintf(stderr,"read file header error.\n"); ret = -1; goto close; } // 解析头 // 第三步: 判断是不是BMP图片 if(buf[0]!='B'||buf[1]!='M')//BMP头肯定为BM { fprintf(stderr,"file %s is not a bmp picture.\n",path); ret = -1; goto close; } else { ret=0; } printf("file %s is a bmp picture....ok\n",path);

close: close(fd); return ret; }

③.调用BMP头信息结构体,实现解析BMP图片数据,并将数据丢到FB中显示

//path是bmp图片的pathname //该函数解析path图片,并将图片数据丢到bmp_buf中 //返回值错误时返回-1,正确返回0 int bmp_analyze(unsigned char *path) { int fd=-1; BitMapFileHeader fheader; BitMapInfoHeader info; ssize_t ret=-1; int i=0; //第一步:打开BMP图片 fd=open(path,O_RDONLY); if(fd<0) { fprintf(stderr,"open %s error.\n",path); return -1; } // 第二步: 读取文件头信息 ret=read(fd,&fheader,sizeof(fheader)); printf("bfsize =%d.\n",fheader.bfSize);//位图大小 printf("boffsize =%d.\n",fheader.bfOffBits);//有效信息偏移量 #if 0 for( i=0;i<sizeof(fheader);i++)//测试打印出的字节数据是否一致 { printf("%x ",*((unsigned char *)&fheader+i)); } printf("\n"); #endif ret= read(fd,&info,sizeof(info));/*会沿着继续读下去*/ printf("picture resolution : %d * %d.\n",info.biHeight,info.biWidth);//读取位图宽度和高度 printf("picture bpp : %d \n",info.biBitCount);//像素比特数 // 第三步: 读取图片有效信息 // 先把文件指针移动到有效信息的偏移量处 //然后读出图片大小=info.biHeight*info.biWidth*info.biBitCount/8个字节,这个公式在驱动frambuff有说明 lseek(fd,fheader.bfOffBits,SEEK_SET);//从文件开始的位置开始 unsigned long len=info.biHeight*info.biWidth*info.biBitCount /8; printf("len put to buff :%ld.\n",len); read(fd,BMP_BUF_SIZE,len); //把内容丢到fb中去显示 fb_draw(BMP_BUF_SIZE);//用于测试使用结构体来解析图片数据时,图片会180°显示后还原显示函数 /*数据和使用image2lcd获取的不一样 *这里会旋转180度颠倒 *即第一个像素放到最后一个像素点像素,依次类推了 */ close(fd); return 0; }

====> 细节:上面的这段代码用于测试打印出的像素数据是否一致

#if 1 for( i=0;i<sizeof(fheader);i++)//测试打印出的字节数据是否一致 { printf("%x ",*((unsigned char *)&fheader+i)); } printf("\n"); #endif

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

④.重新修改图片显示函数

注意:

使用结构体来解析图片数据时,和使用image2lcd不一样。这里会旋转180度颠倒,即第一个像素放到最后一个像素点像素,依次类推了。因此在fb_draw()函数中,需要将最后一个像素点数据放在第一个像素处显示,以此类推。 //用于测试使用结构体来解析图片数据时,图片会180°显示后还原显示函数 void fb_draw(unsigned char* pdata) { //unsigned char* pdata=gImage_320480; unsigned int x,y,cnt; unsigned int a=0; /*数据和使用image2lcd获取的不一样 这里会旋转180度颠倒 即第一个像素放到最后一个像素点像素,依次类推了*/ a= HEIGHT*WIDTH*3-3;//让a一开始就指向了最后一个像素的第一个字节 for(y=0;y<HEIGHT;y++) { for(x=0;x<WIDTH;x++) { cnt=y*WIDTH+x;/*cnt始终都是framebuff像素点的编号*/ *(pfb+cnt)=((pdata[a+0]<<16)|(pdata[a+1]<<8)|(pdata[a+2]<<0)); a-=3;/*由于空缺一部分,像素点编号与像素点矩阵不存在倍数关系了,此时应该三个三个地传送进来*/ } } }

主函数调用:

is_bmp("cute_pic.bmp"); bmp_analyze("cute_pic.bmp"); smen_len = xres_virtual * yres_virtual * bpp / 8

若调用之前没有修改的显示图片函数,则会180°倒置 如下:

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

调用上面修改之后的fb_draw函数,则会正常显示如下:”

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

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

八、及时规整

1、再次强调规范问题

(1)函数、变量起名字要合法合理;小写函数;(2)要写注释;第一步,第二步……可以先写注释再写代码;(3)函数长短要合适;(4)多文件组织,每个东西丢到合理的位置

2、为什么要规整项目?

(1)完全自由写项目时不可能一步到位,只能先重内容和功能,后补条理和规范;(2)规整的过程也是一个梳理逻辑和分析架构的过程。

3、对本项目进行规整

(1)去掉测试显示时头文件形式提供的图片显示相关的东西

即去除fb_draw_picture_n()测试函数,因为我们的最终目的是解析bmp格式图片(已经实现了,因此要删除之前的测试函数)

4、一些重构代码的技巧

(1)用#if 0 #endif来屏幕不需要的代码,不要用/* */;(2)暂时不要的代码先不要删除,而是屏幕掉

5、添加DEBUG宏以控制调试信息输出

debug宏添加好后,要使能输出可以有2种方式:

第一种:在debug宏定义之前定义DEBUG宏。

#define DEBUG //打开调试信息输出的开关 // debug宏的定义 #ifdef DEBUG #define debug(...) \ { \ fprintf(stderr, "[debug][%s:%s:%d] ", \ __FILE__, __FUNCTION__, __LINE__); \ fprintf(stderr, __VA_ARGS__); \ } #else #define debug(...) #endif

第二种:在编译参数中添加-DDEBUG编译选项。(在makefile中添加)

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

6、图片信息用结构体来封装传递

//封装图片各种信息

//封装图片各种信息 typedef struct pic_info { char* pathname;//路径和文件名字 unsigned int width;//位图宽度 unsigned int height;//位图高度 unsigned int bpp;//位图bpp char *pData;//指向图片有效数据存储的buff }pic_info;

从而需要修改相关代码。

①.主要是Fb_bmp.c中的bmp_analyze函数

//path是bmp图片的pathname //该函数解析path图片,并将图片数据丢到bmp_buf中 //返回值错误时返回-1,正确返回0 int bmp_analyze(struct pic_info *pPic) { int fd=-1; BitMapFileHeader fheader; BitMapInfoHeader info; ssize_t ret=-1; unsigned long len; //int i=0; //第一步:打开BMP图片 fd=open(pPic->pathname,O_RDONLY); if(fd<0) { fprintf(stderr,"open %s error.\n",pPic->pathname); return -1; } // 第二步: 读取文件头信息 ret=read(fd,&fheader,sizeof(fheader)); debug("bfsize =%ld.\n",fheader.bfSize);//位图大小 debug("boffsize =%ld.\n",fheader.bfOffBits);//有效信息偏移量 #if 0 for( i=0;i<sizeof(fheader);i++)//测试打印出的字节数据是否一致 { printf("%x ",*((unsigned char *)&fheader+i)); } printf("\n"); #endif ret= read(fd,&info,sizeof(info));/*会沿着继续读下去*/ debug("picture resolution : %ld * %ld.\n",info.biHeight,info.biWidth);//读取位图宽度和高度 debug("picture bpp : %hu \n",info.biBitCount);//像素比特数 pPic->width = info.biWidth;//填充图片的各个数据 pPic->height =info.biHeight; pPic->bpp = info.biBitCount;// 利用输出型参数 debug("image resolution: %d * %d, bpp=%d.\n", pPic->width, pPic->height, pPic->bpp); // 第三步: 读取图片有效信息 // 先把文件指针移动到有效信息的偏移量处 //然后读出图片大小=info.biHeight*info.biWidth*info.biBitCount/8个字节,这个公式在驱动frambuff有说明 lseek(fd,fheader.bfOffBits,SEEK_SET);//从文件开始的位置开始 len=info.biHeight * info.biWidth * info.biBitCount /8; debug("len put to buff :%ld.\n",len); read(fd,bmp_buf,len); pPic->pData = bmp_buf; //把内容丢到fb中去显示 fb_draw(pPic);//用于测试使用结构体来解析图片数据时,图片会180°显示后还原显示函数 close(fd); return 0; }

②.fb.c中的fb_draw函数

//用于测试使用结构体来解析图片数据时,图片会180°显示后还原显示函数 void fb_draw(struct pic_info *pPic) { char* pData=pPic->pData; //指针指向图像数组 //const char *pData = (const char *)pPic->pData; unsigned int x,y,cnt; unsigned int a=0; debug("image resolution: %d * %d, bpp=%d.\n", pPic->width, pPic->height, pPic->bpp); if((pPic->bpp !=24)) { fprintf(stderr,"bpp %s is not support.\n",pPic->bpp); return -1; } /*数据和使用image2lcd获取的不一样 这里会旋转180度颠倒 即第一个像素放到最后一个像素点像素,依次类推了*/ a = pPic->height * pPic->width * 3 - 3;//让a一开始就指向了最后一个像素的第一个字节 for (y=0; y<(pPic->height); y++) { for (x=0; x<(pPic->width); x++) { cnt = WIDTH * y + x;/*cnt始终都是framebuff像素点的编号,注意这里的WIDTH是屏幕的高度*/ *(pfb+cnt)=((pData[a+0]<<16)|(pData[a+1]<<8)|(pData[a+2]<<0)); a-=3;/*由于空缺一部分,像素点编号与像素点矩阵不存在倍数关系了,此时应该三个三个地传送进来*/ } } } } }}

主函数调用:开发板能正常显示图片,且终端打印信息正确。

struct pic_info pictrue;//要显示的图片 // 测试bmp图片显示,ok pictrue.pathname = "cute_pic.bmp";// 指向要显示的图片 bmp_analyze(&pictrue);

在这个过程中遇到的问题:一定要多用DEBUG宏以控制调试信息输出,例如我遇到了刚开始段错误以及图片不正常显示等问题,都是跟踪debug宏输出调试信息解决:截图如下: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q7suSW5M-1570028295463)(A55015739417449E9DA6413A53AF013D)]

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

九、jpg图片的显示原理分析

1、认识jpg图片

(1)属于二进制文件。 (2)有其固定的识别特征,后面我们判断是否为jpg图片,需要识别特征http://www.cnblogs.com/Wendy_Yu/archive/2011/12/27/2303118.html

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

(3)是经过压缩的图片格式。

2、jpg图片如何显示

(1)jpg图片中的二进制数并不对应像素数据。

(2)LCD显示器的接口仍然是framebuffer。

(3)要显示jpg图片必须先解码jpg得到相应的位图数据。

3、如何解码jpg图片

(1)图片编码和解码对应着压缩和解压缩过程

(2)编码和解码其实就是一些数学运算(压缩度、算法复杂度、时间、清晰度)

(3)软件编解码和硬件编解码需要频繁进行编解码的话,一般使用硬件编解码。

(4)不同的图片格式其实就是编解码的算法不同,结果是图片特征不同

(5)编程实战:使用开源编解码库

十、libjpeg介绍及开源库的使用方法

1、libjpeg介绍

(1)基于linux的开源软件;

(2)C语言编写(gcc、使用Makefile管理);

(3)提供JPEG图片的编解码算法实现;

2、libjpeg版本及下载资源

(1)经典版本v6b:https://sourceforge.net/projects/libjpeg/files/libjpeg/6b/

(2)最新版本v9b:http://www.ijg.org/

3、开源库的使用方法

(1)移植(源码下载、解压、配置、修改Makefile、编译或交叉编译)

移植的目的是由源码得到三个东西:动态库.so,静态库.a,头文件.h。 (2)部署(部署动态库so、部署静态库.a和头文件.h)动态库是程序在运行时才需要的,编译程序时不需要。静态库是静态连接时才需要,动态链接时不需要。头文件.h是在编译程序时使用的,运行时不需要的。

总结:静态库和头文件,是在编译链接过程中需要的,因此要把静态库.a文件和头文件.h文件放到ubuntu的文件系统中。 动态库是在运行时需要的,所以动态库so文件要放到开发板的文件系统中(放的过程就叫部署)。

(3)注意三个编译链接选项:-I -l(小L) -L 举例如-I:

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

-I 是编译选项(准确的是说是makefile预处理选项CFLAGS或者CPPFLAGS中指定),用来指定预处理时查找头文件的范围的。-l(小写L)是链接选项(LDFLAGS中指定),用来指定链接额外的库的名字(譬如我们用到了数学函数,就用-lm,链接器就会去链接libm.so;那么我们使用了libjpeg,对应的库名字就叫libjpeg.so,就需要用-ljpeg选项去链接)。-L是链接选项(LDFLAGS中指定),用来指定链接器到哪个路径下面去找动态链接库。

总结:-l(小写L)是告诉链接器要链接的动态库的名字,而-L是告诉链接器要链接的动态库的路径。

十一、libjpeg的移植实战

1、移植

(1)源码下载、解压至/root/decodeporting/目录下。

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

(2)编译前的配置(分析configure文件的usage、借鉴前辈的设置)

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

得到应该在/root/decodeporting/jpeg-6b目录下执行下面命令: ./configure --prefix=/opt/libcode --exec-prefix=/opt/libcode --enable-shared --enable-static-build=i386 -host=arm

说明:/opt/libcode:用于存放移植后得到的3个文件,所以我们需要先建立这个目录

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

指定编译结果放置的目录(如果没有则需要先建立相应的目录) 配置完成后生成了makefile文件,通过查看知道需要在/opt/libcode中创建include、bin、lib目录。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ssiiMTdD-1570028293309)(0FA0FF963EA94C1DAF84E2AF8FFB634D)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-00fVttTK-1570028293310)(8E4BBCC6A90B4D50BF14DD9FD7825A5F)]

(3)Makefile检查,主要查看交叉编译设置是否正确

CC=gcc 改为 CC=arm-linux-gcc //编译器,如果不改,只能在ubuntu使用,不能在开发板使用。 AR=ar rc 改为 AR=arm-linux-ar rc AR2=ranlib 改为 AR2=arm-linux-ranlib

(4)编译:执行make命令。 (5)安装:执行make install-lib; 安装就是将编译生成的库文件、头文件、可执行文件分别装载到–prefix --exec-prefix所指定的那些目录中去。

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

2、部署

前面只是完成移植,相关文件都在ubuntu上。我们要把动态链接库(.so文件)放到开发板的根文件系统中(/root/porting_x210/rootfs/rootfs),可以考虑三者之一:

第一个:/root/porting_x210/rootfs/rootfs/lib

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

第二个:/root/porting_x210/rootfs/rootfs/usr/lib第三个:任意指定目录 (前两个在程序运行时,可以自动找到。后面任意目录时,需要在程序中指定) 我们选择第二个,然后将生成的动态.so文件拷贝到/usr/lib目录下

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

十二、使用libjpeg解码显示jpg图片

1、如何使用一个新的库

(1)思路一:网络上找别人使用过后写的文档、博客等作为参考。 (2)思路二:看库源码自带的文档(说明文档和示例代码)。我们从思路二出发。

2、libjpeg说明文档和示例代码

(1)说明文档:README和libjpeg.doc (2)示例代码:example.c

3、结合说明文档来实践

example.c测试程序提供了两个接口:

写jpeg文件 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mR27RL8z-1570028293314)(90878E2F277C49A5835043BC095FF187)]

读jpeg文件 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-weJUH05t-1570028293314)(F546B1326276447E9F88F109D10587FC)]

另外,还定义了几个外部变量和一个外部函数:

extern JSAMPLE * image_buffer; extern int image_height; extern int image_width; void put_scanline_someplace(char *buf, int count);

这个函数就是在读jpeg文件时,扫描一行的数据通过这个函数传出

4、解读example.c和移植

解读example.c根据example.c的步骤移植添加到我们之前的工程中 建立fb_jpeg.c文件,并根据example.c读jpeg文件源码开始,移植到我们的.c文件,如下: /* * 本文件用来解码jpg图片,并调用fb.c中的显示接口来显示到lcd上 */ #include <stdio.h> #include <config.h> // for debug #include <fb_jpeg.h> #include <jpeglib.h> #include <jerror.h> #include <string.h> struct my_error_mgr { struct jpeg_error_mgr pub; /* "public" fields */ // jmp_buf setjmp_buffer; /* for return to caller */ }; typedef struct my_error_mgr * my_error_ptr; // 自己定义的错误处理函数 METHODDEF(void)my_error_exit (j_common_ptr cinfo) { //my_error_ptr myerr = (my_error_ptr) cinfo->err; //(*cinfo->err->output_message) (cinfo); fprintf(stderr, "my_error_exit\n"); //longjmp(myerr->setjmp_buffer, 1); } /* * 函数功能: 解码jpg图片,并将解码出来的数据存储 * 函数参数: pPIC,记录源jpg图片,解码出来的图片宽高、图片数据缓冲区等信息 * 返回值 : 成功解码则返回0,失败则返回-1 */ int jpg_analyze(struct pic_info *pPic) { struct jpeg_decompress_struct cinfo; // cinfo贯穿整个解码过程的信息记录和传递的数据结构 struct my_error_mgr jerr; // 错误处理的 //JSAMPARRAY buffer = NULL; // 指向解码行数据的指针 char * buffer = NULL; FILE * infile; // 指向fopen打开源jpg图片文件的指针 int row_stride; // 解码出来的一行图片信息的字节数 if ((infile = fopen(pPic->pathname, "rb")) == NULL) { fprintf(stderr, "can't open %s\n", pPic->pathname); return -1; } // 第1步: 错误处理函数部分的绑定 cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; // 给解码器做必要的内存分配和数据结构的初始化 jpeg_create_decompress(&cinfo); // 第2步: 将fopen打开的源jpg图片和解码器相关联 jpeg_stdio_src(&cinfo, infile); // 第3步: 读jpg文件头 jpeg_read_header(&cinfo, TRUE); //打印出图片的宽度高度信息等 debug("image resolution: %d * %d, bpp/8=%d.\n", cinfo.output_width, cinfo.output_height, cinfo.output_components); // 解码完了,做各种清理工作 jpeg_destroy_decompress(&cinfo); fclose(infile); return 0; }

主函数调用该函数:

pictrue.pathname = "cute_pic.jpg";// 指向要显示的图片 jpg_analyze(&pictrue);

5、代码问题

(1)测试代码,先试图读取jpg图片头信息。 (2)问题排除

编译时问题:主要就是头文件包含,除了在代码中包含头文件外,还要注意指明头文件的路径。因为尽管包含了头文件,但只会在当前的目录和path指定的路径中寻找。我们应该在总Makefile中指定。

注意-I、-l、-L三个编译链接选项的使用

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

重新make&make cp,然后启动开发板,运行。run.sh脚本,能正常打印信息,但当我们把lib目录下的动态链接文件删除后,从新运行如下,显示错误。

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

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

6、部署动态库以使程序运行起来

(1)一般放到开发板根文件系统/lib或者/usr/lib下

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

这样不需要给系统指定库路径,就能自动找到。强调一下是开发板根文件系统下的路径,千万不要弄成了ubuntu的根文件系统下的目录。

(2)放到自定义的第三方的目录 (在开发板根文件中新建/opt/mylib/目录,用于存放动态库)

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

将该自定义第三方目录导出到环境变量LD_LIBRARY_PATH下即可。

export LD_LIBRARY_PATH=/opt/mylib:$LD_LIBRARY_PATH

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

可以使用echo $LD_LIBRARY_PATH查看当前的路径环境变量包含哪些路径。可以把上述操作写进到run.sh脚本文件中,省得每次都要这样操作。

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

7、测试读取头信息

在fb_jpeg.c文件中的jpg_analyze函数中继续添加源码如下(注意,都是根据example.c的步骤添加修改的):

// 第1步: 错误处理函数部分的绑定 cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; // 给解码器做必要的内存分配和数据结构的初始化 jpeg_create_decompress(&cinfo); // 第2步: 将fopen打开的源jpg图片和解码器相关联 jpeg_stdio_src(&cinfo, infile); // 第3步: 读jpg文件头 jpeg_read_header(&cinfo, TRUE); // 第4步: 启动解码器 jpeg_start_decompress(&cinfo); debug("image resolution: %d * %d, bpp/8=%d.\n", cinfo.output_width, cinfo.output_height, cinfo.output_components); // 解码出来的数据一行的字节数 row_stride = cinfo.output_width * cinfo.output_components; buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); if (NULL == buffer) { fprintf(stderr, "cinfo.mem->alloc_sarray error.\n"); return -1; } // 第5步: 逐行解码,并将解码出的数据丢到事先准备好的缓冲区去 while (cinfo.output_scanline < cinfo.output_height) { // 解码一行信息,并且丢到buffer中 jpeg_read_scanlines(&cinfo, buffer, 1); // 将buffer中这一行数据移走到别的地方去暂存或者使用,总之是要腾出buffer空间 // 来给循环的下一次解码一行来使用 memcpy(pPic->pData + (cinfo.output_scanline-1) * row_stride, buffer, row_stride); } // 第6步: 解码完了,做各种清理工作 jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(infile); //显示该图片,先填充pPic的数据 pPic->width = cinfo.output_width; pPic->height = cinfo.output_height; pPic->bpp = cinfo.output_components * 8; fb_draw(pPic);

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

主函数调用如下:

对pictrue.pData 缓存区必须先指定填充大小,

// ②测试jpg图片显示 pictrue.pathname = "cute_pic.jpg";// 指向要显示的图片 pictrue.pData = bmp_buf; jpg_analyze(&pictrue);

从新make&&make cp.,运行脚本,开发板显示图片有问题。

十三、解决解码显示中的问题

1、问题分析及解决记录

(1)根据LCD错误的显示状态,分析有可能是显示函数fb_draw中的图片宽高数据有误,于是在fb_draw函数中添加debug打印出宽和高来。结果发现是对的。

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

(2)显示函数中的图片宽高和fb宽高都是对的,结果显示时还是只有一溜(其余位置黑屏),可能的一个原因就是:显示数据本身不对,很多都是0。

如何验证?只要把显示数据打印出来看一看就知道了。 在fb.c中的fb_draw显示函数中添加如下打印信息:

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

结果发现打印出的待显示数据是很多0,说明显示函数的待显示数据就是错的。

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

(3)这些待显示数据为什么会错?

第一种可能性就是libjpeg解码出来的数据就是错的;第二种可能性是解码一行出来暂存到buffer的时候,或者memcpy从暂存的buffer拿出来给pData指向的空间的时候给搞错了。相对来说第二种很好验证而第一种不好验证。只需要在jpeg_read_scanlines函数后面直接打印显示解码出来的一行数据,就可以知道是不是第二种情况。

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

结果打印出来好多0,说明是第一种情况。

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

(4)截至目前已经锁定问题,就是jpeg_read_scanlines解码出来的数据本身就不对。 (5)可能的问题

有可能是libjpeg本身就有问题;有可能我们对libjpeg的部署不对导致他工作不对;有可能我们写的代码不对,也就是说我们没用正确的方法来使用libjpeg。

(6)没有思路怎么办

方法一:去网上找一些别人写的libjpeg解码显示图片的示例代码,多看几个,对着和我们的关键部位对比,寻找思路。方法二:如果在网上找不到相关资料,这时候就只有硬着头皮去看源码了。譬如去libjpeg的源码中查看:jpeg_read_scanlines、cinfo.mem->alloc_sarray等。怎么解决的???? 在jpg_analyze函数中修改如下源码,即可解决(即下面标红的,就是修改后的) int jpg_analyze(struct pic_info *pPic) { ....... //JSAMPARRAY buffer = NULL; // 指向解码行数据的指针 这是原生的 char * buffer = NULL; ............................ // 第4步: 启动解码器 jpeg_start_decompress(&cinfo); debug("image resolution: %d * %d, bpp/8=%d.\n", cinfo.output_width, cinfo.output_height, cinfo.output_components); // 解码出来的数据一行的字节数 row_stride = cinfo.output_width * cinfo.output_components; /*按照提供的example.c,这里这样使用就是有问题,所以解码出来的数据全是0,故我们选择下面另一种方法,自己申请空间*/ //buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); buffer = (char *)malloc(row_stride); if (NULL == buffer) { fprintf(stderr, "cinfo.mem->alloc_sarray error.\n"); return -1; } // 第5步: 逐行解码,并将解码出的数据丢到事先准备好的缓冲区去 while (cinfo.output_scanline < cinfo.output_height) { // 解码一行信息,并且丢到buffer中 //jpeg_read_scanlines(&cinfo, buffer, 1); jpeg_read_scanlines(&cinfo, &buffer, 1); ............. }

编译拷贝,重启开发板运行,就能显示图片了(但图片是翻过来,还有RGB不对) 。

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

(7)解决了buffer申请导致的问题之后,我们再来解决2个遗留的问题

一个就是RGB顺序问题,另一个是图像转了180度的问题。 void fb_draw2(const struct pic_info *pPic) { const char *pData = (const char *)pPic->pData; // 指针指向图像数组 unsigned int cnt = 0, a = 0; unsigned int x, y; if ((pPic->bpp != 32) && (pPic->bpp != 24)) { fprintf(stderr, "BPP %d is not support.\n", pPic->bpp); return; } a = 0; for (y=0; y<pPic->height; y++) { for (x=0; x<pPic->width; x++) { //cnt表示当前像素点的编号 cnt = WIDTH * y + x; // 当前像素点对应的图像数据的RGB就应该分别是: // pData[cnt+0] pData[cnt+1] pData[cnt+2] // 当前像素点的数据 *(pfb + cnt) = ((pData[a+2]<<0) | (pData[a+1]<<8)| (pData[a+0]<<16)); //*p = ((pData[cnt+0]<<16) | (pData[cnt+1]<<8)| (pData[cnt+2]<<0)); a += 3; } } }

(8)添加了fb_draw2函数并且调用后,2个遗留问题彻底解决。至此,jpg图片显示完美实现。

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

2、结束jpg图片部分

(1)加上jpg图片格式识别:判断开头和结尾的特征字节。

在fb.jipeg.c中添加is_jpg函数,用于判断一个图片文件是不是jpg图片

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

// 函数功能: 判断一个图片文件是不是jpg图片 // 函数参数: path是图片文件的pathname // 返回值: 如果是jpg则返回0,不是则返回1,错误返回-1 int is_jpg(const char *path) { FILE *file = NULL; char buf[2] = {0}; // 打开文件 file = fopen(path, "rb"); if (NULL == file) { fprintf(stderr, "fopen %s error.\n", path); fclose(file); return -1; } // 读出前2个字节 fread(buf, 1, 2, file); debug("read: 0x%x%x\n", buf[0], buf[1]); // 判断是不是0xffd8 if (!((buf[0] == 0xff) && (buf[1] == 0xd8))) { fclose(file); return 1; // 不是jpg图片 } // 是0xffd8开头,就继续 // 文件指针移动到倒数2个字符的位置 fseek(file, -2, SEEK_END); // 读出2个字节 fread(buf, 1, 2, file); debug("read: 0x%x%x\n", buf[0], buf[1]); // 判断是不是0x if (!((buf[0] == 0xd9) && (buf[1] == 0xa))) { fclose(file); return 1; // 不是jpg图片 } fclose(file); return 0; } 测试该函数是否正确。

主函数调用:

// ②测试jpg图片显示 ret=is_jpg("cute_pic.jpg"); printf("ret = %d.\n",ret); pictrue.pathname = "cute_pic.jpg";// 指向要显示的图片 pictrue.pData = bmp_buf; jpg_analyze(&pictrue);

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

(2)对外封装好用的jpg图片显示函数

// 封装的一个对外使用的jpg显示函数 // 本函数对外只需要一个jpg图片的pathname即可,那些复杂的数据结构都是jpg显示模块内部处理的 // 正确显示图片返回0,显示过程中出错则返回-1 int display_jpg(const char *pathname) { int ret = -1; struct pic_info picture; // 第一步: 检测给的图片是不是jpg图片 ret = is_jpg(pathname); if (ret != 0) { return -1; printf("ret = %d.\n",ret); } printf("ret = %d.\n",ret); // 第二步: 解析该jpg图片 picture.pathname = pathname; picture.pData = rgb_buf; jpg_analyze(&picture); // 第三步: 显示该jpg图片 fb_draw2(&picture); }

(3)对外封装好用的bmp图片显示函数

// 封装的一个对外使用的bmp显示函数 // 本函数对外只需要一个bmp图片的pathname即可,那些复杂的数据结构都是bmp显示模块内部处理的 // 正确显示图片返回0,显示过程中出错则返回-1 int display_bmp(const char *pathname) { int ret = -1; struct pic_info picture; // 第一步: 检测给的图片是不是jpg图片 ret = is_bmp(pathname); if (ret != 0) { return -1; printf("ret = %d.\n",ret); } printf("ret = %d.\n",ret); // 第二步: 显示该jpg图片 picture.pathname = pathname; picture.pData = rgb_buf; bmp_analyze(&picture); }

测试:

主函数调用:编译拷贝,重启运行开发板,开发板图片正常显示,先显示jpg图片,3s后显示bmp图片,完美解决所以小问题。

// ③测试新的显示接口函数jpg图片显示和bmp图片显示 display_jpg("cute_pic2.jpg"); sleep(3); display_bmp("cute_pic.bmp");

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

十四、解码显示png图片

1、思路分析

(1)png更像是jpg而不像是bmp; (2)png和jpg都是压缩格式的图片,都是二进制文件,不同之处是压缩和解压缩的算法不同。 (3)通过libjpeg来编解码jpg图片,那么同样有一个libpng用来编解码png图片。 (4)工作思路和顺序 找到并移植并部署libpng,然后查readme和其他文档示例代码等来使用libpng提供的API来对png图片进行解码,并将解码出来的数据丢到framebuffer中去显示。

2、libpng移植

(1)下载源码包:

(2)解压、配置、修改Makefile、编译、部署。注意实际路径。

①解压:

root@wwj:~/decodeporting# cp /mnt/hgfs/Linux/winshare/x210kernel/tupian_project/libpng-1.6.6.tar.gz ./ root@wwj:~/decodeporting# tar -zxvf libpng-1.6.6.tar.gz

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

②配置:进入libpng-1.6.6目录下,运行一下命令

./configure --host=arm-linux --enable-shared --enable-static --prefix=/opt/libcode

(3)配置出错,报错信息:configure: error: zlib not installed

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

分析问题是因为libpng依赖于zlib库,所以要先移植zlib库才可以。

3、zlib移植

①下载zlib库,并拷贝到上面的/root/decodeporting/目录下,解压…这里不再赘述

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

②导出CC以确定配置时为arm-linux-:

root@wwj:~/decodeporting/zlib-1.2.8# export CC=arm-linux-gcc

③配置zlib库,得到makefile:

root@wwj:~/decodeporting/zlib-1.2.8# ./configure -shared --prefix=/opt/libcode

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

④make && make install:执行后/out/libcode目录下的lib和include目录下就有zlib的静态库动态库和头文件了,然后回到上面的libpng的配置步骤

4、再次回到==》(2、libpng移植)配置环节

①配置:进入libpng-1.6.6目录下,配置libpng,还是报错,

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

原因是因为没有导出相关环境变量,所以libpng在配置的时候找不到刚才移植的zlib库的库文件和头文件。

②解决方案就是使用epport临时性的导出,在终端中依次输入以下命令:

export LDFLAGS="-L/opt/libcode/lib" export CFLAGS="-I/opt/libcode/include" export CPPFLAGS="-I/opt/libcode/include"

③导出后再次配置就过了,然后编译和安装

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

④ make && make install

然后在/opt/libcode/lib目录下,可以看到动态库文件

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

5、参考源码包自带的资料开始编程

(1)readme

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

(2)libpng-manual.txt

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

(3)example.c 和 pngtest.c

仔细阅读example.c源码,推敲各个函数功能,然后着手编写代码

6、开始编程

①先is_png函数使用判断一个图片文件是不是png图片

新建fb_png.c,并将之前的fb_jpeg.c中的函数原型cp过来修改,并在子文件夹下makefile包含头文件等,这里不再赘述

①测试我们写的int is_png函数(函数内容是直接将example.c 中的check_if_png函数cp过来的) int is_png(const char *path) { FILE *fp = NULL; char buf[PNG_BYTES_TO_CHECK]; /* Open the prospective PNG file. */ if ((*fp = fopen(file_name, "rb")) == NULL) return -1; /* Read in some of the signature bytes */ if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK) return -1; /* Compare the first PNG_BYTES_TO_CHECK bytes of the signature. Return nonzero (true) if they match */ return(!png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK)); }

然后同样步骤将之前jpeg显示函数cp过来修改,我们这里只是先测试is_png函数能否工作。

int display_png(const char *pathname) { int ret = -1; struct pic_info picture; // 第一步: 检测给的图片是不是jpg图片 ret = is_png(pathname); if (ret != 0) { return -1; printf("ret = %d.\n",ret); } printf("ret = %d.\n",ret); /* // 第二步: 解析该jpg图片 picture.pathname = pathname; picture.pData = rgb_buf; jpg_analyze(&picture); // 第三步: 显示该jpg图片 fb_draw2(&picture); */ }

主函数调用:

//④测试png图片显示 display_png("cute_pic4.png");//测试是否为png文件

make编译报错如下:没有找到这个,原因是没有包含相关头文件 怎么解决?

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

我们可以在ubuntu中使用grep命令查找这个变量头文件在那?

grep "PNG_BYTES_TO_CHECK" * -nR

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

我们也在开始处直接宏定义即可

继续make,报错如下,包含了头文件的,为什么还是找不到这个函数,原因是主Makefile 没有链接进来

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

从新make&&make cp…重启开发板,运行脚本,显示找不到lib相关动态库

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

解决方法:我们将之前生成的动态库拷贝到我们的跟文件系统的目录下:

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

然后从新make &&make cp ,运行后并没有打印 is_png函数相关信息,经过debug排查问题,发现错误(方法就是使用debug定位错误在最后的png_sig_cmp函数这,然后使用SI建立libpng-1.6.6的工程,找到这个函数的原型,分析它的功能,发现正常匹配的话,返回值应该是0,但是我们这里使用!取反了,故判断不是png),修改,从新执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C9ZWQPHz-1570028293357)(FD1C79F424904EABA29A61157858E5E2)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zFW82L9K-1570028293358)(16BB1996F2454DFFA52FB94F596DE013)]

② int png_analyze解码图片函数的编写(上)

这个函数参考example.c中的解码函数read_png函数了(因为过于复杂),我们选择直接在网上参考别人的。

int png_analyze(struct pic_info *pPic) { FILE *fp = NULL; png_structp png_ptr; png_infop info_ptr; int color_type; png_bytep* row_pointers; unsigned long len = 0; int pos = 0; int i = 0, j = 0; if ((fp = fopen(pPic->pathname, "rb")) == NULL) { fprintf(stderr, "can't open %s\n", pPic->pathname); return -1; } // 第1步: 相关数据结构实例化 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if (png_ptr == 0) { fclose(fp); return -1; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == 0) { png_destroy_read_struct(&png_ptr, 0, 0); fclose(fp); return -1; } // 第2步: 设置错误处理函数 if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, 0); fclose(fp); return -1; } // 第3步: 将要解码的png图片的文件指针和png解码器绑定起来 png_init_io(png_ptr, fp); // 第4步: 读取png图片信息 png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_ALPHA, 0); // 第5步: 相关图片信息打印出来看一看 color_type = info_ptr->color_type; debug("color_type = %d\n", color_type); pPic->width = info_ptr->width; pPic->height = info_ptr->height; pPic->bpp = info_ptr->pixel_depth; len = info_ptr->width * info_ptr->height * info_ptr->pixel_depth / 8; debug("width = %u, height = %u, bpp = %u\n", pPic->width, pPic->height, pPic->bpp); //打印png图片相关信息 return 0; }

make 报错如下:没找到这个文件,说明我们配置libpng的时候这几个文件没有生成,需要我们手动添加到动态库。

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

依次执行以下命令即可:

cp pngstruct.h /opt/libcode/include/ cp pnginfo.h /opt/libcode/include/

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

然后make && make cp,…从新启动开发板,运行脚本,显示如下:

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

③ int png_analyze解码图片函数的编写(下)

在上一步中,已经实现将相关图片信息打印出来了,接下来就是图片的显示相关了。 继续对函数进行添加:

int png_analyze(struct pic_info *pPic) { // 第5步: 相关图片信息打印出来看一看 ....... // 第6步: 读取真正的图像信息 row_pointers = png_get_rows(png_ptr,info_ptr); // 只处理RGB24位真彩色图片,其他格式的图片不管 // 第7步: 图像数据移动到我们自己的buf中 if(color_type == PNG_COLOR_TYPE_RGB)//PNG_COLOR_TYPE_RGB=2,和我们之前打印的信息要吻合 { //memcpy(pPic->pData, row_pointers, len);/*直接这样不行,因为数据是乱的*/ for(i=0; i<pPic->height; i++) { for(j=0; j<3*pPic->width; j+=3) { pPic->pData[pos++] = row_pointers[i][j+0]; //red pPic->pData[pos++] = row_pointers[i][j+1]; //green pPic->pData[pos++] = row_pointers[i][j+2]; //blue } } } // 第8步: 收尾处理 png_destroy_read_struct(&png_ptr, &info_ptr, 0); // close file fclose(fp); return 0; }

主函数调用

//④测试png图片显示 display_png("cute_pic4.png");

然后make && make cp,…从新启动开发板,运行脚本,显示如下,且开发板正常显示PNG图片

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RAGV2YKS-1570028293361)(DCB95ACEBFFC47B58399AF514EC13A85)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nKvrT7Dq-1570028293362)(C6561FDA37464B3DBDACCA7827858480)]

到这里,对png图片的解码显示就搞定了。

十五、目录扫描,图片文件的管理模块

1、图片文件的管理

(1)在物理磁盘存储层次上,用一个文件夹来管理;

(2)在程序中,用数据结构来管理。

用数组管理用链表管理

(4)编程实战(细节见代码,下面是关键点)

a、新建一个文件夹image_manage,记得要在makefile中添加新建的文件夹路径,以及在新建文件夹中新建makefile来管理文件。

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

b、文件夹的打开操作、读取操作 opendir的使用====打开文件夹

readdir的使用====读取文件夹的文件

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

2、图片信息的自动检索

linux读取文件夹内容,我们直接百度,选择这篇文章参考,修改为自己的 https://blog.csdn.net/weixin_40535588/article/details/89668934

①使用readdir函数来读取文件夹下内容

新建image_manage.h

typedef enum image_type { IMAGE_TYPE_BMP, IMAGE_TYPE_JPG, IMAGE_TYPE_PNG, IMAGE_TPPE_UNKNOWN, }image_type_e; // 结构体用来封装一个图片的信息 typedef struct image_info { char pathname[PATHNAME_LEN]; // 图片文件的pathname image_type_e type; // 图片文件的格式 }image_info_t;

新建image_manage.c, 使用readdir函数来读取文件夹内容 (readdir函数有问题,有时可以,有时不行,仔细阅读man手册,发现readdir函数中真正起作用的是lstat函数)

// images数组本来是空的,然后程序初始化时会去一个事先约定好的目录(image目录)下去 // 递归检索所有的文件及子文件夹,并且将所有的图片格式收集并且填充记录到images数组中 // 经过检索后,image数组中就记录了所有的图片,然后显示图片逻辑部分再去这个图片库中 // 拿出相应的图片来显示即可 // path是要去检索的文件夹的目录的路径 int scan_image(const char *path) { // 在本函数中递归检索path文件夹,将其中所有图片填充到iamges数组中去 DIR *dir; struct dirent *ptr; char base[1000]; if ((dir = opendir(path)) == NULL) { perror("Open dir error..."); exit(1); } // readdir函数每调用一次就会返回opendir打开的basepath目录下的一个文件,直到 // basepath目录下所有文件都被读完之后,就会返回NULL while ((ptr = readdir(dir)) != NULL) { if(strcmp(ptr->d_name, ".")==0 || strcmp(ptr->d_name, "..")==0) ///current dir OR parrent dir continue; debug("d_name = %s.\n", ptr->d_name); debug("d_type = %d, DT_REG = %d, DT_DIR = %d, DT_UNKNOWN = %d.\n", ptr->d_type, DT_REG, DT_DIR, DT_UNKNOWN); switch (ptr->d_type) { case DT_REG: // 普通文件 printf("d_name:%s/%s\n", path, ptr->d_name); break; case DT_DIR: // 文件夹 memset(base,'\0',sizeof(base)); strcpy(base,path); strcat(base,"/"); strcat(base,ptr->d_name); scan_image(base); break; case DT_UNKNOWN: // 不识别的文件格式 printf("unknown file type.\n"); break; default: break; } } }

主函数调用该函数:

scan_image("./image");//readdir读取文件夹下相关内容

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

②使用lstat函数来实现读取文件夹下内容

/* 使用lstat函数来实现读取文件夹下的内容,因为使用readdir函数有问题 扫描目录,索引图片,并完成图片数据的初始化 参数:目录的路径 */ int scan_image2(const char *path) { // 在本函数中递归检索path文件夹,将其中所有图片填充到iamges数组中去 DIR *dir; struct dirent *ptr; char base[1000]; struct stat sta; if ((dir = opendir(path)) == NULL) { perror("Open dir error..."); exit(1); } // readdir函数每调用一次就会返回opendir打开的basepath目录下的一个文件,直到 // basepath目录下所有文件都被读完之后,就会返回NULL while ((ptr = readdir(dir)) != NULL) { if(strcmp(ptr->d_name, ".")==0 || strcmp(ptr->d_name, "..")==0) ///current dir OR parrent dir continue; // 用lstat来读取文件属性并判断文件类型 memset(base,'\0',sizeof(base)); strcpy(base,path); strcat(base,"/"); strcat(base,ptr->d_name); lstat(base, &sta); if (S_ISREG(sta.st_mode)) { //printf("regular file.\n"); //printf("d_name:%s/%s\n", path, ptr->d_name); // 如果是普通文件,就要在这里进行处理: // 处理思路就是 先判定是否属于已知的某种图片格式,如果是则放到images数组中 // 如果都不属于则不理他 if (!is_bmp(base)) { strcpy(images[image_index].pathname, base); images[image_index].type = IMAGE_TYPE_BMP; } if (!is_jpg(base)) { strcpy(images[image_index].pathname, base); images[image_index].type = IMAGE_TYPE_JPG; } if (!is_png(base)) { strcpy(images[image_index].pathname, base); images[image_index].type = IMAGE_TYPE_PNG; } image_index++; } if (S_ISDIR(sta.st_mode)) { //printf("directory.\n"); //printf("d_name:%s/%s\n", path, ptr->d_name); scan_image2(base); } } } //打印出文件夹下图片的路径和文件名 void print_images(void) { int i; printf("iamge_index = %d.\n", image_index); for (i=0; i<image_index; i++) { printf("images[i].pathname = %s, type = %d.\n", images[i].pathname, images[i].type); } }

测试print_images函数,主函数调用:

scan_image2("./image"); print_images();

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

//循环显示各个图片函数,并打印出文件路径和文件名

void show_images(void) { int i; for (i=0; i<image_index; i++) { switch (images[i].type) { case IMAGE_TYPE_BMP: display_bmp(images[i].pathname); break; case IMAGE_TYPE_JPG: display_jpg(images[i].pathname); break; case IMAGE_TYPE_PNG: display_png(images[i].pathname); break; default: break; } sleep(2); } }

测试show_images函数,主函数调用://循环显示各个图片函数,并打印出文件路径和文件名

scan_image2("./image"); //print_images(); while(1) { show_images(); }

编译拷贝,运行开发板,能看到循环显示我们imag目录下的图片,且终端移植打印出各个图片的类型及其路径等相关信息。

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

十六、添加触摸翻页功能(前提是已经学习过触摸屏驱动相关)

完成触摸屏驱动后,点击左右屏幕实现切换图片。

1、读取触摸坐标数据

参考如下触摸屏驱动移植实战中的app_input目录下的程序,并修改为我们的

在image_manage.c中新建一下函数, 开发板上的触摸屏设备文件是/dev/input/event2(我这里是event2)

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

//定义系统中的触摸屏设备的设备名 #define DEVICE_TOUCHSCREEN "/dev/input/event2" /* *检测触摸屏,根据结果实现图片上下切换 *参数:无传参 */ int ts_updown(void) { // 第一步: 触摸屏的触摸操作检测 int fd = -1, ret = -1; struct input_event ev; fd = open(DEVICE_TOUCHSCREEN, O_RDONLY);// 打开设备文件 if (fd < 0) { perror("open"); return -1; } while (1) { // 读取一个event事件包 memset(&ev, 0, sizeof(struct input_event)); ret = read(fd, &ev, sizeof(struct input_event)); if (ret != sizeof(struct input_event)) { perror("read"); close(fd); return -1; } // 解析event包,才知道发生了什么样的输入事件 printf("-------------------------\n"); printf("type: %hd\n", ev.type); printf("code: %hd\n", ev.code); printf("value: %d\n", ev.value); printf("\n"); } // 关闭设备 close(fd); // 第二步: 根据触摸坐标来翻页 return 0; }

主函数调用:

//⑥测试触摸翻页显示图片功能 scan_image2("./image"); ts_updown();

make &&make cp, 开发板运行该程序,触摸屏幕,终端显示坐标信息如下:

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

code 0表示x坐标,value为x的值;code 1表示y坐标,value为y的值。

2、使用触摸坐标判断并执行翻页操作

修改ts_updown函数,使其能实现触摸翻页,红色部分为修改的源码

//定义触摸翻页区域的宽度 #define TOUCH_WIDTH 200 /* *检测触摸屏,根据结果实现图片上下切换 *参数:无传参 */ int ts_updown(void) { // 第一步: 触摸屏的触摸操作检测 int fd = -1, ret = -1; struct input_event ev; int i = 0; // 用来记录当前显示的是第几个图片 fd = open(DEVICE_TOUCHSCREEN, O_RDONLY);// 打开设备文件 if (fd < 0) { perror("open"); return -1; } while (1) { // 读取一个event事件包 memset(&ev, 0, sizeof(struct input_event)); ret = read(fd, &ev, sizeof(struct input_event)); if (ret != sizeof(struct input_event)) { perror("read"); close(fd); return -1; } // 第二步: 根据触摸坐标来翻页 if ((ev.type == EV_ABS) && (ev.code == ABS_X)) { // 确定这个是x坐标 如果在这个0~200的宽度范围 if ((ev.value >= 0) && (ev.value < TOUCH_WIDTH)) { // 上翻页 if (i-- <= 1) { i = image_index; debug("i=%d.\n", i); } } //如果在这个1024-200 ~1024的宽度范围 else if ((ev.value > (WIDTH - TOUCH_WIDTH)) && (ev.value <= WIDTH)) { // 下翻页 if (i++ >= image_index) { i = 1; debug("i=%d.\n", i); } } else { // 不翻页 } show_image(i - 1);//将i用来记录当前显示的是第几个图片,然后传给这个函数,用于显示图片 } /* // 解析event包,才知道发生了什么样的输入事件 printf("-------------------------\n"); printf("type: %hd\n", ev.type); printf("code: %hd\n", ev.code); printf("value: %d\n", ev.value); printf("\n"); */ } // 关闭设备 close(fd); return 0; }

主函数调用该函数

//⑥测试触摸翻页显示图片功能 scan_image2("./image"); ts_updown();

make &&make cp, 开发板运行该程序,触摸屏幕,终端显示坐标信息如下:

(1)执行./run.sh后会阻塞,如果点击触摸屏,会在终端中显示测试的内容。 (2)在不同区域点一下,有不同的效果。

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

十七、总结与回顾

1、项目总结

(1)项目描述:软硬件平台等

硬件平台使用使用s5pv210开发板,软件以linux平台开发。 支持解码显示bmp,png,JPEG图片,并通过点击触摸屏左右两端实现上下切换图片的效果

(2)重点和难点

第三方库libpng、 libgpeg库的移植 图片文件的管理和检索,刚开始使用read函数,出现问题,后来使用lstat函数来实现读取文件夹下内容,实现目录扫描

(3)主要技术:

linux Framebuffer驱动移植linux input输入子系统移植linux i2c子系统触摸屏驱动移植libjpeg库移植libpng库移植

1.1.驱动模块(驱动模块主要的任务就是进行Framebuffer地址映射)

linux Framebuffer驱动 参考前面我们学习的framebufer驱动,移植framebufer驱动。触摸屏(gslX680)驱动 参考驱动篇触摸屏驱动,移植gslX680驱动。

1.2.目录扫描,图片管理模块

目录扫描功能采用递归方法,扫描目录的所有文件,经过排除,最后只对普通文件做进一步处理。图片管理采用数组来组织。

1.3.图片切换模块

完成触摸屏驱动后,点击左右屏幕实现切换图片。

1.4.图片显示模块

BMP显示

通过判断头两个字符是否为”BM”,判断是否为BMP图片,BMP本身没有对数据进行压缩,所以可直接读取数据进行操作

JPEG显示

判断是否为JPEG图片, 通过判断特点位是否为JPEG专用字符,进而确认为JPEG图片后,解码jpg图片(使用libjpeg库提供JPEG图片的编解码算法实现),并将解码出来相应的位图数据进行存储。显示该图片

显示PNG图片

通过调用libpng库函数png_sig_cmp,判断是否为PNG图片。显示PNG图片,运用libpng库,完成PNG图片的显示

TOUCH_WIDTH 200

/* *检测触摸屏,根据结果实现图片上下切换 *参数:无传参 */ int ts_updown(void) { // 第一步: 触摸屏的触摸操作检测 int fd = -1, ret = -1; struct input_event ev; int i = 0; // 用来记录当前显示的是第几个图片

fd = open(DEVICE_TOUCHSCREEN, O_RDONLY);// 打开设备文件 if (fd < 0) { perror(“open”); return -1; } while (1) { // 读取一个event事件包 memset(&ev, 0, sizeof(struct input_event)); ret = read(fd, &ev, sizeof(struct input_event)); if (ret != sizeof(struct input_event)) { perror(“read”); close(fd); return -1; }

// 第二步: 根据触摸坐标来翻页 if ((ev.type == EV_ABS) && (ev.code == ABS_X)) { // 确定这个是x坐标 如果在这个0~200的宽度范围 if ((ev.value >= 0) && (ev.value < TOUCH_WIDTH)) { // 上翻页 if (i-- <= 1) { i = image_index; debug(“i=%d.\n”, i); }

} //如果在这个1024-200 ~1024的宽度范围 else if ((ev.value > (WIDTH - TOUCH_WIDTH)) && (ev.value <= WIDTH)) { // 下翻页 if (i++ >= image_index) { i = 1; debug(“i=%d.\n”, i); } } else { // 不翻页 } show_image(i - 1);//将i用来记录当前显示的是第几个图片,然后传给这个函数,用于显示图片

}

/* // 解析event包,才知道发生了什么样的输入事件 printf("-------------------------\n"); printf(“type: %hd\n”, ev.type); printf(“code: %hd\n”, ev.code); printf(“value: %d\n”, ev.value); printf("\n"); */ } // 关闭设备 close(fd);

return 0;

}

## 主函数调用该函数 //⑥测试触摸翻页显示图片功能 scan_image2("./image"); ts_updown(); make &&make cp, 开发板运行该程序,触摸屏幕,终端显示坐标信息如下: (1)执行./run.sh后会阻塞,如果点击触摸屏,会在终端中显示测试的内容。 (2)在不同区域点一下,有不同的效果。 [外链图片转存中...(img-hGATF2JG-1570028293369)] # 十七、总结与回顾 ## 1、项目总结 ### (1)项目描述:软硬件平台等 硬件平台使用使用s5pv210开发板,软件以linux平台开发。 支持解码显示bmp,png,JPEG图片,并通过点击触摸屏左右两端实现上下切换图片的效果 ### (2)重点和难点 第三方库libpng、 libgpeg库的移植 图片文件的管理和检索,刚开始使用read函数,出现问题,后来使用lstat函数来实现读取文件夹下内容,实现目录扫描 ### (3)主要技术: - linux Framebuffer驱动移植 - linux input输入子系统移植 - linux i2c子系统 - 触摸屏驱动移植 - libjpeg库移植 - libpng库移植 #### 1.1.驱动模块(驱动模块主要的任务就是进行Framebuffer地址映射) - linux Framebuffer驱动 参考前面我们学习的framebufer驱动,移植framebufer驱动。 - 触摸屏(gslX680)驱动 参考驱动篇触摸屏驱动,移植gslX680驱动。 #### 1.2.目录扫描,图片管理模块 - 目录扫描功能采用递归方法,扫描目录的所有文件,经过排除,最后只对普通文件做进一步处理。图片管理采用数组来组织。 #### 1.3.图片切换模块 - 完成触摸屏驱动后,点击左右屏幕实现切换图片。 #### 1.4.图片显示模块 - BMP显示 通过判断头两个字符是否为”BM”,判断是否为BMP图片,BMP本身没有对数据进行压缩,所以可直接读取数据进行操作 - JPEG显示 判断是否为JPEG图片, 通过判断特点位是否为JPEG专用字符,进而确认为JPEG图片后,解码jpg图片(使用libjpeg库提供JPEG图片的编解码算法实现),并将解码出来相应的位图数据进行存储。显示该图片 - 显示PNG图片 通过调用libpng库函数png_sig_cmp,判断是否为PNG图片。显示PNG图片,运用libpng库,完成PNG图片的显示
最新回复(0)