嵌入式Linux小项目之图片编解码播放器(7)

一、图片文件的管理和检索

1、图片文件的管理

(1)在物理磁盘存储层次上:用一个文件夹来管理;在程序中,用数据结构来管理。
(2)用数组管理
大小是提前规定好的,不便于扩展,不灵活
(3)用链表管理
大小可变,灵活便于管理,但耗费内存
(4)编程实战

#define PATHNMAE_LEN    256

typedef enum image_type
{
	IMAGE_TYPE_BMP,
	IMAGE_TYPE_JPG,
	IMAGE_TYPE_PNG,
	IMAGE_TYPE_UNKNOW,	
}image_type_e;

typedef struct image_info
{
	char pathname[PATHNMAE_LEN];	//图片文件的pathname
	image_type_e type;				//图片文件的格式		
}image_info_t;

//定义存储图片信息的数组
image_info_t images[MAX_IMAGE_CNT];

2、图片信息的自动检索

(1)读取文件类型
(2)普通文件和文件夹分类处理
(3)普通文件区分,将其中的图片按格式存储到图片管理数组/链表中

3、实践编程和调试

用到的API函数:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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;
		}
	}
}

在这里插入图片描述

int scan_image_limiting(const char *basePath)
{
    DIR *dir;
    struct dirent *ptr;
    char base[1000];

    if ((dir=opendir(basePath)) == 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;
        else if(ptr->d_type == 8)    ///file
        {
            printf("d_name:%s/%s\n",basePath,ptr->d_name);
			// 如果是普通文件,就要在这里进行处理:
			// 处理思路就是 先判定是否属于已知的某种图片格式,如果是则放到images数组中
			// 如果都不属于则不理他
			if (!is_bmp(base))
			{
				strcpy(images[image_index].pathname, ptr->d_name);
				images[image_index].type = IMAGE_TYPE_BMP;
			}
			if (!is_jpg(base))
			{
				strcpy(images[image_index].pathname, ptr->d_name);
				images[image_index].type = IMAGE_TYPE_JPG;
			}
			if (!is_png(base))
			{
				strcpy(images[image_index].pathname, ptr->d_name);
				images[image_index].type = IMAGE_TYPE_PNG;
			}		
			image_index++;			

        }
		else if(ptr->d_type == 10)    ///link file
            printf("d_name:%s/%s\n",basePath,ptr->d_name);
        else if(ptr->d_type == 4)    ///dir
        {
            memset(base,'\0',sizeof(base));
            strcpy(base,basePath);
            strcat(base,"/");//字符串拼接函数,制造一个新的目录basePath
            strcat(base,ptr->d_name);//同上
            scan_image_limiting(base);
        }
		
    }
    closedir(dir);

	return 0;
}

在这里插入图片描述
在这里插入图片描述

注意:readdir的使用与操作系统的版本以及根文件系统的类型是有一定的关系的,如果你使用的是nfs方式的网络根文件系统,则函数执行结果并不是你预期的那样,详情查看man手册关于readdir函数的讲解。因为DT_UNKNOWN等宏和其代表的值在不同的情况下不一定支持,而且作用会有一定的差异。上面提供的两个函数均无法在nfs根文件系统中使用,会使得文件格式识别错误或者执行的时候有时候成功有时候不成功,我遇到了一些莫名其妙的错误。

参考学习:https://www.cnblogs.com/xudong-bupt/p/3504442.html

本篇文章使用lstat函数解决readdir函数的问题,我的代码如下:

/***************************************************************
FileName: image_manage.c

Author:Mr.Zhang      Version:1.0      Date:2021/11/12

Description: This file is mainly responsible for the management
			 and retrieval of picture files.
***************************************************************/
#include "image_manage.h"
#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>


//定义存储图片信息的数组
image_info_t images[MAX_IMAGE_CNT];
unsigned int image_index = 0;       //用于images数组的计数

//image数组本来是空的,然后程序初始化的时候会去实现约定好的目录(image目录)
//下去递归检索所有的文件和子文件夹,并且将所有的图片格式收集并填充到记录到
//images数组中去,经过检索后,images数组中就记录了所有的图片,然后显示图片逻
//辑部分再去这个图片库中拿出相应图片来显示即可


//该函数是我们实际使用的函数,可在nfs方式的根文件系统中测试程序
int scan_image(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;
				DBG("This is a PNG picture.\n");
			}		
			image_index++;
			printf(" image_index = %d.\n", image_index);
			
		}
		if (S_ISDIR(sta.st_mode))
		{
			//printf("directory.\n");
			//printf("d_name:%s/%s\n", path, ptr->d_name);
			scan_image(base);
		}
	}

	return 0;
}

