基于Linux的电子相册

项目简介:

在Linux系统中,利用LCD和触摸屏模拟器,显示某文件夹(相册)中的bmp和jpg图片,鼠标点击可切换上一张、下一张,同时,利用madplay播放器播放音乐。

 

环境搭载:

所用软件以及工具如下:

1.VMware Workstation 16 Play搭载Ubuntu18.04的Linux操作系统。

2.使用VScode代码编写软件,配置好C语言开发环境。

3.Linux系统中:

(1)安装LCD和触摸屏模拟器。

(2)移植libjpeg开源库(用于解压jpg格式文件)。

(3)安装madplay播放器。

 

核心代码:

首先,运行LCD模拟器,模拟一块分辨率为800*480的LCD屏幕,利用mmap函数将LCD设备文件映射到内存中,再通过内存指针plcd直接在内存中访问LCD文件,读取或写入文件内容。

void init_lcd()
{
		//1. 打开LCD设备文件
		int fd = open("/dev/ubuntu_lcd", O_RDWR);
		if(fd == -1)
		{
			perror("open");
			return ;
		}
		//2. 映射
		plcd = mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED,	 fd, 0);
}

bmp图片的显示:利用draw_bmp函数,读取bmp文件信息(文件宽、高、色深)得出像素数组的大小,通过显示像素数组,完成在bmp图片在LCD上的显示。

//显示 像素数组
void draw_image(int x0,int y0,int width, int height , const unsigned char image[])
{
	int i,j,k=0;
	int color;
	for(i=height;i>=0;i--)
	for(j=0;j<width;j++)
	{
		color = image[k]|(image[k+1]<<8)|(image[k+2]<<16);
		k = k+3;
		draw_point(j+x0, i+y0, color);
	}
}
//读bmp文件,获取 w,h,depth
int draw_bmp(int x0,int y0, const char *bmpfile)
{
	unsigned char buf[4];
	
	int fd = open(bmpfile,O_RDONLY);
	if(fd < 0)
	{
		perror("open");
		return -1;
	}
	read(fd, buf, 2);
	if(buf[0]=='B' && buf[1]=='M')
	{
		printf("It's bmpfile\n");
	}
	else 
		return -1;
	//1. 读出文件的 宽,高,色深:才能知道像素数组的大小
	lseek(fd, 0x12,SEEK_SET);
	read(fd, buf, 4);
	int width = (buf[3]<<24)|(buf[2]<<16)|(buf[1]<<8)|buf[0];
	//2. 读出文件的 高
	lseek(fd, 0x16,SEEK_SET);
	read(fd, buf, 4);
	int height = (buf[3]<<24)|(buf[2]<<16)|(buf[1]<<8)|buf[0];
	//3. 读出文件的 色深
	lseek(fd, 0x1c,SEEK_SET);
	read(fd, buf, 2);
	int depth = (buf[1]<<8)|buf[0];  //24
	// 4:像素数组大小: width * height *depth/8
	// 读 像素数组
	unsigned char *image = (unsigned char*)malloc(width * height *depth/8);
	lseek(fd, 54, SEEK_SET); // bmp 信息head占了 54个字节
	read(fd, image, width * height *depth/8);
	draw_image(x0,y0, width, height, image);
	
}

 jpg图片的显示:调用draw_jpg函数,初始化解压对象,打开要解压的jpg文件并调用jepg_start_decompress函数启动解压过程,再读取文件,获取jpg图像数据(图像的宽、高、颜色分量),并使用循环逐行读取、解压图像数据,并在内存中显示每个像素点的颜色值。从而完成jpg图片在LCD上的显示。

int draw_jpg(int x0,int y0, const char *jpgfile)
{
	//1. 声明一个 解压对象
	struct  jpeg_decompress_struct dinfo;   
	//声明一个出错信息的对象
	struct jpeg_error_mgr jerr; 
	//2. 初始化这个解压对象
	jpeg_create_decompress(&dinfo); 
	//初始化这个出错对象
	dinfo.err = jpeg_std_error(&jerr); 
	//3. 打开要解压的文件
	FILE * infile = fopen(jpgfile,"r");
	if(infile == NULL)
	{
		perror("fopen");
		return -1;
	}
	//4. 指定要解压的图像文件
	jpeg_stdio_src(&dinfo, infile); 
	jpeg_read_header(&dinfo, TRUE);
	//5. 启动解压 
	jpeg_start_decompress(&dinfo); 
	int width,height,components;
	width = dinfo.output_width;
	height = dinfo.output_height;
	components = dinfo.output_components;
	printf("width:%d, height:%d, components:%d\n",dinfo.output_width, dinfo.output_height,dinfo.output_components);
	//6. 分配内存,每次读一行 显示
	unsigned char * image = (unsigned char *)malloc(width*components);

	int color,k=0,x;
	
	 while(dinfo.output_scanline<dinfo.output_height)
	 {
		jpeg_read_scanlines(&dinfo, &image, 1 ); // 800x3
		
		for(x=0,k=0;x<width;x++)
		{
		color = image[k]|(image[k+1]<<8)|(image[k+2]<<16);
		k = k+3;
		draw_point(x0+x, y0+dinfo.output_scanline, color);
		}
 	 }
	
}

 接着是触摸屏模块,模块功能为:从触摸屏设备/dev/ubuntu_event中读取触摸事件(鼠标点击)的坐标信息,并返回一个包含x和y坐标的结构体pos该模块用于实现鼠标点击切换图片。

