以下内容源于朱有鹏《物联网大讲堂》课程的学习整理,如有侵权,请告知删除。
一、开始动手写代码
1、Makefile介绍
(1)这是一个通用的项目管理的Makefile体系,自己写的(有子文件夹组织的)项目可以直接套用这套Makefile体系。
(2)包含三类:顶层Makefile、Makefile.build(完全不需要改动)、子文件夹下面的Makefile。
- 子文件夹下的Makefile一般是类似下面的内容。
(3)可以参考:http://www.cnblogs.com/lidabo/p/4521123.html。
2、使用SI建立工程
二、framebuffer基本操作代码
显示图片,需要framebuffer。
三、图片显示原理和实践
1、图片显示原理
(1)概念1:像素
(2)概念2:点阵(像素点构成的阵列)
(3)分辨率
- 物理分辨率:物理屏幕像素点的真实个数;
- 显示分辨率:可以小于等于物理分辨率;(通过抽样)
(4)清晰度(与分辨率和像素间距有关,主观概念)
- 像素间距相同时(物理尺寸固定了,则像素间距就固定了),分辨率越大越清晰;分辨率相同时,像素间距越小越清晰。
(5)bpp(RGB565、RGB888)
- 像素深度,每个像素用多少bit数据表示。
- 一般每个像素点使用32bit(4个字节),但一般是24位色(高八位一般没用或者用着其他标识,用00或者ff填充以达到内存对齐);
- RGB888表示红用8位,绿8位,蓝8位。
(6)颜色序(RGB、BGR)
2、图片点阵数据获取
(1)Image2LCD软件提取
(2)通过图片/视频文件直接代码方式提取
四、图片数据提取和显示
1、Image2LCD提取图片数据
(1)软件下载:http://www.cr173.com/soft/43222.html
(2)软件使用
- 一般不要图像头数据,只需要纯数据;
- 一般水平扫描;
- 一般选24位真彩色(即RGB888);
- 选1024*600后,点右边按钮更新;
- 输出图像调整中,可以调整RGB。
- 最后点击保存。
2、图片显示编码与实践
void fb_draw_picture(void)
{unsigned char* pdata=gImage_1024600;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;*p=((pdata[cnt+0]<<0)|(pdata[cnt+1]<<8)|(pdata[cnt+2]<<16));//可以在这里修改,达到正确的显示(当RB相反时)p++;}}
}
五、图片显示的高级话题
1、RGB顺序调整
(1)RGB顺序有三个地方都有影响
- 第一个是fb驱动中的排布;
- 第二个是应用程序中的排布;
- 第三个是图像数据本身排布(Image2LCD中调整RGB顺序);
(2)如果三个点中RGB顺序是一致的就会显示正常。如果三个设置不一致就可能会导致显示结果中R和B相反了;
(3)实际写程序时,一般不去分析这东西,而是根据实际显示效果去调。如果反了就去调正应用程序中的RGB顺序就行了,这样最简单。
2、显示函数的其他写法
void fb_draw_picture2(void)
{unsigned char* pdata=gImage_1024600;unsigned int x,y,cnt;for(y=0;y<HEIGHT;y++){for(x=0;x<WIDTH;x++){cnt=y*WIDTH+x;*(pfb+cnt)=((pdata[cnt*3+0]<<16)|(pdata[cnt*3+1]<<8)|(pdata[cnt*3+2]<<0));//这里的像素矩阵和cnt有线性关系,所以可以这样写}}
}
六、任意分辨率大小图片显示
(1)图片比屏幕分辨率大
- 这种情况下多出来的部分肯定是没法显示的。处理方法是直接在显示函数中把多余不能被显示的部分给丢掉。
(2)图片大小比屏幕大小要小
- 这种情况下图片只是填充屏幕中一部分,剩余部分仍然保持原来的底色。
- 在获取图片数据时,大小和图片实际大小在这里是一致的。假如不一致呢?
void fb_draw_picture3(void)
{unsigned char* pdata=gImage_500281;unsigned int x,y,cnt;unsigned int a=0;for(y=0;y<281;y++){for(x=0;x<500;x++){cnt=y*WIDTH+x;/*cnt始终都是framebuff像素点的编号*/*(pfb+cnt)=((pdata[a+0]<<16)|(pdata[a+1]<<8)|(pdata[a+2]<<0));a+=3;/*由于空缺一部分,像素点编号与像素点矩阵不存在倍数关系了,此时应该三个三个地传送进来*/}}
}
七、任意起点位置图片显示
1、小图片任意起点(但整个图片显示没有超出屏幕范围内)
算法1:
void fb_draw_picture4(unsigned int x0,unsigned int y0)
{unsigned char* pdata=gImage_500281;unsigned int x,y,cnt;unsigned int cnt1,cnt2;for(y=y0;y<281+y0;y++){for(x=x0;x<500+x0;x++){cnt1=y*WIDTH+x;/*cnt始终都是fb 缓冲区整体中的像素点的编号*/cnt2=500*(y-y0)+(x-x0);*(pfb+cnt1)=((pdata[cnt2*3+0]<<16)|(pdata[cnt2*3+1]<<8)|(pdata[cnt2*3+2]<<0));/*左值考虑当前像素点在fb中的偏移量*//*右值考虑当前像素点在图像数据数组的下标(这里是三个为一个元素的数组的下标,因此上面会*3)*/}}
}
算法2:(因为每循环一次,+3)
2、起点导致图片超出屏幕外
(1)现象
- 左右超出去,会在相反方向补全:这是内部for循环可能超过1024的界定(但没有超出fb的大小)造成的。
- 上下超出去,则会消失:因为双缓冲进到了另一帧。如果没有双缓冲,则会内存溢出。
(2)修改代码,使得超出部分不再显示。
法一:
void fb_draw_picture5(unsigned int x0,unsigned int y0)
{unsigned char* pdata=gImage_500281;unsigned int x,y,cnt;unsigned int a=0;for(y=y0;y<281+y0;y++){if(y>=HEIGHT)//y方向超出{a+=3;break;//最后一行已经显示了,剩下的不用考虑了}for(x=x0;x<500+x0;x++){if(x>=WIDTH)//x方向超出了{a+=3;continue;}cnt=y*WIDTH+x;/*cnt始终都是像素点的编号*/*(pfb+cnt)=((pdata[a+0]<<16)|(pdata[a+1]<<8)|(pdata[a+2]<<0));a+=3;/*因此,像素点矩阵也应该三个三个地传送进来*/}}
}
法二:
void fb_draw_picture4(unsigned int x0,unsigned int y0)
{unsigned char* pdata=gImage_500281;unsigned int x,y,cnt;unsigned int cnt1,cnt2;for(y=y0;y<281+y0;y++){for(x=x0;x<500+x0;x++){if(x>=WIDTH)//x方向超出了{///a+=3;注意这里被诠释了continue;}cnt1=y*WIDTH+x;/*cnt始终都是fb 缓冲区整体中的像素点的编号*/cnt2=500*(y-y0)+(x-x0);*(pfb+cnt1)=((pdata[cnt2*3+0]<<16)|(pdata[cnt2*3+1]<<8)|(pdata[cnt2*3+2]<<0));/*左值考虑当前像素点在fb中的偏移量*//*右值考虑当前像素点在图像数据数组的下标*/}}
}
八、BMP图片的显示
1、图片文件的本质
(1)图片文件是二进制文件。
- 文件分两种,即二进制文件、文本文件;
(2)不同格式的图片文件的差别。
- 图片被压缩与否的区别。
(3)BMP图片的基本特征
- 未被压缩的元素位图格式(bit map)。
2、BMP图片详解
(1)如何识别BMP文件?
- 每种图片格式都有定义好的一种识别方法,BMP图片特征是以0x424D开头;
(2)BMP文件组成
- 头信息+有效信息
3、BMP文件头信息、图片有效数据区
见博客:https://www.2cto.com/kf/201310/252434.html
(1)文件大小
(2)有效数据开始的位置
(3)信息头的大小:40个字节
(4)分辨率
(5)24位真彩色
(6)……
4、写代码解析BMP图片
第一步:打开BMP图片
第二步:判断图片格式是否真是BMP
第三步:解析头信息,得到该BMP图片的详细信息
第四步:根据第三步得到的信息,去合适位置提取真正的有效图片信息
第五步:将得到的有效数据丢到fb中去显示
这样实际比较繁琐!使用结构体比较好!
//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=0;//打开bmp文件fd=open(path,O_RDONLY);if(fd<0){fprintf(stderr,"open %s error.\n",path);return -1;}//读取文件头信息ret=read(fd,buf,54);if(ret!=54){fprintf(stderr,"read file header error.\n");return -1;}//解析头//判断是否BMP图片if(buf[0]!='B'||buf[1]!='M'){fprintf(stderr,"file %s is not a bmp picture.\n",path);return -1;}printf("file %s is a bmp picture.\n",path);printf("width is %d\n",*((unsigned int*)(buf+0x12)));printf("hith is %d\n",*((unsigned int*)(buf+0x16)));//成功则说明小端模式close(fd);return 0;}
5、用结构体方式解析BMP文件头
6、用结构体方式解析BMP信息头
#ifndef __BMP_H__
#define __BMP_H__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;//为目标设备说明位面数,其值将总是被设为1unsigned 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; #endif
void fb_draw(const unsigned char* ppic)
{unsigned char* pdata=ppic;unsigned int x,y;unsigned int a=0;unsigned int cnt=0;a=281*500*3-3;for(y=0;y<281;y++){for(x=0;x<500;x++){cnt=y*WIDTH+x;*(pfb+cnt)=((pdata[a+0]<<16)|(pdata[a+1]<<8)|(pdata[a+2]<<0));a-=3;}//a表示每个像素的三个字节数据的首字节编号}
}
//path是bmp图片的pathname
//该函数解析path图片,并将图片数据丢到bmp_buf中
//返回值错误时返回-1,正确返回0
int bmp_analyze(unsigned char *path)
{int fd=-1;BitMapFileHeader fheader ;BitMapInfoHeader info;ssize_t ret=0;int i;unsigned long len;//打开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 0for( i=0;i<sizeof(fheader);i++){printf("%x ",*((unsigned char *)&fheader+i));}printf("\n");
#endifret= read(fd,&info,sizeof(info));/*会沿着继续读下去*/printf("picture resolution : %d * %d.\n",info.biHeight,info.biWidth);printf("picture bpp : %d \n",info.biBitCount);//读取图片有效信息//先把文件指针移动到有效信息的偏移量处(lseek函数)//然后读出info.biHeight*info.biWidth*info.biBitCount/8个字节lseek(fd,fheader.bfOffBits,SEEK_SET);//从文件开始的位置开始len=info.biHeight*info.biWidth*info.biBitCount /8;printf("len put to buff :%ld.\n",len);read(fd,bmp_buf,len);//把内容丢到fb中去显示fb_draw(bmp_buf);/*数据和使用image2lcd获取的不一样*这里会旋转180度颠倒*即第一个像素放到最后一个像素点像素,依次类推了*/close(fd);return 0;
}
注意:
- 使用结构体来解析图片数据时,和使用image2lcd不一样。
- 这里会旋转180度颠倒,即第一个像素放到最后一个像素点像素,依次类推了。
- 因此在fb_draw()函数中,需要将最后一个像素点数据放在第一个像素处显示,以此类推。
---------------以上代码写得不规整,要进行规整--------------------
九、及时规整
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宏。见http://blog.csdn.net/oqqhutu12345678/article/details/78873195
- 第二种:在编译参数中添加-DDEBUG编译选项。(在makefile中添加)
6、图片信息用结构体来封装传递
//封装图片各种信息
typedef struct pic_info
{char* pathname;//路径和文件名字unsigned int width;unsigned int height;unsigned int bpp;unsigned char *pData;//指向图片有效数据存储的buff}pic_info;
从而需要修改相关代码。
十、jpg图片的显示原理分析
1、认识jpg图片
(1)属于二进制文件。
(2)有其固定的识别特征:http://www.cnblogs.com/Wendy_Yu/archive/2011/12/27/2303118.html
(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:
- -I是编译选项(准确的是说是makefile预处理选项CFLAGS或者CPPFLAGS中指定),用来指定预处理时查找头文件的范围的。
- -l(小写L)是链接选项(LDFLAGS中指定),用来指定链接额外的库的名字(譬如我们用到了数学函数,就用-lm,链接器就会去链接libm.so;那么我们使用了libjpeg,对应的库名字就叫libjpeg.so,就需要用-ljpeg选项去链接)。
- -L是链接选项(LDFLAGS中指定),用来指定链接器到哪个路径下面去找动态链接库。
- 总结:-l(小写L)是告诉链接器要链接的动态库的名字,而-L是告诉链接器要链接的动态库的路径。
十二、libjpeg的移植实战
1、移植
(1)源码下载、解压至/tmp/decodeporting目录下。
(2)编译前的配置(分析configure文件的usage、借鉴前辈的设置,得到应该执行下面命令)
- ./configure --prefix=/opt/libcode --exec-prefix=/opt/libcode --enable-shared --enable-static-build=i386 -host=arm
- 指定编译结果放置的目录(如果没有则需要先建立相应的目录)
- 配置生成了makefile文件,通过查看知道需要在/opt/libcode中创建include、bin、lib目录。
(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所指定的那些目录中去。
2、部署
前面只是完成移植,相关文件都在ubuntu上。我们要把动态链接库(.so文件)放到开发板的根文件系统中(/root/rootfs/),可以考虑三者之一:
- 第一个:/lib
- 第二个:/usr/lib(前两个在程序运行时,可以自动找到。后面任意目录时,需要在程序中指定)
- 第三个:任意指定目录
十三、使用libjpeg解码显示jpg图片
1、如何使用一个新的库
(1)思路一:网络上找别人使用过后写的文档、博客等作为参考。(2)思路二:看库源码自带的文档(说明文档和示例代码)。我们从思路二出发。
2、libjpeg说明文档和示例代码
(1)说明文档:README和libjpeg.doc(2)示例代码:example.c
3、结合说明文档来实践
4、解读example.c和移植
5、代码问题
(1)测试代码,先试图读取jpg图片头信息。
(2)问题排除- 编译时问题:主要就是头文件包含,除了在代码中包含头文件外,还要注意指明头文件的路径。
- 因为尽管包含了头文件,但只会在当前的目录和path指定的路径中寻找。我们应该在总Makefile中指定。
- 注意-I、-l、-L三个编译链接选项的使用
6、部署动态库以使程序运行起来
(1)一般放到开发板根文件系统/lib或者/usr/lib下
- 这样不需要给系统指定库路径,就能自动找到。
- 强调一下是开发板根文件系统下的路径,千万不要弄成了ubuntu的根文件系统下的目录。
(2)放到自定义的第三方的目录
- 将该自定义第三方目录导出到环境变量LD_LIBRARY_PATH下即可。
- 可以使用echo $LD_LIBRARY_PATH查看当前的路径环境变量包含哪些路径。
- 可以把上述操作写进到run.sh文件中,省得每次都要这样操作。
7、测试读取头信息
十四、解决解码显示中的问题
1、问题分析及解决记录
(1)根据LCD错误的显示状态,分析有可能是显示函数fb_draw中的图片宽高数据有误,于是在fb_draw函数中添加debug打印出宽和高来。结果发现是对的。
(2)显示函数中的图片宽高和fb宽高都是对的,结果显示时还是只有一溜(其余位置黑屏),可能的一个原因就是:显示数据本身不对,很多都是0。
- 如何验证?只要把显示数据打印出来看一看就知道了。
- 结果发现打印出的待显示数据果然是很多0,说明给显示函数的待显示数据就是错的。
(3)这些待显示数据为什么会错?
- 第一种可能性就是libjpeg解码出来的数据就是错的;
- 第二种可能性是解码一行出来暂存到buffer的时候,或者memcpy从暂存的buffer拿出来给pData指向的空间的时候给搞错了。
- 相对来说第二种很好验证而第一种不好验证。只需要在jpeg_read_scanlines函数后面直接打印显示解码出来的一行数据,就可以知道是不是第二种情况。
- 结果打印出来好多0,说明是第一种情况。
(4)截至目前已经锁定问题,就是jpeg_read_scanlines解码出来的数据本身就不对。
(5)可能的问题
- 有可能是libjpeg本身就有问题;
- 有可能我们对libjpeg的部署不对导致他工作不对;
- 有可能我们写的代码不对,也就是说我们没用正确的方法来使用libjpeg。
(6)没有思路怎么办
- 去网上找一些别人写的libjpeg解码显示图片的示例代码,多看几个,对着和我们的关键部位对比,寻找思路。
- 如果在网上找不到相关资料,这时候就只有硬着头皮去看源码了。譬如去libjpeg的源码中查看:jpeg_read_scanlines、cinfo.mem->alloc_sarray等。
(7)解决了buffer申请导致的问题之后,我们再来解决2个遗留的问题
- 一个就是RGB顺序问题,另一个是图像转了180度的问题。
(8)添加了fb_draw2函数并且调用后,2个遗留问题彻底解决。至此,jpg图片显示完美实现。
2、结束jpg图片部分
(1)加上jpg图片格式识别:判断开头和结尾的特征字节。
(2)对外封装好用的jpg图片显示函数
(3)对外封装好用的bmp图片显示函数
十五、解码显示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、编译、部署。注意实际路径。
- ./configure --host=arm-linux --enable-shared --enable-static --prefix=/opt/libdecode
(3)配置出错,报错信息:configure: error: zlib not installed
- 分析问题是因为libpng依赖于zlib库,所以要先移植zlib库才可以。
(4)移植了zlib后再过来配置,还是报错,原因是因为没有导出相关环境变量,所以libpng在配置的时候找不到刚才移植的zlib库的库文件和头文件。
(5)解决方案就是使用epport临时性的导出,在scrt中输入:
# export LDFLAGS="-L/opt/libdecode/lib"
# export CFLAGS="-I/opt/libdecode/include"
# export CPPFLAGS="-I/opt/libdecode/include"
(6)导出后再次配置就过了,然后编译和安装
(7)make && make install
3、zlib移植
(1)下载:http://www.zlib.net/,并解压
(2)配置:export CC=arm-linux-gcc
./configure -shared --prefix=/opt/libdecode
(3)make && make install
(4)make install后/opt/libdecode目录下的lib和include目录下就有了zlib的静态库动态库和头文件了,然后再回去继续libpng的移植。
4、参考源码包自带的资料开始编程
(1)readme
(2)libpng-manual.txt
(3)example.c 和 pngtest.c
十六、图片文件的管理和检索
1、图片文件的管理
(1)在物理磁盘存储层次上,用一个文件夹来管理;(2)在程序中,用数据结构来管理。
- 用数组管理
- 用链表管理
(4)编程实战(细节见代码,下面是关键点)
a、新建一个文件夹,记得要在makefile中添加新建的文件夹路径,以及在新建文件夹中新建makefile来管理文件。
b、文件夹的打开操作、读取操作
2、图片信息的自动检索
(1)读取文件类型
(2)普通文件和文件夹分类处理
(3)普通文件区分,将其中的图片按格式存储到图片管理数组/链表中
typedef enum image_type
{IMAGE_TYPE_BMP,IMAGE_TYPE_JPG,IMAGE_TYPE_UNKNOWN,
}image_type_e;typedef struct image_info
{char pathname[PATHNAME_LEN];image_type_e type;
}image_info_t;
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目录下所有文件都被读完之后,就会返回NULLwhile ((ptr = readdir(dir)) != NULL){if(strcmp(ptr->d_name, ".")==0 || strcmp(ptr->d_name, "..")==0) ///current dir OR parrent dircontinue;// 用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;}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 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;default:break;}sleep(2);}
}
十七、添加触摸翻页功能
1、读取触摸坐标数据
- 开发板上的触摸屏是/dev/input/event2(我这里是event2)
2、使用触摸坐标判断并执行翻页操作
(1)执行./run.sh后会阻塞,如果点击触摸屏,会在scrt中显示测试的内容。
- code 0表示x坐标,value为x的值;code 1表示y坐标,value为y的值。
(2)在不同区域点一下,有不同的效果。
十八、总结与回顾
1、bug解决
2、项目总结
(1)项目描述:软硬件平台等
(2)重点和难点
3、项目展望与扩展功能
(1)划屏翻页
(2)图片放大与缩小显示
(3)动画
(4)开机画面
(5)背景音乐