图片播放器小项目(详解)

以下内容源于朱有鹏《物联网大讲堂》课程的学习整理,如有侵权,请告知删除。


一、开始动手写代码

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)背景音乐


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461116.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Telnet远程访问思科交换机、路由器

一、实验目的Telnet远程访问思科交换机、路由器二、实验拓扑三、实验步骤1、PC1远程管理S11&#xff09;配置交换机的管理IPS1(config)#int vlan 1S1(config-if)#ip add 192.168.1.100 255.255.255.0S1(config-if)#no shu2&#xff09;开启S1的telnet远程管理服务S1(config)#li…

[置顶]       cocos2d-x 手游源码站

尊重开发者的劳动成果&#xff0c;转载的时候请务必注明出处&#xff1a;http://blog.csdn.net/haomengzhu/article/details/37829061 1、魔幻方块 链接&#xff1a;魔幻方块源码关键词&#xff1a;魔幻方块源码 源代码 Cocos2d-x2.0 游戏源码 益智 休闲 游戏 游戏类型&#xf…

Android SDK开发包国内下载地址

原帖地址&#xff1a;http://www.cnblogs.com/bjzhanghao/archive/2012/11/14/2769409.html 不知道是因为最近kaihui还是怎么的&#xff0c;打开android sdk官方网站特别的慢&#xff0c;想下载最新版本的platform几乎变成不可能完成的任务&#xff0c;不知道为什么Google不像…

SharePoint 2013 Workflow - Advanced Workflow Debugging with Fiddler

来自&#xff1a;Andrew Connell [MVP SharePoint] | 时间&#xff1a;2012-07-18 19:26:30 原文链接&#xff1a; http://www.andrewconnell.com/blog/archive/2012/07/18/sharepoint-2013-workflow-advanced-workflow-debugging-with-fiddler.aspx In previous posts Iv…

java sheet 打印区域设定,如何使用Java设置电子表格的打印区域。(How to set the print area of a spreadsheet using Java.)...

如何使用Java设置电子表格的打印区域。(How to set the print area of a spreadsheet using Java.)问题描述 (Problem Description)如何使用Java设置电子表格的打印区域。解决方案 (Solution)以下是使用Java设置电子表格打印区域的程序。import java.io.File;import java.io.Fi…

RedHat6.2 x86手动配置LNMP环境

为什么80%的码农都做不了架构师&#xff1f;>>> 因为公司要求用RedHat配&#xff0c;顺便让我练习一下Linux里面的操作什么的。 折腾来折腾去终于搞好了&#xff0c;其实也没那么难嘛。但是也要记录一下。 首先&#xff0c;是在服务器里面用VMware搭建的RedHat6.2 …

《c语言深度剖析》读书笔记

一、注意点 1、 2、 3、 4、 5、 6、 7、 8、 9、 10、 11、 二、问题 1、 2、 3、 4、 5、 6、 7、

androidpn的一次亲密接触(二)

简单看了一下源码的实现&#xff0c;这里贴一点个人觉得比叫重要的代码。 XmppManager.java 构造方法&#xff1a;Java代码public XmppManager(NotificationService notificationService) 在这里主要是从共享引用中取得xmpp服务器地址和端口号、用户名和密码。 内部类Java代码…

指针知识学习[总]

printf("p %p.\n", p); // %p打印指针和%x打印指针&#xff0c;打印出的值是一样的 printf("p 0x%x.\n", p); 一、指针是什么&#xff1f; 1、指针变量和普通变量的区别 指针的实质就是个变量&#xff0c;它跟普通变量没有任何本质区别。指针完整的名字…

php excel 分页,excel分页线怎么增加

增加excel分页线的方法&#xff1a;首先依次点击“工作簿视图-分页预览”&#xff1b;然后点击要在其下方插入分页符的这一行&#xff1b;最后在“页面布局”选项卡上的“页面设置”组中&#xff0c;单击“分隔符”即可。本文操作环境&#xff1a;Windows7系统&#xff0c;Micr…