​
​struct pos getPos()
{
	struct pos p;
	p.x = -1;
	p.y = -1;
	int fd = open("/dev/ubuntu_event", O_RDONLY);
	if(fd < 0)
	{
		perror("open");
		return p;
	}
	// 读触摸屏驱动文件:可以读到一个 事件的结构体  
	struct input_event ev;
	while(1)
	{
		read(fd, &ev, sizeof(ev));
		//printf("type=%d, code=%d, value=%d\n", ev.type, ev.code, ev.value);
		if(ev.type==3 && ev.code==0)
		{
			//printf("x = %d ", ev.value);
			p.x = ev.value;
		}
		else if(ev.type==3 && ev.code==1)
		{
			//printf("y = %d\n", ev.value);
			p.y = ev.value;
			close(fd);
			return p;
		}
				
	}
}


​

​

最后,重点介绍项目功能实现的核心方法:

1.创建图片节点:包含用于保存bmp或jpg文件名的filename数组、用于保存文件修改时间的time结构体,以及分别指向上一节点和下一节点的指针prevnext

typedef struct node{
	struct timespec time;
	char filename[50];
	struct node *next;//指针域:指向下一个节点
	struct node *prev;//指针域:指向上一个节点
}NODE;

2.读文件目录:搜素目录,读目录项,将bmp和jpg文件名和修改时间存入节点。完成此操作后,节点即代表一张图片,再将图片插入链表。

NODE * search_dir(NODE *head, char *dir)
{
	//搜索目录,如果找到有 bmp ,jpg 插入链表中
	DIR *dp = opendir(dir);
	struct dirent *p;  // 目录项
	struct stat statbuf;
	NODE s;

	
	while(p = readdir(dp)) //当 p == NULL 就读完了
	{
		if(isBmpJpg(p->d_name)==BMP || isBmpJpg(p->d_name)==JPG)
		{
			
			sprintf(s.filename,"%s/%s",dir,p->d_name);
			stat(p->d_name, &statbuf); //获取了 文件的 属性
			s.time = statbuf.st_mtim; //文件的修改时间
			
			head = insert(head, s);//插入链表
		}
	}
	closedir(dp); //关闭目录
	return head;
}

3.图片节点插入链表:这里我用的是排序法插入,按文件修改时间time.tv_sec的先后顺序插入图片节点,并确定好每个节点的prevnext指针的指向,从而创建了一个双向循环链表

NODE *insert(NODE *head, NODE s)

{
	NODE *new1,*p;
	p = head;
	new1 = (NODE *)malloc(sizeof(NODE));

	strcpy(new1->filename , s.filename);
	new1->time = s.time;
	
	if(head==NULL)//如果是空链表
	{
		head = new1;
		new1->next = new1;
		new1->prev = new1;
		p = head;
		return head;
	}
	else if (p->next == p)//当链表只有一个节点时,新节点插在其后
	{
		p->next = new1;
		new1->prev = p;
		p->prev = new1;
		new1->next = p;
		if( new1->time.tv_sec <= p->time.tv_sec)//new1的数据比p还小,则交换二者数据
		{
			int time = p->time.tv_sec;
			p->time.tv_sec = new1->time.tv_sec;
			new1->time.tv_sec = time; 
		}
		return head;
	}
	else if (p->next != p)//当链表有多个节点时
	{
		while(p->next != head)//p不是尾节点
		{
			if(p->time.tv_sec <= new1->time.tv_sec&&p->next->time.tv_sec > new1->time.tv_sec)//找一个数据比new1小,且其下一节点数据比new1大的节点,将new1插在中间。
			{
				new1->next = p->next;
				p->next->prev = new1;
				p->next = new1;
				new1->prev = p;
				return head;
			}
			p = p->next;
		}
	}
	p->next = new1;
	new1->prev = p;
	new1->next = head;
	head->prev = new1;
	return head;
}

4.项目功能的实现:在主循环中,通过getPos函数返回的坐标信息判断鼠标点击的是哪个区域,再利用双向循环链表实现切换上一张、下一张图片。此外,定义了一个线程函数musicplayer,并在main函数中利用pthread_create函数创建一个线程,用于在相册放映的同时,在线程中播放音乐。部分核心代码如下:

void * musicplayer(void *arg)
{
	int oldstate;
	char song[20];
	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE , &oldstate);
	pthread_detach(pthread_self());
	sprintf(song, "madplay mp3/3.mp3");
	system(song);
}


void photo_play(NODE *head)
{
	int color[7]={0xff0000,0xff00,0xff,0xffff00,0xff00ff,0xffff,0xffffff};
	NODE *p = head;
	draw_bmp(0, 0, "imagefiles/1.bmp");
	struct pos po;
	while(1)
	{
		po = getPos();
		if(po.x <300 && po.y <300 ) //上一张
		{
			brush_lcd(color[6]);
			p = p->prev;
			if(isBmpJpg(p->filename)==BMP)
			{
				draw_bmp(0, 0, p->filename);
			}
			else if(isBmpJpg(p->filename)==JPG)
				draw_jpg(0, 0, p->filename);
		}
		else if(po.x >500 && po.y < 200)//下一张
		{
			brush_lcd(color[6]);
			p = p->next;
			if(isBmpJpg(p->filename)==BMP)
			{
				draw_bmp(0, 0, p->filename);
			}
			else if(isBmpJpg(p->filename)==JPG)
				draw_jpg(0, 0, p->filename);
		}

		else if(po.x >500 && po.y > 300)//结束播放
		{
			break;
		}
	}

}

功能演示:

演示视频如下:

 

  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值