void print_images(void)
{
	int i;
	
	printf("image_index = %d.\n", image_index);
	for (i = 0; i < image_index; i++)
	{
		printf("image[%d].pathname = %s,  type = %d.\n", 
				i, images[i].pathname, images[i].type);

	}

}


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);
				DBG("showing %s.\n", images[i].pathname);
				break;
				
			case IMAGE_TYPE_JPG:
				display_jpg(images[i].pathname);
				DBG("showing %s.\n", images[i].pathname);
				break;			

			case IMAGE_TYPE_PNG:
				display_png(images[i].pathname);
				DBG("showing %s.\n", images[i].pathname);
				break;

			default:
				break;

		}

		sleep(2);
	}

}

在这里插入图片描述

二、添加触摸翻页功能

在开发板命令行测试得到触摸屏的设备文件是那个(event1或者event2或者其他的):

cat /dev/input/event1

1、读取触摸坐标数据

通过读取触摸屏设备文件实现该功能,对于触摸屏的触摸是归类于按键事件进行处理的,可参考以下博文:
https://blog.csdn.net/u010835747/article/details/108638660

	int fd = -1, ret = -1;
	struct input_event dev;
	int i = 0;              //用来记录当前显示的是第几个图片
	
	//第一步:打开设备文件
	fd = open(DEVICE_TOUCHSCREEN, O_RDONLY);
	if (fd < 0)
	{
		perror("open failure.");
		return -1;
	}
	//第二步:读取一个event事件包	
	memset(&dev, 0, sizeof(struct input_event));
	ret = read(fd, &dev, sizeof(struct input_event));
	if (ret != sizeof(struct input_event))
	{
		perror("read failure.");
		break;
	}
		
	//第三步:解析event包,知晓发生了什么样的输入事件
	DBG("------------------------------------\n");
	DBG("type:%d\n", dev.type);
	DBG("code:%d\n", dev.code);
	DBG("value:%d\n", dev.value);
	DBG("\n");	

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

			if ((ev.value >= 0) && (ev.value < TOUCH_WIDTH))
			{
				// 上翻页
				if (i-- <= 0)
				{
					i = image_index;
					DBG("i=%d.\n", i);
				}
				
			}
			else if ((ev.value > (WIDTH - TOUCH_WIDTH)) && (ev.value <= WIDTH))
			{
				// 下翻页			
				if (i++ >= image_index)
				{
					i = 0;
					DBG("i=%d.\n", i);
				}
			}
			else
			{
				// 不翻页
			}

三、总结与回顾

1、触摸翻页完整代码

static void show_image(int index)
{
	DBG("index = %d.\n", index);
	switch (images[index].type)
	{
		case IMAGE_TYPE_BMP:
			display_bmp(images[index].pathname);		break;
		case IMAGE_TYPE_JPG:
			display_jpg(images[index].pathname);		break;
		case IMAGE_TYPE_PNG:
			display_png(images[index].pathname);		break;
		default:
			break;
	}	
}


// 本函数实现通过触摸屏来对图片翻页显示
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)
	{
		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坐标
			if ((ev.value >= 0) && (ev.value < TOUCH_WIDTH))
			{
				// 上翻页
				if (i-- <= 1)
				{
					i = image_index;
					debug("i=%d.\n", i);
				}
				
			}
			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);
		}
		
			
/*
		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;
	
}

2、项目总结

(1)项目描述
基于Linux的API实现了bmp、jpg、png格式图片的显示,并实现了图片管理和检索功能,可配合触摸屏实现图片的上下翻页功能。

(2)重点和难点

遇到的问题:
1、移植libjpg配置过程中由于缺少libtool的两个配置文件导致无法正常配置,通过百度,CSDN,得知要先去移植了libtool解决这个问题后再去移植。

2、libjpg解码出现问题,由于该开源库提供的例程有错误,通过百度,查阅博客,阅读文档发现解决。

3、libpng库依赖于zib库的问题。

4、程序编译调试过程中遇到的问题,通过DBG打印调试信息协助解决遇到的问题。遇到比较多的就是段错误,再就是声明与定义先后顺序的问题,有时候需要知道某个函数在开源lib库中是否定义,需要去查看源代码,看他的参数。

5、Makefile添加各种编译选项,指定链接库的路径,头文件的路径等等,Makefile语法报错问题。

3、项目展望与扩展功能

(1)划屏翻页

(2)图片放大与缩小显示

(3)动画

(4)开机画面
这个比较好实现,在main函数开头直接先显示一个画面,再去进行其他的工作。

(5)背景音乐

summary:到这里,图片编解码播放器的系列文章就结束了,目前我们已经实现了三种类型的图片的显示、图片的管理和检索功能、图片的上下翻页功能。这个小项目具有无限的扩展性,上面也给出了大家几个思路,如果大家感兴趣,可以接着做下去,十分欢迎大家找我一起交流学习,共同进步!由于我时间和精力有限,就不继续探索了,要去搞别的东西了。如果真的有谁想继续做下去,不断扩充,可以私信我,我们可以交流下思路和想法,当然具体的实现工作我估计没太多时间参与了!

注:本资料大部分由朱老师物联网大讲堂课程笔记整理而来并且引用了部分他人博客的内容,如有侵权,联系删除!水平有限,如有错误,欢迎各位在评论区交流。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小嵌同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值