C# 中的委托和事件

PDF 浏览&#xff1a;http://www.tracefact.net/Document/Delegates-and-Events-in-CSharp.pdf文中代码在VS2005下通过&#xff0c;由于VS2003(.Net Framework 1.1)不支持隐式的委托变量&#xff0c;所以如果在一个接受委托类型的位置直接赋予方法名&#xff0c;在VS2003下会报…

php7 有ext skel吗,PHP扩展开发系列02 - 老司机起步之函数

上一篇扩展开发引导文章中。创建了编写扩展的三个基本文件。或许你会有个疑问PHP没有类似的自动生成项目框架的工具吗&#xff1f; 当然有。这篇文章就开始介绍使用 "php-ext-cli" 工具来生成扩展项目文件注意这里的 "php-ext-cli" 本身没有这玩意&#xf…

(转载)说说char

char一直都在使用并且大量的使用&#xff0c;但是&#xff0c;大部分都是在单一平台上&#xff0c;那样没有太多的问题&#xff0c;在windows上大量直接使用wchar_t来解决中文的问题。并且在使用跨平台时也大多直接使用了Qt的QString来节省了很多的麻烦&#xff0c;但是现在项目…

WPF 正確理解ContentPresenter

2019独角兽企业重金招聘Python工程师标准>>> 我們先由下圖來看類層次,可知ContentControl繼承Control,ContentPresenter繼承FrameworkElement(Control也繼承FrameworkElement); 同樣的,ItemsControl繼承Control,ItemsPresenter繼承FrameworkElement. 在Control類並…

java动态交叉表,SqlServer如何生成动态交叉表查询

为了说明问题&#xff0c;我们用SqlServer自带的事例数据库(Northwind)来进行验证&#xff0c;所有的例子请放到Northwind中运行&#xff0c;我可能会省略Use语句&#xff0c;所引用的表&#xff0c;都是Northwind中的&#xff0c;下面我就不再说明了这里指的交叉表&#xff0c…

Canvas Clock

这两天在看html5的canvas,实现了上面那个东西 需要注意的地方&#xff1a; 1.canvas的sava()和restore()理解和使用 2.canvas的translate scale rotate ..的使用&#xff0c;每个变化都应该清楚圆心和角度..看&#xff1a;http://blog.sina.com.cn/s/blog_8fab526c01015tqs.htm…

CentOS 6.3 下用ntfs-3g挂载Windows NTFS分区

2019独角兽企业重金招聘Python工程师标准>>> 默认情况下&#xff0c;CentOS 6.3不支持Widows NTFS硬盘分区读写&#xff0c;要想把NTFS格式的磁盘挂载到CentOS 6.3下面需要安装第三方的插件ntfs-3g&#xff0c;这里我们采用编译安装插件。 1、安装编译器&#xff0c…

C++的const修饰

2019独角兽企业重金招聘Python工程师标准>>> C的const修饰 ‍const的两个用途‍ &#xff08;1&#xff09;可以定义 const 常量 &#xff08;2&#xff09;const 可以修饰函数的参数、返回值. const的好处 &#xff08;1&#xff09;便于进行类型检查&#xff0c;…

心率变异性 matlab,心率变异性好的功率谱分析方面的问题

本帖最后由 天路 于 2018-2-25 21:16 编辑本人正在学习心率变异性方面的内容&#xff0c;但是按照文献上的方法做出来的结果并不是很理想&#xff0c;文献上说的是心率变异性的频率的范围是0.4以内&#xff0c;但是我做的功率谱上显示频率分布在整个频域内&#xff0c;试了很多…

决策树php,决策树模型组合之随机森林与GBDT

前言&#xff1a;决策树这种算法有着很多良好的特性&#xff0c;比如说训练时间复杂度较低&#xff0c;预测的过程比较快速&#xff0c;模型容易展示(容易将得到的决策树做成图片展示出来)等。但是同时&#xff0c;单决策树又有一些不好的地方&#xff0c;比如说容易over-fitti…